Search in sources :

Example 1 with ProjectStructureExtension

use of org.finos.legend.sdlc.server.project.extension.ProjectStructureExtension in project legend-sdlc by finos.

the class ProjectStructure method updateProjectConfiguration.

private static Revision updateProjectConfiguration(ProjectConfigurationUpdateBuilder updateBuilder, boolean requireRevisionId) {
    ProjectFileAccessProvider projectFileAccessProvider = CachingProjectFileAccessProvider.wrap(updateBuilder.getProjectFileAccessProvider());
    ProjectFileAccessProvider.WorkspaceAccessType workspaceAccessType = updateBuilder.getWorkspaceAccessType();
    WorkspaceType workspaceType = updateBuilder.getWorkspaceType();
    if (updateBuilder.hasGroupId() && !isValidGroupId(updateBuilder.getGroupId())) {
        throw new LegendSDLCServerException("Invalid groupId: " + updateBuilder.getGroupId(), Status.BAD_REQUEST);
    }
    if (updateBuilder.hasArtifactId() && !isValidArtifactId(updateBuilder.getArtifactId())) {
        throw new LegendSDLCServerException("Invalid artifactId: " + updateBuilder.getArtifactId(), Status.BAD_REQUEST);
    }
    ProjectType projectType = updateBuilder.getProjectType();
    String projectId = updateBuilder.getProjectId();
    String workspaceId = updateBuilder.getWorkspaceId();
    String revisionId = updateBuilder.getRevisionId();
    // if revisionId not specified, get the current revision
    if (revisionId == null) {
        Revision currentRevision = projectFileAccessProvider.getRevisionAccessContext(projectId, workspaceId, workspaceType, workspaceAccessType).getCurrentRevision();
        if (currentRevision != null) {
            revisionId = currentRevision.getId();
        } else if (requireRevisionId) {
            StringBuilder builder = new StringBuilder("Could not find current revision for ");
            if (workspaceId != null) {
                builder.append(workspaceType.getLabel() + " " + workspaceAccessType.getLabel()).append(" ").append(workspaceId).append("in ");
            }
            builder.append("project ").append(projectId).append(": it may be corrupt");
            throw new LegendSDLCServerException(builder.toString());
        }
    }
    // find out what we need to update for project structure
    FileAccessContext fileAccessContext = CachingFileAccessContext.wrap(projectFileAccessProvider.getFileAccessContext(projectId, workspaceId, workspaceType, workspaceAccessType, revisionId));
    ProjectFile configFile = getProjectConfigurationFile(fileAccessContext);
    ProjectConfiguration currentConfig = (configFile == null) ? getDefaultProjectConfiguration(projectId, projectType) : readProjectConfiguration(configFile);
    if (projectType != currentConfig.getProjectType()) {
        throw new LegendSDLCServerException("Project type mismatch for project " + projectId + ": got " + projectType + ", found " + currentConfig.getProjectType(), Status.BAD_REQUEST);
    }
    boolean updateProjectStructureVersion = updateBuilder.hasProjectStructureVersion() && (updateBuilder.getProjectStructureVersion() != currentConfig.getProjectStructureVersion().getVersion());
    boolean updateProjectStructureExtensionVersion = updateBuilder.hasProjectStructureExtensionVersion() && !updateBuilder.getProjectStructureExtensionVersion().equals(currentConfig.getProjectStructureVersion().getExtensionVersion());
    boolean updateGroupId = updateBuilder.hasGroupId() && !updateBuilder.getGroupId().equals(currentConfig.getGroupId());
    boolean updateArtifactId = updateBuilder.hasArtifactId() && !updateBuilder.getArtifactId().equals(currentConfig.getArtifactId());
    // find out which dependencies we need to update
    boolean updateProjectDependencies = false;
    Set<ProjectDependency> projectDependencies = Sets.mutable.withAll(currentConfig.getProjectDependencies());
    Set<ProjectDependency> toUpdateProjectDependencies = projectDependencies.stream().filter(dep -> ProjectDependency.isLegacyProjectDependency(dep) && !(updateBuilder.hasProjectDependenciesToRemove() && updateBuilder.getProjectDependenciesToRemove().contains(dep))).collect(Collectors.toSet());
    if (toUpdateProjectDependencies.size() > 0) {
        updateProjectDependencies = true;
        updateOrAddDependencies(toUpdateProjectDependencies, projectFileAccessProvider, projectDependencies, true);
    }
    if (updateBuilder.hasProjectDependenciesToRemove()) {
        updateProjectDependencies |= projectDependencies.removeAll(updateBuilder.getProjectDependenciesToRemove());
    }
    // add new dependencies to the list of dependencies while also validate that there are no unknown/non-prod dependencies
    if (updateBuilder.hasProjectDependenciesToAdd()) {
        updateProjectDependencies = true;
        updateOrAddDependencies(updateBuilder.getProjectDependenciesToAdd(), projectFileAccessProvider, projectDependencies, false);
    }
    // validate if there are any conflicts between the dependencies
    if (updateProjectDependencies) {
        validateDependencyConflicts(projectDependencies, ProjectDependency::getProjectId, (id, deps) -> {
            if ((deps.size() <= 1) || deps.stream().allMatch(dep -> getProjectStructure(projectFileAccessProvider.getFileAccessContext(dep.getProjectId(), dep.getVersionId())).isSupportedArtifactType(ArtifactType.versioned_entities))) {
                return null;
            }
            List<ProjectDependency> supported = Lists.mutable.empty();
            List<ProjectDependency> unsupported = Lists.mutable.empty();
            deps.forEach(dep -> (getProjectStructure(projectFileAccessProvider.getFileAccessContext(dep.getProjectId(), dep.getVersionId())).isSupportedArtifactType(ArtifactType.versioned_entities) ? supported : unsupported).add(dep));
            StringBuilder message = new StringBuilder();
            unsupported.forEach(dep -> dep.appendVersionIdString((message.length() == 0) ? message : message.append(", ")));
            message.append((unsupported.size() == 1) ? " does" : " do").append(" not support multi-version dependency");
            if (!supported.isEmpty()) {
                int startLen = message.length();
                supported.forEach(dep -> dep.appendVersionIdString(message.append((message.length() == startLen) ? "; " : ", ")));
                message.append((supported.size() == 1) ? " does" : " do");
            }
            return message.toString();
        }, "projects");
    }
    // check if we need to update any metamodel dependencies
    boolean updateMetamodelDependencies = false;
    Set<MetamodelDependency> metamodelDependencies = Sets.mutable.withAll(currentConfig.getMetamodelDependencies());
    if (updateBuilder.hasMetamodelDependenciesToRemove()) {
        updateMetamodelDependencies |= metamodelDependencies.removeAll(updateBuilder.getMetamodelDependenciesToRemove());
    }
    // add new metamodel dependencies to the list of metamodel dependencies while also validate that there are no unknown metamodel dependencies
    if (updateBuilder.hasMetamodelDependenciesToAdd()) {
        List<MetamodelDependency> unknownDependencies = Lists.mutable.empty();
        for (MetamodelDependency metamodelDependency : updateBuilder.getMetamodelDependenciesToAdd()) {
            if (metamodelDependencies.add(metamodelDependency)) {
                updateMetamodelDependencies = true;
                if (!isKnownMetamodel(metamodelDependency)) {
                    unknownDependencies.add(metamodelDependency);
                }
            }
        }
        if (!unknownDependencies.isEmpty()) {
            StringBuilder builder = new StringBuilder("There were issues with one or more added metamodel dependencies");
            builder.append("; unknown ").append((unknownDependencies.size() == 1) ? "dependency" : "dependencies").append(": ");
            unknownDependencies.sort(Comparator.naturalOrder());
            unknownDependencies.forEach(d -> d.appendDependencyString((d == unknownDependencies.get(0)) ? builder : builder.append(", ")));
            throw new LegendSDLCServerException(builder.toString(), Status.BAD_REQUEST);
        }
    }
    // validate that there are no conflicts between the metamodel dependencies
    if (updateMetamodelDependencies) {
        validateDependencyConflicts(metamodelDependencies, MetamodelDependency::getMetamodel, (id, deps) -> (deps.size() > 1) ? deps.stream().collect(StringBuilder::new, (builder, dep) -> dep.appendVersionIdString(builder.append((builder.length() == 0) ? "multiple versions not allowed: " : ", ")), StringBuilder::append).toString() : null, "metamodels");
    }
    boolean updateGeneration = false;
    Map<String, ArtifactGeneration> generationsByName = currentConfig.getArtifactGenerations().stream().collect(Collectors.toMap(ArtifactGeneration::getName, Function.identity()));
    if (updateBuilder.hasArtifactGenerationsToRemove()) {
        updateGeneration = generationsByName.keySet().stream().anyMatch(updateBuilder.getArtifactGenerationToRemove()::contains);
        updateBuilder.getArtifactGenerationToRemove().forEach(generationsByName::remove);
    }
    if (updateBuilder.hasArtifactGenerationsToAdd()) {
        validateArtifactGenerations(generationsByName, updateBuilder.getArtifactGenerationToAdd());
        updateGeneration = updateBuilder.getArtifactGenerationToAdd().stream().noneMatch(generationsByName.values()::contains);
        updateBuilder.getArtifactGenerationToAdd().forEach(art -> generationsByName.put(art.getName(), art));
    }
    // abort if there is no change to make
    if (!updateProjectStructureVersion && !updateProjectStructureExtensionVersion && !updateGroupId && !updateArtifactId && !updateProjectDependencies && !updateMetamodelDependencies && !updateGeneration) {
        return null;
    }
    // Collect operations
    List<ProjectFileOperation> operations = Lists.mutable.empty();
    // New configuration
    SimpleProjectConfiguration newConfig = new SimpleProjectConfiguration(currentConfig);
    if (updateProjectStructureVersion) {
        if (updateBuilder.hasProjectStructureExtensionVersion()) {
            newConfig.setProjectStructureVersion(updateBuilder.getProjectStructureVersion(), updateBuilder.getProjectStructureExtensionVersion());
        } else if (updateBuilder.hasProjectStructureExtensionProvider()) {
            newConfig.setProjectStructureVersion(updateBuilder.getProjectStructureVersion(), updateBuilder.getProjectStructureExtensionProvider().getLatestVersionForProjectStructureVersion(updateBuilder.getProjectStructureVersion()));
        } else {
            newConfig.setProjectStructureVersion(updateBuilder.getProjectStructureVersion(), null);
        }
    } else if (updateProjectStructureExtensionVersion) {
        newConfig.setProjectStructureVersion(currentConfig.getProjectStructureVersion().getVersion(), updateBuilder.getProjectStructureExtensionVersion());
    }
    if (updateGroupId) {
        newConfig.setGroupId(updateBuilder.getGroupId());
    }
    if (updateArtifactId) {
        newConfig.setArtifactId(updateBuilder.getArtifactId());
    }
    if (updateProjectDependencies) {
        List<ProjectDependency> projectDependencyList = Lists.mutable.withAll(projectDependencies);
        projectDependencyList.sort(Comparator.naturalOrder());
        newConfig.setProjectDependencies(projectDependencyList);
    }
    if (updateMetamodelDependencies) {
        List<MetamodelDependency> metamodelDependencyList = Lists.mutable.withAll(metamodelDependencies);
        metamodelDependencyList.sort(Comparator.naturalOrder());
        newConfig.setMetamodelDependencies(metamodelDependencyList);
    }
    if (updateGeneration) {
        List<ArtifactGeneration> artifactGenerationsList = Lists.mutable.withAll(generationsByName.values());
        artifactGenerationsList.sort(Comparator.comparing(ArtifactGeneration::getName));
        newConfig.setArtifactGeneration(artifactGenerationsList);
    }
    // prevent downgrading project
    if (newConfig.getProjectStructureVersion().compareTo(currentConfig.getProjectStructureVersion()) < 0) {
        throw new LegendSDLCServerException("Cannot change project " + projectId + " from project structure version " + currentConfig.getProjectStructureVersion().toVersionString() + " to version " + newConfig.getProjectStructureVersion().toVersionString(), Status.BAD_REQUEST);
    }
    String serializedNewConfig = serializeProjectConfiguration(newConfig);
    operations.add((configFile == null) ? ProjectFileOperation.addFile(PROJECT_CONFIG_PATH, serializedNewConfig) : ProjectFileOperation.modifyFile(PROJECT_CONFIG_PATH, serializedNewConfig));
    ProjectStructure currentProjectStructure = getProjectStructure(currentConfig, updateBuilder.getProjectStructurePlatformExtensions());
    ProjectStructure newProjectStructure = getProjectStructure(newConfig, updateBuilder.getProjectStructurePlatformExtensions());
    // Move or re-serialize entities if necessary
    List<EntitySourceDirectory> currentEntityDirectories = currentProjectStructure.getEntitySourceDirectories();
    List<EntitySourceDirectory> newEntityDirectories = newProjectStructure.getEntitySourceDirectories();
    if (!currentEntityDirectories.equals(newEntityDirectories)) {
        currentEntityDirectories.forEach(currentSourceDirectory -> fileAccessContext.getFilesInDirectory(currentSourceDirectory.getDirectory()).forEach(file -> {
            String currentPath = file.getPath();
            if (currentSourceDirectory.isPossiblyEntityFilePath(currentPath)) {
                byte[] currentBytes = file.getContentAsBytes();
                Entity entity;
                try {
                    entity = currentSourceDirectory.deserialize(currentBytes);
                } catch (Exception e) {
                    StringBuilder builder = new StringBuilder("Error deserializing entity from file \"").append(currentPath).append('"');
                    StringTools.appendThrowableMessageIfPresent(builder, e);
                    throw new LegendSDLCServerException(builder.toString(), e);
                }
                EntitySourceDirectory newSourceDirectory = Iterate.detectWith(newEntityDirectories, EntitySourceDirectory::canSerialize, entity);
                if (newSourceDirectory == null) {
                    throw new LegendSDLCServerException("Could not find a new source directory for entity " + entity.getPath() + ", currently in " + currentPath);
                }
                if (!currentSourceDirectory.equals(newSourceDirectory)) {
                    String newPath = newSourceDirectory.entityPathToFilePath(entity.getPath());
                    byte[] newBytes = newSourceDirectory.serializeToBytes(entity);
                    if (!newPath.equals(currentPath)) {
                        operations.add(ProjectFileOperation.moveFile(currentPath, newPath, newBytes));
                    } else if (!Arrays.equals(currentBytes, newBytes)) {
                        operations.add(ProjectFileOperation.modifyFile(currentPath, newBytes));
                    }
                }
            }
        }));
    }
    // Collect any further update operations
    newProjectStructure.collectUpdateProjectConfigurationOperations(currentProjectStructure, fileAccessContext, projectFileAccessProvider::getFileAccessContext, operations::add);
    // Collect update operations from any project structure extension
    if (updateBuilder.hasProjectStructureExtensionProvider() && (newConfig.getProjectStructureVersion().getExtensionVersion() != null)) {
        ProjectStructureExtension projectStructureExtension = updateBuilder.getProjectStructureExtensionProvider().getProjectStructureExtension(newConfig.getProjectStructureVersion().getVersion(), newConfig.getProjectStructureVersion().getExtensionVersion());
        projectStructureExtension.collectUpdateProjectConfigurationOperations(currentConfig, newConfig, fileAccessContext, operations::add);
    }
    // Submit changes
    return projectFileAccessProvider.getFileModificationContext(projectId, workspaceId, workspaceType, workspaceAccessType, revisionId).submit(updateBuilder.getMessage(), operations);
}
Also used : ProjectType(org.finos.legend.sdlc.domain.model.project.ProjectType) Arrays(java.util.Arrays) ArtifactType(org.finos.legend.sdlc.domain.model.project.configuration.ArtifactType) SortedSet(java.util.SortedSet) BiFunction(java.util.function.BiFunction) MutableList(org.eclipse.collections.api.list.MutableList) StringTools(org.finos.legend.sdlc.server.tools.StringTools) LegendSDLCServerException(org.finos.legend.sdlc.server.error.LegendSDLCServerException) Map(java.util.Map) EnumSet(java.util.EnumSet) Dependency(org.finos.legend.sdlc.domain.model.project.configuration.Dependency) ArtifactTypeGenerationConfiguration(org.finos.legend.sdlc.domain.model.project.configuration.ArtifactTypeGenerationConfiguration) VersionId(org.finos.legend.sdlc.domain.model.version.VersionId) MetamodelDependency(org.finos.legend.sdlc.domain.model.project.configuration.MetamodelDependency) Collection(java.util.Collection) Set(java.util.Set) StreamWriteFeature(com.fasterxml.jackson.core.StreamWriteFeature) Reader(java.io.Reader) Iterate(org.eclipse.collections.impl.utility.Iterate) StreamReadFeature(com.fasterxml.jackson.core.StreamReadFeature) JsonMapper(com.fasterxml.jackson.databind.json.JsonMapper) Collectors(java.util.stream.Collectors) ListAdapter(org.eclipse.collections.impl.list.mutable.ListAdapter) SourceVersion(javax.lang.model.SourceVersion) List(java.util.List) Stream(java.util.stream.Stream) Pattern(java.util.regex.Pattern) ArtifactGeneration(org.finos.legend.sdlc.domain.model.project.configuration.ArtifactGeneration) SortedMap(java.util.SortedMap) ProjectStructureExtension(org.finos.legend.sdlc.server.project.extension.ProjectStructureExtension) Lists(org.eclipse.collections.api.factory.Lists) Function(java.util.function.Function) TreeSet(java.util.TreeSet) FileAccessContext(org.finos.legend.sdlc.server.project.ProjectFileAccessProvider.FileAccessContext) MapperFeature(com.fasterxml.jackson.databind.MapperFeature) EntitySerializer(org.finos.legend.sdlc.serialization.EntitySerializer) Entity(org.finos.legend.sdlc.domain.model.entity.Entity) Status(javax.ws.rs.core.Response.Status) Sets(org.eclipse.collections.api.factory.Sets) IOException(java.io.IOException) EntitySerializers(org.finos.legend.sdlc.serialization.EntitySerializers) WorkspaceType(org.finos.legend.sdlc.domain.model.project.workspace.WorkspaceType) ProjectStructureVersion(org.finos.legend.sdlc.domain.model.project.configuration.ProjectStructureVersion) Consumer(java.util.function.Consumer) ProjectFile(org.finos.legend.sdlc.server.project.ProjectFileAccessProvider.ProjectFile) ProjectDependency(org.finos.legend.sdlc.domain.model.project.configuration.ProjectDependency) TreeMap(java.util.TreeMap) ProjectConfiguration(org.finos.legend.sdlc.domain.model.project.configuration.ProjectConfiguration) SerializationFeature(com.fasterxml.jackson.databind.SerializationFeature) Revision(org.finos.legend.sdlc.domain.model.revision.Revision) Comparator(java.util.Comparator) Collections(java.util.Collections) InputStream(java.io.InputStream) Entity(org.finos.legend.sdlc.domain.model.entity.Entity) ArtifactGeneration(org.finos.legend.sdlc.domain.model.project.configuration.ArtifactGeneration) ProjectStructureExtension(org.finos.legend.sdlc.server.project.extension.ProjectStructureExtension) ProjectFile(org.finos.legend.sdlc.server.project.ProjectFileAccessProvider.ProjectFile) LegendSDLCServerException(org.finos.legend.sdlc.server.error.LegendSDLCServerException) FileAccessContext(org.finos.legend.sdlc.server.project.ProjectFileAccessProvider.FileAccessContext) ProjectDependency(org.finos.legend.sdlc.domain.model.project.configuration.ProjectDependency) MetamodelDependency(org.finos.legend.sdlc.domain.model.project.configuration.MetamodelDependency) LegendSDLCServerException(org.finos.legend.sdlc.server.error.LegendSDLCServerException) IOException(java.io.IOException) WorkspaceType(org.finos.legend.sdlc.domain.model.project.workspace.WorkspaceType) Revision(org.finos.legend.sdlc.domain.model.revision.Revision) ProjectType(org.finos.legend.sdlc.domain.model.project.ProjectType) ProjectConfiguration(org.finos.legend.sdlc.domain.model.project.configuration.ProjectConfiguration)

