Search in sources :

Example 56 with LegendSDLCServerException

use of org.finos.legend.sdlc.server.error.LegendSDLCServerException in project legend-sdlc by finos.

the class GitLabConflictResolutionApi method acceptConflictResolution.

/**
 * This method will apply conflict resolution changes and mark a conflict resolution as done.
 * Assume we have workspace branch `w1`, this method will:
 * 1. Perform entity changes to resolve conflicts
 * 2. Remove backup branch for `w1` if exists
 * 3. Create backup branch for `w1`
 * 4. Remove workspace branch `w1`
 * 5. Create new workspace branch `w1` from conflict resolution branch `w1`
 * 6. Remove conflict resolution branch `w1`
 * 7. Remove backup branch `w1`
 */
@Override
public void acceptConflictResolution(String projectId, String workspaceId, WorkspaceType workspaceType, PerformChangesCommand command) {
    LegendSDLCServerException.validateNonNull(projectId, "projectId may not be null");
    LegendSDLCServerException.validateNonNull(workspaceId, "workspaceId may not be null");
    GitLabProjectId gitLabProjectId = parseProjectId(projectId);
    RepositoryApi repositoryApi = getGitLabApi(gitLabProjectId.getGitLabMode()).getRepositoryApi();
    ProjectFileAccessProvider.WorkspaceAccessType conflictResolutionWorkspaceType = ProjectFileAccessProvider.WorkspaceAccessType.CONFLICT_RESOLUTION;
    // Verify conflict resolution is happening
    try {
        withRetries(() -> repositoryApi.getBranch(gitLabProjectId.getGitLabId(), getWorkspaceBranchName(workspaceId, workspaceType, conflictResolutionWorkspaceType)));
    } catch (Exception e) {
        if (e instanceof GitLabApiException && GitLabApiTools.isNotFoundGitLabApiException((GitLabApiException) e)) {
            LOGGER.error("Conflict resolution is not happening on workspace {} in project {}, so accepting conflict resolution is not actionable", workspaceId, projectId);
        }
        throw buildException(e, () -> "User " + getCurrentUser() + " is not allowed to get " + workspaceType.getLabel() + " " + conflictResolutionWorkspaceType.getLabel() + " " + workspaceId + " in project " + projectId, () -> "Unknown " + workspaceType.getLabel() + " " + conflictResolutionWorkspaceType.getLabel() + " (" + workspaceId + ") or project (" + projectId + "). " + "This implies that conflict resolution is not taking place, hence accepting conflict resolution is not actionable", () -> "Error getting " + workspaceType.getLabel() + " " + conflictResolutionWorkspaceType.getLabel() + " " + workspaceId + " in project " + projectId);
    }
    // Perform entity changes to resolve conflicts
    try {
        this.entityApi.getWorkspaceWithConflictResolutionEntityModificationContext(projectId, workspaceId).performChanges(command.getEntityChanges(), command.getRevisionId(), command.getMessage());
    } catch (Exception e) {
        throw buildException(e, () -> "User " + getCurrentUser() + " is not allowed to apply conflict resolution changes in " + workspaceType.getLabel() + " " + conflictResolutionWorkspaceType.getLabel() + " " + workspaceId + " in project " + projectId, () -> "Unknown " + workspaceType.getLabel() + " " + conflictResolutionWorkspaceType.getLabel() + " (" + workspaceId + ") or project (" + projectId + "). " + "This implies that conflict resolution is not taking place, hence accept is not actionable", () -> "Error applying conflict resolution changes in " + workspaceType.getLabel() + " " + conflictResolutionWorkspaceType.getLabel() + " " + workspaceId + " in project " + projectId);
    }
    // Delete backup branch if already exists
    boolean backupWorkspaceDeleted;
    ProjectFileAccessProvider.WorkspaceAccessType backupWorkspaceType = ProjectFileAccessProvider.WorkspaceAccessType.BACKUP;
    try {
        backupWorkspaceDeleted = GitLabApiTools.deleteBranchAndVerify(repositoryApi, gitLabProjectId.getGitLabId(), getWorkspaceBranchName(workspaceId, workspaceType, backupWorkspaceType), 20, 1_000);
    } catch (Exception e) {
        // If we fail to delete the residual backup workspace, we cannot proceed anyway, so we will throw the error here
        throw buildException(e, () -> "User " + getCurrentUser() + " is not allowed to delete " + workspaceType.getLabel() + " " + backupWorkspaceType.getLabel() + " " + workspaceId + " in project " + projectId, () -> "Unknown " + workspaceType.getLabel() + " " + backupWorkspaceType.getLabel() + " (" + workspaceId + ") or project (" + projectId + ")", () -> "Error deleting " + workspaceType.getLabel() + " " + backupWorkspaceType.getLabel() + " " + workspaceId + " in project " + projectId);
    }
    if (!backupWorkspaceDeleted) {
        throw new LegendSDLCServerException("Failed to delete " + workspaceType.getLabel() + " " + backupWorkspaceType.getLabel() + " " + workspaceId + " in project " + projectId);
    }
    // Create backup branch from original branch
    Branch newBackupBranch;
    ProjectFileAccessProvider.WorkspaceAccessType workspaceAccessType = ProjectFileAccessProvider.WorkspaceAccessType.WORKSPACE;
    try {
        // Wait to allow nodes to synchronize that backup branch is already deleted
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        LOGGER.warn("Interrupted while waiting for nodes to synchronize that backup branch was deleted.", e);
        Thread.currentThread().interrupt();
    }
    try {
        newBackupBranch = GitLabApiTools.createBranchFromSourceBranchAndVerify(repositoryApi, gitLabProjectId.getGitLabId(), getWorkspaceBranchName(workspaceId, workspaceType, backupWorkspaceType), getWorkspaceBranchName(workspaceId, workspaceType, workspaceAccessType), 30, 1_000);
    } catch (Exception e) {
        throw buildException(e, () -> "User " + getCurrentUser() + " is not allowed to create " + workspaceType.getLabel() + " " + backupWorkspaceType.getLabel() + " " + workspaceId + " in project " + projectId, () -> "Unknown project: " + projectId, () -> "Error creating " + workspaceType.getLabel() + " " + backupWorkspaceType.getLabel() + " " + workspaceId + " in project " + projectId);
    }
    if (newBackupBranch == null) {
        throw new LegendSDLCServerException("Failed to create " + workspaceType.getLabel() + " " + backupWorkspaceType.getLabel() + " " + workspaceId + " from " + workspaceType.getLabel() + " " + workspaceAccessType.getLabel() + " " + workspaceId + " in project " + projectId);
    }
    // Delete original branch
    boolean originalBranchDeleted;
    try {
        originalBranchDeleted = GitLabApiTools.deleteBranchAndVerify(repositoryApi, gitLabProjectId.getGitLabId(), getWorkspaceBranchName(workspaceId, workspaceType, workspaceAccessType), 20, 1_000);
    } catch (Exception e) {
        throw buildException(e, () -> "User " + getCurrentUser() + " is not allowed to delete " + workspaceType.getLabel() + " " + workspaceAccessType.getLabel() + " " + workspaceId + " in project " + projectId, () -> "Unknown " + workspaceType.getLabel() + " " + workspaceAccessType.getLabel() + " (" + workspaceId + ") or project (" + projectId + ")", () -> "Error deleting " + workspaceType.getLabel() + " " + workspaceAccessType.getLabel() + " " + workspaceId + " in project " + projectId);
    }
    if (!originalBranchDeleted) {
        throw new LegendSDLCServerException("Failed to delete " + workspaceType.getLabel() + " " + workspaceAccessType.getLabel() + " " + workspaceId + " in project " + projectId);
    }
    // Create new workspace branch off the conflict workspace head
    Branch newWorkspaceBranch;
    try {
        // Wait to allow nodes to synchronize that original branch is already deleted.
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        LOGGER.warn("Interrupted while waiting for nodes to synchronize that original branch was deleted.", e);
        Thread.currentThread().interrupt();
    }
    try {
        newWorkspaceBranch = GitLabApiTools.createBranchFromSourceBranchAndVerify(repositoryApi, gitLabProjectId.getGitLabId(), getWorkspaceBranchName(workspaceId, workspaceType, workspaceAccessType), getWorkspaceBranchName(workspaceId, workspaceType, conflictResolutionWorkspaceType), 30, 1_000);
    } catch (Exception e) {
        throw buildException(e, () -> "User " + getCurrentUser() + " is not allowed to create " + workspaceType.getLabel() + " " + workspaceAccessType.getLabel() + " " + workspaceId + " in project " + projectId, () -> "Unknown " + workspaceType.getLabel() + " " + conflictResolutionWorkspaceType.getLabel() + " (" + workspaceId + ") or project (" + projectId + ")", () -> "Error creating " + workspaceType.getLabel() + " " + workspaceAccessType.getLabel() + " " + workspaceId + " in project " + projectId);
    }
    if (newWorkspaceBranch == null) {
        throw new LegendSDLCServerException("Failed to create " + workspaceType.getLabel() + " " + workspaceAccessType.getLabel() + " " + workspaceId + " from " + workspaceType.getLabel() + " " + conflictResolutionWorkspaceType.getLabel() + " " + workspaceId + " in project " + projectId);
    }
    // Delete conflict resolution branch
    boolean conflictResolutionWorkspaceDeleted;
    try {
        // No need to waste wait time here since conflict resolution branch was long created during update
        conflictResolutionWorkspaceDeleted = GitLabApiTools.deleteBranchAndVerify(repositoryApi, gitLabProjectId.getGitLabId(), getWorkspaceBranchName(workspaceId, workspaceType, conflictResolutionWorkspaceType), 20, 1_000);
    } catch (Exception e) {
        throw buildException(e, () -> "User " + getCurrentUser() + " is not allowed to delete " + workspaceType.getLabel() + " " + conflictResolutionWorkspaceType.getLabel() + " " + workspaceId + " in project " + projectId, () -> "Unknown " + workspaceType.getLabel() + " " + conflictResolutionWorkspaceType.getLabel() + " (" + workspaceId + ") or project (" + projectId + ")", () -> "Error deleting " + workspaceType.getLabel() + " " + conflictResolutionWorkspaceType.getLabel() + " " + workspaceId + " in project " + projectId);
    }
    if (!conflictResolutionWorkspaceDeleted) {
        throw new LegendSDLCServerException("Failed to delete " + workspaceType.getLabel() + " " + conflictResolutionWorkspaceType.getLabel() + " " + workspaceId + " in project " + projectId);
    }
    // Delete backup branch
    try {
        // Wait extra 500 ms to allow nodes to synchronize that backup branch was recreated
        Thread.sleep(500);
    } catch (InterruptedException e) {
        LOGGER.warn("Interrupted while waiting for nodes to synchronize that backup branch was recreated.", e);
        Thread.currentThread().interrupt();
    }
    try {
        boolean deleted = GitLabApiTools.deleteBranchAndVerify(repositoryApi, gitLabProjectId.getGitLabId(), getWorkspaceBranchName(workspaceId, workspaceType, backupWorkspaceType), 20, 1_000);
        if (!deleted) {
            LOGGER.error("Failed to delete {} {} in project {}", workspaceType.getLabel() + " " + backupWorkspaceType.getLabel(), workspaceId, projectId);
        }
    } catch (Exception e) {
        // unfortunate, but this should not throw error
        LOGGER.error("Error deleting {} {} in project {}", workspaceType.getLabel() + " " + backupWorkspaceType.getLabel(), workspaceId, projectId, e);
    }
}
Also used : ProjectFileAccessProvider(org.finos.legend.sdlc.server.project.ProjectFileAccessProvider) LegendSDLCServerException(org.finos.legend.sdlc.server.error.LegendSDLCServerException) GitLabProjectId(org.finos.legend.sdlc.server.gitlab.GitLabProjectId) Branch(org.gitlab4j.api.models.Branch) RepositoryApi(org.gitlab4j.api.RepositoryApi) GitLabApiException(org.gitlab4j.api.GitLabApiException) LegendSDLCServerException(org.finos.legend.sdlc.server.error.LegendSDLCServerException) GitLabApiException(org.gitlab4j.api.GitLabApiException)

