diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/domains/ce/GitArtifactMetadataCE.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/domains/ce/GitArtifactMetadataCE.java index 0cfd1ca44f6f..f0988e8956c8 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/domains/ce/GitArtifactMetadataCE.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/domains/ce/GitArtifactMetadataCE.java @@ -130,5 +130,17 @@ public void setDefaultApplicationId(String defaultApplicationId) { this.defaultArtifactId = defaultApplicationId; } + /** + * this returns the branchName instead of reference name + * @return returns the ref name. + */ + public String getRefName() { + return this.getBranchName(); + } + + public void setRefName(String refName) { + this.branchName = refName; + } + public static class Fields {} } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/git/central/CentralGitServiceCE.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/git/central/CentralGitServiceCE.java index b713af4502b6..68473742e46a 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/git/central/CentralGitServiceCE.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/git/central/CentralGitServiceCE.java @@ -49,4 +49,7 @@ Mono checkoutReference( Mono createReference( String referencedArtifactId, GitRefDTO refDTO, ArtifactType artifactType, GitType gitType); + + Mono deleteGitReference( + String baseArtifactId, GitRefDTO gitRefDTO, ArtifactType artifactType, GitType gitType); } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/git/central/CentralGitServiceCEImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/git/central/CentralGitServiceCEImpl.java index 7fa8cdca3e11..207310d4e043 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/git/central/CentralGitServiceCEImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/git/central/CentralGitServiceCEImpl.java @@ -538,7 +538,7 @@ public Mono createReference( Artifact newRefArtifact = tuple.getT1(); Mono refCreationMono = gitHandlingService - .prepareForNewRefCreation(jsonTransformationDTO) + .createGitReference(jsonTransformationDTO) // TODO: ths error could be shipped to handling layer as well? .onErrorResume(error -> Mono.error(new AppsmithException( AppsmithError.GIT_ACTION_FAILED, @@ -572,6 +572,127 @@ public Mono createReference( return Mono.create(sink -> createBranchMono.subscribe(sink::success, sink::error, null, sink.currentContext())); } + @Override + public Mono deleteGitReference( + String baseArtifactId, GitRefDTO gitRefDTO, ArtifactType artifactType, GitType gitType) { + + String refName = gitRefDTO.getRefName(); + RefType refType = gitRefDTO.getRefType(); + + if (refType == null) { + return Mono.error(new AppsmithException(AppsmithError.INVALID_PARAMETER, REF_TYPE)); + } + + if (!hasText(refName)) { + return Mono.error(new AppsmithException(AppsmithError.INVALID_PARAMETER, REF_NAME)); + } + + if (!hasText(baseArtifactId)) { + return Mono.error(new AppsmithException(AppsmithError.INVALID_PARAMETER, FieldName.ID)); + } + + GitArtifactHelper gitArtifactHelper = gitArtifactHelperResolver.getArtifactHelper(artifactType); + AclPermission artifactEditPermission = gitArtifactHelper.getArtifactEditPermission(); + + Mono baseArtifactMono = + gitArtifactHelper.getArtifactById(baseArtifactId, artifactEditPermission); + + Mono branchedArtifactMono = + gitArtifactHelper.getArtifactByBaseIdAndBranchName(baseArtifactId, refName, artifactEditPermission); + + return Mono.zip(baseArtifactMono, branchedArtifactMono).flatMap(tuple2 -> { + Artifact baseArtifact = tuple2.getT1(); + Artifact referenceArtifact = tuple2.getT2(); + return deleteGitReference(baseArtifact, referenceArtifact, gitType, refType); + }); + } + + protected Mono deleteGitReference( + Artifact baseArtifact, Artifact referenceArtifact, GitType gitType, RefType refType) { + + GitArtifactMetadata baseGitMetadata = baseArtifact.getGitArtifactMetadata(); + GitArtifactMetadata referenceArtifactMetadata = referenceArtifact.getGitArtifactMetadata(); + + GitHandlingService gitHandlingService = gitHandlingServiceResolver.getGitHandlingService(gitType); + GitArtifactHelper gitArtifactHelper = + gitArtifactHelperResolver.getArtifactHelper(baseArtifact.getArtifactType()); + + // TODO: write a migration to shift everything to refName in gitMetadata + final String finalRefName = referenceArtifactMetadata.getRefName(); + final String baseArtifactId = referenceArtifact.getGitArtifactMetadata().getDefaultArtifactId(); + + if (finalRefName.equals(baseGitMetadata.getDefaultBranchName())) { + return Mono.error(new AppsmithException( + AppsmithError.GIT_ACTION_FAILED, "delete ref", " Cannot delete default branch")); + } + + Mono deleteReferenceMono = gitPrivateRepoHelper + .isBranchProtected(baseGitMetadata, finalRefName) + .flatMap(isBranchProtected -> { + if (!TRUE.equals(isBranchProtected)) { + return gitRedisUtils.acquireGitLock( + baseArtifactId, GitConstants.GitCommandConstants.DELETE, TRUE); + } + + return Mono.error(new AppsmithException( + AppsmithError.GIT_ACTION_FAILED, + "delete", + "Cannot delete protected branch " + finalRefName)); + }) + .flatMap(ignoreLockAcquisition -> { + ArtifactJsonTransformationDTO jsonTransformationDTO = new ArtifactJsonTransformationDTO(); + jsonTransformationDTO.setWorkspaceId(baseArtifact.getWorkspaceId()); + jsonTransformationDTO.setArtifactType(baseArtifact.getArtifactType()); + jsonTransformationDTO.setRefType(refType); + jsonTransformationDTO.setRefName(finalRefName); + jsonTransformationDTO.setBaseArtifactId(baseArtifactId); + jsonTransformationDTO.setRepoName(referenceArtifactMetadata.getRepoName()); + + return gitHandlingService + .deleteGitReference(jsonTransformationDTO) + .doFinally(signalType -> gitRedisUtils.releaseFileLock(baseArtifactId, TRUE)) + .flatMap(isReferenceDeleted -> { + if (FALSE.equals(isReferenceDeleted)) { + return Mono.error(new AppsmithException( + AppsmithError.GIT_ACTION_FAILED, + " delete branch. Branch does not exists in the repo")); + } + + if (referenceArtifact.getId().equals(baseArtifactId)) { + return Mono.just(referenceArtifact); + } + + return gitArtifactHelper + .deleteArtifactByResource(referenceArtifact) + .onErrorResume(throwable -> { + log.error( + "An error occurred while deleting db artifact and resources for reference {}", + throwable.getMessage()); + + return gitAnalyticsUtils + .addAnalyticsForGitOperation( + AnalyticsEvents.GIT_DELETE_BRANCH, + referenceArtifact, + throwable.getClass().getName(), + throwable.getMessage(), + baseGitMetadata.getIsRepoPrivate()) + .then(Mono.error(new AppsmithException( + AppsmithError.GIT_ACTION_FAILED, + "Cannot delete branch from database"))); + }); + }); + }) + .flatMap(deletedArtifact -> gitAnalyticsUtils.addAnalyticsForGitOperation( + AnalyticsEvents.GIT_DELETE_BRANCH, + deletedArtifact, + deletedArtifact.getGitArtifactMetadata().getIsRepoPrivate())) + .name(GitSpan.OPS_DELETE_BRANCH) + .tap(Micrometer.observation(observationRegistry)); + + return Mono.create( + sink -> deleteReferenceMono.subscribe(sink::success, sink::error, null, sink.currentContext())); + } + /** * Connect the artifact from Appsmith to a git repo * This is the prerequisite step needed to perform all the git operation for an artifact diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/git/central/GitHandlingServiceCE.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/git/central/GitHandlingServiceCE.java index 374fcc90da7f..3159627aeb77 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/git/central/GitHandlingServiceCE.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/git/central/GitHandlingServiceCE.java @@ -76,5 +76,7 @@ Mono recreateArtifactJsonFromLastCommit( Mono getStatus(ArtifactJsonTransformationDTO jsonTransformationDTO); - Mono prepareForNewRefCreation(ArtifactJsonTransformationDTO artifactJsonTransformationDTO); + Mono createGitReference(ArtifactJsonTransformationDTO artifactJsonTransformationDTO); + + Mono deleteGitReference(ArtifactJsonTransformationDTO jsonTransformationDTO); } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/git/fs/GitFSServiceCEImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/git/fs/GitFSServiceCEImpl.java index e9d0cfa4eed8..a642383ce230 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/git/fs/GitFSServiceCEImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/git/fs/GitFSServiceCEImpl.java @@ -44,6 +44,7 @@ import io.micrometer.observation.ObservationRegistry; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.eclipse.jgit.api.errors.CannotDeleteCurrentBranchException; import org.eclipse.jgit.api.errors.EmptyCommitException; import org.eclipse.jgit.api.errors.InvalidRemoteException; import org.eclipse.jgit.api.errors.TransportException; @@ -661,7 +662,7 @@ public Mono getStatus(ArtifactJsonTransformationDTO jsonTransforma } @Override - public Mono prepareForNewRefCreation(ArtifactJsonTransformationDTO jsonTransformationDTO) { + public Mono createGitReference(ArtifactJsonTransformationDTO jsonTransformationDTO) { GitArtifactHelper gitArtifactHelper = gitArtifactHelperResolver.getArtifactHelper(jsonTransformationDTO.getArtifactType()); @@ -672,4 +673,29 @@ public Mono prepareForNewRefCreation(ArtifactJsonTransformationDTO jsonT return fsGitHandler.createAndCheckoutToBranch(repoSuffix, jsonTransformationDTO.getRefName()); } + + @Override + public Mono deleteGitReference(ArtifactJsonTransformationDTO jsonTransformationDTO) { + GitArtifactHelper gitArtifactHelper = + gitArtifactHelperResolver.getArtifactHelper(jsonTransformationDTO.getArtifactType()); + + Path repoSuffix = gitArtifactHelper.getRepoSuffixPath( + jsonTransformationDTO.getWorkspaceId(), + jsonTransformationDTO.getBaseArtifactId(), + jsonTransformationDTO.getRepoName()); + + return fsGitHandler + .deleteBranch(repoSuffix, jsonTransformationDTO.getRefName()) + .onErrorResume(throwable -> { + log.error("Delete branch failed {}", throwable.getMessage()); + if (throwable instanceof CannotDeleteCurrentBranchException) { + return Mono.error(new AppsmithException( + AppsmithError.GIT_ACTION_FAILED, + "delete branch", + "Cannot delete current checked out branch")); + } + return Mono.error(new AppsmithException( + AppsmithError.GIT_ACTION_FAILED, "delete branch", throwable.getMessage())); + }); + } }