Example 2 with ProjectStructureExtension

use of org.finos.legend.sdlc.server.project.extension.ProjectStructureExtension in project legend-sdlc by finos.

the class AbstractBaseModule method resolveProjectStructureExtensionProvider.

private ProjectStructureExtensionProvider resolveProjectStructureExtensionProvider() {
    ProjectStructureConfiguration projectStructureConfiguration = getConfiguration().getProjectStructureConfiguration();
    if (projectStructureConfiguration != null) {
        ProjectStructureExtensionProvider configuredProvider = projectStructureConfiguration.getProjectStructureExtensionProvider();
        if (configuredProvider != null) {
            return configuredProvider;
        }
        List<ProjectStructureExtension> extensions = projectStructureConfiguration.getProjectStructureExtensions();
        if ((extensions != null) && !extensions.isEmpty()) {
            return DefaultProjectStructureExtensionProvider.fromExtensions(extensions);
        }
    }
    return new VoidProjectStructureExtensionProvider();
}
Also used : DefaultProjectStructureExtensionProvider(org.finos.legend.sdlc.server.project.extension.DefaultProjectStructureExtensionProvider) ProjectStructureExtensionProvider(org.finos.legend.sdlc.server.project.extension.ProjectStructureExtensionProvider) VoidProjectStructureExtensionProvider(org.finos.legend.sdlc.server.project.extension.VoidProjectStructureExtensionProvider) VoidProjectStructureExtensionProvider(org.finos.legend.sdlc.server.project.extension.VoidProjectStructureExtensionProvider) ProjectStructureExtension(org.finos.legend.sdlc.server.project.extension.ProjectStructureExtension) ProjectStructureConfiguration(org.finos.legend.sdlc.server.project.config.ProjectStructureConfiguration)

Aggregations

StreamReadFeature (com.fasterxml.jackson.core.StreamReadFeature)1 StreamWriteFeature (com.fasterxml.jackson.core.StreamWriteFeature)1 MapperFeature (com.fasterxml.jackson.databind.MapperFeature)1 SerializationFeature (com.fasterxml.jackson.databind.SerializationFeature)1 JsonMapper (com.fasterxml.jackson.databind.json.JsonMapper)1 IOException (java.io.IOException)1 InputStream (java.io.InputStream)1 Reader (java.io.Reader)1 Arrays (java.util.Arrays)1 Collection (java.util.Collection)1 Collections (java.util.Collections)1 Comparator (java.util.Comparator)1 EnumSet (java.util.EnumSet)1 List (java.util.List)1 Map (java.util.Map)1 Set (java.util.Set)1 SortedMap (java.util.SortedMap)1 SortedSet (java.util.SortedSet)1 TreeMap (java.util.TreeMap)1 TreeSet (java.util.TreeSet)1