Example 57 with LegendSDLCServerException

use of org.finos.legend.sdlc.server.error.LegendSDLCServerException in project legend-sdlc by finos.

the class GitLabEntityApi method updateEntities.

private Revision updateEntities(String projectId, String workspaceId, Iterable<? extends Entity> newEntities, boolean replace, String message, WorkspaceType workspaceType, ProjectFileAccessProvider.WorkspaceAccessType workspaceAccessType) {
    Map<String, Entity> newEntityDefinitions = Maps.mutable.empty();
    List<String> errorMessages = Lists.mutable.empty();
    for (Entity entity : newEntities) {
        if (entity == null) {
            errorMessages.add("Invalid entity: null");
            continue;
        }
        String path = entity.getPath();
        if (!isValidEntityPath(path)) {
            errorMessages.add("Invalid entity path: \"" + path + "\"");
            continue;
        }
        String classifierPath = entity.getClassifierPath();
        if (!isValidClassifierPath(classifierPath)) {
            errorMessages.add("Entity: " + path + "; error: invalid classifier path \"" + classifierPath + "\"");
        }
        Map<String, ?> content = entity.getContent();
        if (content == null) {
            errorMessages.add("Entity: " + path + "; error: missing content");
        } else if (path != null) {
            Object pkg = content.get("package");
            Object name = content.get("name");
            if (!(pkg instanceof String) || !(name instanceof String) || !path.equals(pkg + "::" + name)) {
                StringBuilder builder = new StringBuilder("Entity: ").append(path).append("; mismatch between entity path and package (");
                if (pkg instanceof String) {
                    builder.append('"').append(pkg).append('"');
                } else {
                    builder.append(pkg);
                }
                builder.append(") and name (");
                if (name instanceof String) {
                    builder.append('"').append(name).append('"');
                } else {
                    builder.append(name);
                }
                builder.append(") properties");
                errorMessages.add(builder.toString());
            }
        }
        Entity oldDefinition = newEntityDefinitions.put(path, entity);
        if (oldDefinition != null) {
            errorMessages.add("Entity: " + path + "; error: multiple definitions");
        }
    }
    if (!errorMessages.isEmpty()) {
        throw new LegendSDLCServerException((errorMessages.size() == 1) ? errorMessages.get(0) : "There are errors with entity definitions:\n\t" + String.join("\n\t", errorMessages), Status.BAD_REQUEST);
    }
    Revision currentWorkspaceRevision = getProjectFileAccessProvider().getRevisionAccessContext(projectId, workspaceId, workspaceType, workspaceAccessType).getCurrentRevision();
    if (currentWorkspaceRevision == null) {
        throw new LegendSDLCServerException("Could not find current revision for " + workspaceType.getLabel() + " " + workspaceAccessType.getLabel() + " " + workspaceId + " in project " + projectId + ": " + workspaceType.getLabel() + " " + workspaceAccessType.getLabel() + " may be corrupt");
    }
    String revisionId = currentWorkspaceRevision.getId();
    LOGGER.debug("Using revision {} for reference in entity update in {} {} in project {}", revisionId, workspaceType.getLabel() + " " + workspaceAccessType.getLabel(), workspaceId, projectId);
    List<EntityChange> entityChanges = Lists.mutable.ofInitialCapacity(newEntityDefinitions.size());
    if (newEntityDefinitions.isEmpty()) {
        if (replace) {
            try (Stream<EntityProjectFile> stream = getEntityProjectFiles(projectId, workspaceId, revisionId, workspaceType, workspaceAccessType)) {
                stream.map(EntityProjectFile::getEntityPath).map(EntityChange::newDeleteEntity).forEach(entityChanges::add);
            }
        }
    } else {
        try (Stream<EntityProjectFile> stream = getEntityProjectFiles(projectId, workspaceId, revisionId, workspaceType, workspaceAccessType)) {
            stream.forEach(epf -> {
                String path = epf.getEntityPath();
                Entity newDefinition = newEntityDefinitions.remove(path);
                if (newDefinition == null) {
                    if (replace) {
                        entityChanges.add(EntityChange.newDeleteEntity(path));
                    }
                } else {
                    Entity entity = epf.getEntity();
                    String newClassifierPath = newDefinition.getClassifierPath();
                    Map<String, ?> newContent = newDefinition.getContent();
                    if (!newClassifierPath.equals(entity.getClassifierPath()) || !newContent.equals(entity.getContent())) {
                        entityChanges.add(EntityChange.newModifyEntity(path, newClassifierPath, newContent));
                    }
                }
            });
        }
        newEntityDefinitions.values().forEach(definition -> entityChanges.add(EntityChange.newCreateEntity(definition.getPath(), definition.getClassifierPath(), definition.getContent())));
    }
    return performChanges(projectId, workspaceId, revisionId, message, entityChanges, workspaceType, workspaceAccessType);
}
Also used : Entity(org.finos.legend.sdlc.domain.model.entity.Entity) EntityChange(org.finos.legend.sdlc.domain.model.entity.change.EntityChange) LegendSDLCServerException(org.finos.legend.sdlc.server.error.LegendSDLCServerException) Revision(org.finos.legend.sdlc.domain.model.revision.Revision)

