Search in sources :

Example 1 with FileAccessContext

use of org.finos.legend.sdlc.server.project.ProjectFileAccessProvider.FileAccessContext in project legend-sdlc by finos.

the class ProjectStructure method getProjectStructureForProjectDependency.

protected static ProjectStructure getProjectStructureForProjectDependency(ProjectDependency projectDependency, BiFunction<String, VersionId, FileAccessContext> versionFileAccessContextProvider) {
    FileAccessContext versionFileAccessContext = versionFileAccessContextProvider.apply(projectDependency.getProjectId(), projectDependency.getVersionId());
    ProjectConfiguration versionConfig = ProjectStructure.getProjectConfiguration(versionFileAccessContext);
    if (versionConfig == null) {
        throw new LegendSDLCServerException("Invalid version of project " + projectDependency.getProjectId() + ": " + projectDependency.getVersionId().toVersionIdString());
    }
    return ProjectStructure.getProjectStructure(versionConfig);
}
Also used : FileAccessContext(org.finos.legend.sdlc.server.project.ProjectFileAccessProvider.FileAccessContext) LegendSDLCServerException(org.finos.legend.sdlc.server.error.LegendSDLCServerException) ProjectConfiguration(org.finos.legend.sdlc.domain.model.project.configuration.ProjectConfiguration)

Example 2 with FileAccessContext

use of org.finos.legend.sdlc.server.project.ProjectFileAccessProvider.FileAccessContext 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)

Aggregations

ProjectConfiguration (org.finos.legend.sdlc.domain.model.project.configuration.ProjectConfiguration)2 LegendSDLCServerException (org.finos.legend.sdlc.server.error.LegendSDLCServerException)2 FileAccessContext (org.finos.legend.sdlc.server.project.ProjectFileAccessProvider.FileAccessContext)2 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