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);
}
}
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);
}
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);
}
}
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);
}
}
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);
}
}
Aggregations