Example 58 with LegendSDLCServerException

use of org.finos.legend.sdlc.server.error.LegendSDLCServerException in project legend-sdlc by finos.

the class GitLabIssueApi method deleteIssue.

@Override
public void deleteIssue(String projectId, String issueId) {
    LegendSDLCServerException.validateNonNull(projectId, "projectId may not be null");
    LegendSDLCServerException.validateNonNull(issueId, "issueId may not be null");
    try {
        GitLabProjectId gitLabProjectId = parseProjectId(projectId);
        withRetries(() -> getGitLabApi(gitLabProjectId.getGitLabMode()).getIssuesApi().deleteIssue(gitLabProjectId.getGitLabId(), parseIntegerIdIfNotNull(issueId)));
    } catch (LegendSDLCServerException e) {
        throw e;
    } catch (Exception e) {
        throw buildException(e, () -> "User " + getCurrentUser() + " is not allowed to delete issue " + issueId + " for project " + projectId, () -> "Unknown issue (" + issueId + ") or project (" + projectId + ")", () -> "Failed to delete issue " + issueId + " for project " + projectId);
    }
}
Also used : LegendSDLCServerException(org.finos.legend.sdlc.server.error.LegendSDLCServerException) GitLabProjectId(org.finos.legend.sdlc.server.gitlab.GitLabProjectId) LegendSDLCServerException(org.finos.legend.sdlc.server.error.LegendSDLCServerException)

Example 59 with LegendSDLCServerException

use of org.finos.legend.sdlc.server.error.LegendSDLCServerException in project legend-sdlc by finos.

the class GitLabProjectApi method checkUserAuthorizationAction.

@Override
public boolean checkUserAuthorizationAction(String id, ProjectAuthorizationAction action) {
    try {
        GitLabProjectId projectId = parseProjectId(id);
        org.gitlab4j.api.models.Project gitLabProject = withRetries(() -> getGitLabApi(projectId.getGitLabMode()).getProjectApi().getProject(projectId.getGitLabId()));
        if (!isLegendSDLCProject(gitLabProject)) {
            throw new LegendSDLCServerException("Failed to get project " + id);
        }
        AccessLevel userLevel = getUserAccess(gitLabProject);
        if (userLevel == null) {
            return false;
        }
        return checkUserAction(projectId, action, userLevel);
    } catch (Exception e) {
        throw buildException(e, () -> "Failed to get project " + id);
    }
}
Also used : LegendSDLCServerException(org.finos.legend.sdlc.server.error.LegendSDLCServerException) GitLabProjectId(org.finos.legend.sdlc.server.gitlab.GitLabProjectId) AccessLevel(org.gitlab4j.api.models.AccessLevel) LegendSDLCServerException(org.finos.legend.sdlc.server.error.LegendSDLCServerException)

Example 60 with LegendSDLCServerException

use of org.finos.legend.sdlc.server.error.LegendSDLCServerException in project legend-sdlc by finos.

the class GitLabProjectApi method getCurrentUserAccessRole.

@Override
public AccessRole getCurrentUserAccessRole(String id) {
    LegendSDLCServerException.validateNonNull(id, "id may not be null");
    try {
        GitLabProjectId projectId = parseProjectId(id);
        org.gitlab4j.api.models.Project gitLabProject = withRetries(() -> getGitLabApi(projectId.getGitLabMode()).getProjectApi().getProject(projectId.getGitLabId()));
        if (!isLegendSDLCProject(gitLabProject)) {
            throw new LegendSDLCServerException("Failed to get project " + id);
        }
        Permissions permissions = gitLabProject.getPermissions();
        if (permissions != null) {
            ProjectAccess projectAccess = permissions.getProjectAccess();
            AccessLevel projectAccessLevel = (projectAccess == null) ? null : projectAccess.getAccessLevel();
            if (projectAccessLevel != null) {
                return new AccessRoleWrapper(projectAccessLevel);
            }
            ProjectAccess groupAccess = permissions.getGroupAccess();
            AccessLevel groupAccessLevel = (groupAccess == null) ? null : groupAccess.getAccessLevel();
            if (groupAccessLevel != null) {
                return new AccessRoleWrapper(groupAccessLevel);
            }
        }
        return null;
    } catch (Exception e) {
        throw buildException(e, () -> "Failed to get project " + id);
    }
}
Also used : LegendSDLCServerException(org.finos.legend.sdlc.server.error.LegendSDLCServerException) GitLabProjectId(org.finos.legend.sdlc.server.gitlab.GitLabProjectId) Permissions(org.gitlab4j.api.models.Permissions) ProjectAccess(org.gitlab4j.api.models.ProjectAccess) AccessLevel(org.gitlab4j.api.models.AccessLevel) LegendSDLCServerException(org.finos.legend.sdlc.server.error.LegendSDLCServerException)

Aggregations

LegendSDLCServerException (org.finos.legend.sdlc.server.error.LegendSDLCServerException)64 GitLabProjectId (org.finos.legend.sdlc.server.gitlab.GitLabProjectId)34 GitLabApiException (org.gitlab4j.api.GitLabApiException)23 ProjectFileAccessProvider (org.finos.legend.sdlc.server.project.ProjectFileAccessProvider)20 RepositoryApi (org.gitlab4j.api.RepositoryApi)17 MergeRequest (org.gitlab4j.api.models.MergeRequest)16 GitLabApi (org.gitlab4j.api.GitLabApi)14 Branch (org.gitlab4j.api.models.Branch)11 ProjectStructure (org.finos.legend.sdlc.server.project.ProjectStructure)10 ProjectConfiguration (org.finos.legend.sdlc.domain.model.project.configuration.ProjectConfiguration)7 GitLabMode (org.finos.legend.sdlc.server.gitlab.mode.GitLabMode)7 DiffRef (org.gitlab4j.api.models.DiffRef)7 Revision (org.finos.legend.sdlc.domain.model.revision.Revision)6 VersionId (org.finos.legend.sdlc.domain.model.version.VersionId)6 MergeRequestApi (org.gitlab4j.api.MergeRequestApi)6 List (java.util.List)5 CommitsApi (org.gitlab4j.api.CommitsApi)5 Commit (org.gitlab4j.api.models.Commit)5 Comparator (java.util.Comparator)4 Pattern (java.util.regex.Pattern)4