diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/applications/base/ApplicationServiceCEImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/applications/base/ApplicationServiceCEImpl.java index 068be68114..9254892260 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/applications/base/ApplicationServiceCEImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/applications/base/ApplicationServiceCEImpl.java @@ -301,10 +301,10 @@ public class ApplicationServiceCEImpl extends BaseService { GitArtifactMetadata gitArtifactMetadata = defaultApplication.getGitApplicationMetadata(); @@ -770,7 +770,7 @@ public class ApplicationServiceCEImpl extends BaseService { GitAuthDTO gitAuthDTO = new GitAuthDTO(); GitAuth gitAuth = rootApplication diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/applications/git/GitApplicationHelperCEImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/applications/git/GitApplicationHelperCEImpl.java index c5fe579186..83356f1ed8 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/applications/git/GitApplicationHelperCEImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/applications/git/GitApplicationHelperCEImpl.java @@ -11,6 +11,8 @@ import com.appsmith.server.domains.Artifact; import com.appsmith.server.domains.GitArtifactMetadata; import com.appsmith.server.domains.NewAction; import com.appsmith.server.domains.NewPage; +import com.appsmith.server.dtos.ApplicationJson; +import com.appsmith.server.dtos.ArtifactExchangeJson; import com.appsmith.server.dtos.GitAuthDTO; import com.appsmith.server.exceptions.AppsmithError; import com.appsmith.server.exceptions.AppsmithException; @@ -47,7 +49,7 @@ import static com.appsmith.server.helpers.DefaultResourcesUtils.createDefaultIds @RequiredArgsConstructor public class GitApplicationHelperCEImpl implements GitArtifactHelperCE { - private final CommonGitFileUtils gitFileUtils; + private final CommonGitFileUtils commonGitFileUtils; private final GitPrivateRepoHelper gitPrivateRepoHelper; private final ApplicationService applicationService; @@ -178,9 +180,9 @@ public class GitApplicationHelperCEImpl implements GitArtifactHelperCE publishArtifact(Artifact artifact) { + public Mono publishArtifact(Artifact artifact, Boolean isPublishedManually) { Application application = (Application) artifact; - return applicationPageService.publish(application.getId(), true).then(Mono.just(application)); + return applicationPageService.publish(application.getId(), isPublishedManually); } // TODO: scope for improvement @@ -204,7 +206,7 @@ public class GitApplicationHelperCEImpl implements GitArtifactHelperCE { log.error("Error while initialising git repo, {0}", throwable); @@ -305,4 +307,23 @@ public class GitApplicationHelperCEImpl implements GitArtifactHelperCE createArtifactForImport(String workspaceId, String repoName) { + Application newApplication = new Application(); + newApplication.setName(repoName); + newApplication.setWorkspaceId(workspaceId); + newApplication.setGitApplicationMetadata(new GitArtifactMetadata()); + return applicationPageService.createOrUpdateSuffixedApplication(newApplication, newApplication.getName(), 0); + } + + @Override + public Mono deleteArtifact(String artifactId) { + return applicationPageService.deleteApplication(artifactId); + } + + @Override + public Boolean isContextInArtifactEmpty(ArtifactExchangeJson artifactExchangeJson) { + return CollectionUtils.isNullOrEmpty(((ApplicationJson) artifactExchangeJson).getPageList()); + } } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/applications/imports/ApplicationImportServiceCEImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/applications/imports/ApplicationImportServiceCEImpl.java index d8889bd077..3babdf67da 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/applications/imports/ApplicationImportServiceCEImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/applications/imports/ApplicationImportServiceCEImpl.java @@ -436,7 +436,7 @@ public class ApplicationImportServiceCEImpl Mono parentApplicationMono; if (application.getGitApplicationMetadata() != null) { parentApplicationMono = applicationService.findById( - application.getGitApplicationMetadata().getDefaultApplicationId()); + application.getGitApplicationMetadata().getDefaultArtifactId()); } else { parentApplicationMono = Mono.just(application); } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/controllers/GitController.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/controllers/GitController.java index d5d0c1ad72..a8278337c7 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/controllers/GitController.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/controllers/GitController.java @@ -2,7 +2,7 @@ package com.appsmith.server.controllers; import com.appsmith.server.constants.Url; import com.appsmith.server.controllers.ce.GitControllerCE; -import com.appsmith.server.services.GitService; +import com.appsmith.server.git.common.CommonGitService; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @@ -12,7 +12,7 @@ import org.springframework.web.bind.annotation.RestController; @RequestMapping(Url.GIT_URL) public class GitController extends GitControllerCE { - public GitController(GitService service) { + public GitController(CommonGitService service) { super(service); } } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/controllers/ce/GitControllerCE.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/controllers/ce/GitControllerCE.java index 1bbc642b4b..e45695d9d0 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/controllers/ce/GitControllerCE.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/controllers/ce/GitControllerCE.java @@ -5,6 +5,7 @@ import com.appsmith.external.dtos.GitLogDTO; import com.appsmith.external.dtos.GitStatusDTO; import com.appsmith.external.dtos.MergeStatusDTO; import com.appsmith.external.views.Views; +import com.appsmith.server.constants.ArtifactType; import com.appsmith.server.constants.FieldName; import com.appsmith.server.constants.Url; import com.appsmith.server.domains.Application; @@ -21,14 +22,14 @@ import com.appsmith.server.dtos.GitDocsDTO; import com.appsmith.server.dtos.GitMergeDTO; import com.appsmith.server.dtos.GitPullDTO; import com.appsmith.server.dtos.ResponseDTO; +import com.appsmith.server.git.common.CommonGitService; import com.appsmith.server.helpers.GitDeployKeyGenerator; -import com.appsmith.server.services.GitService; import com.fasterxml.jackson.annotation.JsonView; import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.BooleanUtils; import org.eclipse.jgit.lib.BranchTrackingStatus; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; @@ -48,14 +49,10 @@ import java.util.Map; @Slf4j @RequestMapping(Url.GIT_URL) +@RequiredArgsConstructor public class GitControllerCE { - private final GitService service; - - @Autowired - public GitControllerCE(GitService service) { - this.service = service; - } + private final CommonGitService service; /** * applicationId is the defaultApplicationId @@ -97,7 +94,7 @@ public class GitControllerCE { @JsonView({Views.Metadata.class}) @GetMapping("/metadata/app/{defaultApplicationId}") public Mono> getGitMetadata(@PathVariable String defaultApplicationId) { - return service.getGitApplicationMetadata(defaultApplicationId) + return service.getGitArtifactMetadata(defaultApplicationId, ArtifactType.APPLICATION) .map(metadata -> new ResponseDTO<>(HttpStatus.OK.value(), metadata, null)); } @@ -107,7 +104,8 @@ public class GitControllerCE { @PathVariable String defaultApplicationId, @RequestBody GitConnectDTO gitConnectDTO, @RequestHeader("Origin") String originHeader) { - return service.connectApplicationToGit(defaultApplicationId, gitConnectDTO, originHeader) + return service.connectArtifactToGit(defaultApplicationId, gitConnectDTO, originHeader, ArtifactType.APPLICATION) + .map(artefact -> (Application) artefact) .map(application -> new ResponseDTO<>(HttpStatus.OK.value(), application, null)); } @@ -120,7 +118,7 @@ public class GitControllerCE { @RequestHeader(name = FieldName.BRANCH_NAME, required = false) String branchName, @RequestParam(required = false, defaultValue = "false") Boolean doAmend) { log.debug("Going to commit application {}, branch : {}", defaultApplicationId, branchName); - return service.commitApplication(commitDTO, defaultApplicationId, branchName, doAmend) + return service.commitArtifact(commitDTO, defaultApplicationId, branchName, doAmend, ArtifactType.APPLICATION) .map(result -> new ResponseDTO<>(HttpStatus.CREATED.value(), result, null)); } @@ -130,7 +128,7 @@ public class GitControllerCE { @PathVariable String defaultApplicationId, @RequestHeader(name = FieldName.BRANCH_NAME, required = false) String branchName) { log.debug("Fetching commit-history for application {}, branch : {}", defaultApplicationId, branchName); - return service.getCommitHistory(defaultApplicationId, branchName) + return service.getCommitHistory(defaultApplicationId, branchName, ArtifactType.APPLICATION) .map(logs -> new ResponseDTO<>(HttpStatus.OK.value(), logs, null)); } @@ -141,7 +139,7 @@ public class GitControllerCE { @PathVariable String defaultApplicationId, @RequestHeader(name = FieldName.BRANCH_NAME, required = false) String branchName) { log.debug("Going to push application application {}, branch : {}", defaultApplicationId, branchName); - return service.pushApplication(defaultApplicationId, branchName) + return service.pushArtifact(defaultApplicationId, branchName, ArtifactType.APPLICATION) .map(result -> new ResponseDTO<>(HttpStatus.CREATED.value(), result, null)); } @@ -153,7 +151,8 @@ public class GitControllerCE { @RequestHeader(name = FieldName.BRANCH_NAME, required = false) String srcBranch, @RequestBody GitBranchDTO branchDTO) { log.debug("Going to create a branch from root application {}, srcBranch {}", defaultApplicationId, srcBranch); - return service.createBranch(defaultApplicationId, branchDTO, srcBranch) + return service.createBranch(defaultApplicationId, branchDTO, srcBranch, ArtifactType.APPLICATION) + .map(artefact -> (Application) artefact) .map(result -> new ResponseDTO<>(HttpStatus.CREATED.value(), result, null)); } @@ -163,7 +162,8 @@ public class GitControllerCE { @PathVariable String defaultApplicationId, @RequestParam(name = FieldName.BRANCH_NAME, required = false) String branchName) { log.debug("Going to checkout to branch {} application {} ", branchName, defaultApplicationId); - return service.checkoutBranch(defaultApplicationId, branchName, true) + return service.checkoutBranch(defaultApplicationId, branchName, true, ArtifactType.APPLICATION) + .map(artefact -> (Application) artefact) .map(result -> new ResponseDTO<>(HttpStatus.OK.value(), result, null)); } @@ -171,7 +171,8 @@ public class GitControllerCE { @PostMapping("/disconnect/app/{defaultApplicationId}") public Mono> disconnectFromRemote(@PathVariable String defaultApplicationId) { log.debug("Going to remove the remoteUrl for application {}", defaultApplicationId); - return service.detachRemote(defaultApplicationId) + return service.detachRemote(defaultApplicationId, ArtifactType.APPLICATION) + .map(artefact -> (Application) artefact) .map(result -> new ResponseDTO<>(HttpStatus.OK.value(), result, null)); } @@ -181,7 +182,7 @@ public class GitControllerCE { @PathVariable String defaultApplicationId, @RequestHeader(name = FieldName.BRANCH_NAME, required = false) String branchName) { log.debug("Going to pull the latest for application {}, branch {}", defaultApplicationId, branchName); - return service.pullApplication(defaultApplicationId, branchName) + return service.pullArtifact(defaultApplicationId, branchName, ArtifactType.APPLICATION) .map(result -> new ResponseDTO<>(HttpStatus.OK.value(), result, null)); } @@ -192,7 +193,8 @@ public class GitControllerCE { @RequestParam(required = false, defaultValue = "false") Boolean pruneBranches, @RequestHeader(name = FieldName.BRANCH_NAME, required = false) String branchName) { log.debug("Going to get branch list for application {}", defaultApplicationId); - return service.listBranchForApplication(defaultApplicationId, BooleanUtils.isTrue(pruneBranches), branchName) + return service.listBranchForArtifact( + defaultApplicationId, BooleanUtils.isTrue(pruneBranches), branchName, ArtifactType.APPLICATION) .map(result -> new ResponseDTO<>(HttpStatus.OK.value(), result, null)); } @@ -203,7 +205,7 @@ public class GitControllerCE { @RequestHeader(name = FieldName.BRANCH_NAME, required = false) String branchName, @RequestParam(required = false, defaultValue = "true") Boolean compareRemote) { log.debug("Going to get status for default application {}, branch {}", defaultApplicationId, branchName); - return service.getStatus(defaultApplicationId, compareRemote, branchName) + return service.getStatus(defaultApplicationId, compareRemote, branchName, ArtifactType.APPLICATION) .map(result -> new ResponseDTO<>(HttpStatus.OK.value(), result, null)); } @@ -214,7 +216,7 @@ public class GitControllerCE { @RequestHeader(name = FieldName.BRANCH_NAME, required = false) String branchName) { log.debug( "Going to compare with remote for default application {}, branch {}", defaultApplicationId, branchName); - return service.fetchRemoteChanges(defaultApplicationId, branchName, true) + return service.fetchRemoteChanges(defaultApplicationId, branchName, true, ArtifactType.APPLICATION) .map(result -> new ResponseDTO<>(HttpStatus.OK.value(), result, null)); } @@ -227,7 +229,7 @@ public class GitControllerCE { gitMergeDTO.getSourceBranch(), gitMergeDTO.getDestinationBranch(), defaultApplicationId); - return service.mergeBranch(defaultApplicationId, gitMergeDTO) + return service.mergeBranch(defaultApplicationId, gitMergeDTO, ArtifactType.APPLICATION) .map(result -> new ResponseDTO<>(HttpStatus.OK.value(), result, null)); } @@ -240,7 +242,7 @@ public class GitControllerCE { gitMergeDTO.getSourceBranch(), gitMergeDTO.getDestinationBranch(), defaultApplicationId); - return service.isBranchMergeable(defaultApplicationId, gitMergeDTO) + return service.isBranchMergeable(defaultApplicationId, gitMergeDTO, ArtifactType.APPLICATION) .map(result -> new ResponseDTO<>(HttpStatus.OK.value(), result, null)); } @@ -249,7 +251,7 @@ public class GitControllerCE { public Mono> createConflictedBranch( @PathVariable String defaultApplicationId, @RequestHeader(name = FieldName.BRANCH_NAME) String branchName) { log.debug("Going to create conflicted state branch {} for application {}", branchName, defaultApplicationId); - return service.createConflictedBranch(defaultApplicationId, branchName) + return service.createConflictedBranch(defaultApplicationId, branchName, ArtifactType.APPLICATION) .map(result -> new ResponseDTO<>(HttpStatus.OK.value(), result, null)); } @@ -263,14 +265,15 @@ public class GitControllerCE { @PostMapping("/import/{workspaceId}") public Mono> importApplicationFromGit( @PathVariable String workspaceId, @RequestBody GitConnectDTO gitConnectDTO) { - return service.importApplicationFromGit(workspaceId, gitConnectDTO) + return service.importArtifactFromGit(workspaceId, gitConnectDTO, ArtifactType.APPLICATION) + .map(artifactImportDTO -> (ApplicationImportDTO) artifactImportDTO) .map(result -> new ResponseDTO<>(HttpStatus.CREATED.value(), result, null)); } @JsonView(Views.Public.class) @GetMapping("/test-connection/app/{defaultApplicationId}") public Mono> testGitConnection(@PathVariable String defaultApplicationId) { - return service.testConnection(defaultApplicationId) + return service.testConnection(defaultApplicationId, ArtifactType.APPLICATION) .map(result -> new ResponseDTO<>((HttpStatus.OK.value()), result, null)); } @@ -279,7 +282,8 @@ public class GitControllerCE { public Mono> deleteBranch( @PathVariable String defaultApplicationId, @RequestParam String branchName) { log.debug("Going to delete branch {} for defaultApplicationId {}", branchName, defaultApplicationId); - return service.deleteBranch(defaultApplicationId, branchName) + return service.deleteBranch(defaultApplicationId, branchName, ArtifactType.APPLICATION) + .map(artefact -> (Application) artefact) .map(application -> new ResponseDTO<>(HttpStatus.OK.value(), application, null)); } @@ -291,7 +295,8 @@ public class GitControllerCE { "Going to discard changes for branch {} with defaultApplicationId {}", branchName, defaultApplicationId); - return service.discardChanges(defaultApplicationId, branchName) + return service.discardChanges(defaultApplicationId, branchName, ArtifactType.APPLICATION) + .map(artefact -> (Application) artefact) .map(result -> new ResponseDTO<>((HttpStatus.OK.value()), result, null)); } @@ -314,14 +319,15 @@ public class GitControllerCE { public Mono>> updateProtectedBranches( @PathVariable String defaultApplicationId, @RequestBody @Valid BranchProtectionRequestDTO branchProtectionRequestDTO) { - return service.updateProtectedBranches(defaultApplicationId, branchProtectionRequestDTO.getBranchNames()) + return service.updateProtectedBranches( + defaultApplicationId, branchProtectionRequestDTO.getBranchNames(), ArtifactType.APPLICATION) .map(data -> new ResponseDTO<>(HttpStatus.OK.value(), data, null)); } @JsonView(Views.Public.class) @GetMapping("/branch/app/{defaultApplicationId}/protected") public Mono>> getProtectedBranches(@PathVariable String defaultApplicationId) { - return service.getProtectedBranches(defaultApplicationId) + return service.getProtectedBranches(defaultApplicationId, ArtifactType.APPLICATION) .map(list -> new ResponseDTO<>(HttpStatus.OK.value(), list, null)); } @@ -329,21 +335,21 @@ public class GitControllerCE { @PostMapping("/auto-commit/app/{defaultApplicationId}") public Mono> autoCommit( @PathVariable String defaultApplicationId, @RequestParam String branchName) { - return service.autoCommitApplication(defaultApplicationId, branchName) + return service.autoCommitApplication(defaultApplicationId, branchName, ArtifactType.APPLICATION) .map(data -> new ResponseDTO<>(HttpStatus.OK.value(), data, null)); } @JsonView(Views.Public.class) @GetMapping("/auto-commit/progress/app/{defaultApplicationId}") public Mono> getAutoCommitProgress(@PathVariable String defaultApplicationId) { - return service.getAutoCommitProgress(defaultApplicationId) + return service.getAutoCommitProgress(defaultApplicationId, ArtifactType.APPLICATION) .map(data -> new ResponseDTO<>(HttpStatus.OK.value(), data, null)); } @JsonView(Views.Public.class) @PatchMapping("/auto-commit/toggle/app/{defaultApplicationId}") public Mono> toggleAutoCommitEnabled(@PathVariable String defaultApplicationId) { - return service.toggleAutoCommitEnabled(defaultApplicationId) + return service.toggleAutoCommitEnabled(defaultApplicationId, ArtifactType.APPLICATION) .map(data -> new ResponseDTO<>(HttpStatus.OK.value(), data, null)); } } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/domains/Application.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/domains/Application.java index 3b88521ed6..11349c0c0c 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/domains/Application.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/domains/Application.java @@ -489,6 +489,9 @@ public class Application extends BaseDomain implements Artifact { dotted(gitApplicationMetadata, GitArtifactMetadata.Fields.gitAuth); public static final String gitApplicationMetadata_defaultApplicationId = dotted(gitApplicationMetadata, GitArtifactMetadata.Fields.defaultApplicationId); + + public static final String gitApplicationMetadata_defaultArtifactId = + dotted(gitApplicationMetadata, GitArtifactMetadata.Fields.defaultArtifactId); public static final String gitApplicationMetadata_isAutoDeploymentEnabled = dotted(gitApplicationMetadata, GitArtifactMetadata.Fields.isAutoDeploymentEnabled); public static final String gitApplicationMetadata_branchName = diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/domains/Context.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/domains/Context.java index 6186ed5777..6ae04332d0 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/domains/Context.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/domains/Context.java @@ -3,4 +3,8 @@ package com.appsmith.server.domains; public interface Context { String getId(); + + String getArtifactId(); + + Layout getLayout(); } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/domains/GitArtifactMetadata.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/domains/GitArtifactMetadata.java index 9fe35262e5..1e20e973b6 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/domains/GitArtifactMetadata.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/domains/GitArtifactMetadata.java @@ -107,8 +107,18 @@ public class GitArtifactMetadata implements AppsmithDomain { @JsonView(Views.Public.class) public String getDefaultArtifactId() { - if (StringUtils.hasText(defaultApplicationId)) { - return defaultApplicationId; - } else return defaultArtifactId; + if (StringUtils.hasText(defaultArtifactId)) { + return defaultArtifactId; + } else return defaultApplicationId; + } + + // TODO : Set to private to prevent direct access unless migration is performed + private void setDefaultArtifactId(String defaultArtifactId) { + this.defaultArtifactId = defaultArtifactId; + } + + public void setDefaultApplicationId(String defaultApplicationId) { + this.defaultApplicationId = defaultApplicationId; + this.defaultArtifactId = defaultApplicationId; } } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/domains/NewPage.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/domains/NewPage.java index 856b38498d..5e890ae6e2 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/domains/NewPage.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/domains/NewPage.java @@ -11,6 +11,8 @@ import lombok.Setter; import lombok.experimental.FieldNameConstants; import org.springframework.data.mongodb.core.mapping.Document; +import java.util.List; + @Getter @Setter @NoArgsConstructor @@ -39,6 +41,19 @@ public class NewPage extends BranchAwareDomain implements Context { super.sanitiseToExportDBObject(); } + @JsonView(Views.Internal.class) + @Override + public String getArtifactId() { + return this.applicationId; + } + + @JsonView(Views.Internal.class) + @Override + public Layout getLayout() { + List layouts = this.getUnpublishedPage().getLayouts(); + return (layouts != null && !layouts.isEmpty()) ? layouts.get(0) : null; + } + public static class Fields extends BranchAwareDomain.Fields { public static String unpublishedPage_layouts = unpublishedPage + "." + PageDTO.Fields.layouts; public static String unpublishedPage_name = unpublishedPage + "." + PageDTO.Fields.name; diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/exceptions/GlobalExceptionHandler.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/exceptions/GlobalExceptionHandler.java index 53ff336634..74b82ffd6e 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/exceptions/GlobalExceptionHandler.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/exceptions/GlobalExceptionHandler.java @@ -9,7 +9,7 @@ import com.appsmith.external.exceptions.pluginExceptions.AppsmithPluginException import com.appsmith.server.constants.FieldName; import com.appsmith.server.dtos.ResponseDTO; import com.appsmith.server.exceptions.util.DuplicateKeyExceptionUtils; -import com.appsmith.server.helpers.GitFileUtils; +import com.appsmith.server.helpers.CommonGitFileUtils; import com.appsmith.server.helpers.RedisUtils; import com.appsmith.server.services.AnalyticsService; import com.appsmith.server.services.SessionUserService; @@ -54,7 +54,7 @@ public class GlobalExceptionHandler { private final AnalyticsService analyticsService; - private final GitFileUtils fileUtils; + private final CommonGitFileUtils commonGitFileUtils; private final SessionUserService sessionUserService; @@ -351,7 +351,7 @@ public class GlobalExceptionHandler { } private Mono deleteLockFileAndSendAnalytics(File file, String urlPath) { - return fileUtils.deleteIndexLockFile(Path.of(file.getPath())).flatMap(fileTime -> { + return commonGitFileUtils.deleteIndexLockFile(Path.of(file.getPath())).flatMap(fileTime -> { Map analyticsProps = new HashMap<>(); if (urlPath.contains("/git") && urlPath.contains("/app")) { String appId = getAppIdFromUrlPath(urlPath); diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/git/AutoCommitEventHandlerCEImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/git/AutoCommitEventHandlerCEImpl.java index f51bd5dac2..96f8b5c3cc 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/git/AutoCommitEventHandlerCEImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/git/AutoCommitEventHandlerCEImpl.java @@ -16,7 +16,6 @@ import com.appsmith.server.exceptions.AppsmithException; import com.appsmith.server.helpers.CollectionUtils; import com.appsmith.server.helpers.CommonGitFileUtils; import com.appsmith.server.helpers.DSLMigrationUtils; -import com.appsmith.server.helpers.GitFileUtils; import com.appsmith.server.helpers.GitUtils; import com.appsmith.server.helpers.RedisUtils; import com.appsmith.server.services.AnalyticsService; @@ -47,7 +46,6 @@ public class AutoCommitEventHandlerCEImpl implements AutoCommitEventHandlerCE { private final GitRedisUtils gitRedisUtils; private final RedisUtils redisUtils; private final DSLMigrationUtils dslMigrationUtils; - private final GitFileUtils fileUtils; private final CommonGitFileUtils commonGitFileUtils; private final GitExecutor gitExecutor; private final ProjectProperties projectProperties; @@ -106,7 +104,7 @@ public class AutoCommitEventHandlerCEImpl implements AutoCommitEventHandlerCE { ApplicationJson applicationJson, AutoCommitEvent autoCommitEvent) { // all the migrations are done, write to file system try { - return fileUtils.saveApplicationToLocalRepo( + return commonGitFileUtils.saveArtifactToLocalRepo( autoCommitEvent.getWorkspaceId(), autoCommitEvent.getApplicationId(), autoCommitEvent.getRepoName(), diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/git/AutoCommitEventHandlerImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/git/AutoCommitEventHandlerImpl.java index ee875d550e..45f804c4f5 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/git/AutoCommitEventHandlerImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/git/AutoCommitEventHandlerImpl.java @@ -4,7 +4,6 @@ import com.appsmith.external.git.GitExecutor; import com.appsmith.server.configurations.ProjectProperties; import com.appsmith.server.helpers.CommonGitFileUtils; import com.appsmith.server.helpers.DSLMigrationUtils; -import com.appsmith.server.helpers.GitFileUtils; import com.appsmith.server.helpers.RedisUtils; import com.appsmith.server.services.AnalyticsService; import org.springframework.context.ApplicationEventPublisher; @@ -18,7 +17,6 @@ public class AutoCommitEventHandlerImpl extends AutoCommitEventHandlerCEImpl imp GitRedisUtils gitRedisUtils, RedisUtils redisUtils, DSLMigrationUtils dslMigrationUtils, - GitFileUtils fileUtils, CommonGitFileUtils commonGitFileUtils, GitExecutor gitExecutor, ProjectProperties projectProperties, @@ -28,7 +26,6 @@ public class AutoCommitEventHandlerImpl extends AutoCommitEventHandlerCEImpl imp gitRedisUtils, redisUtils, dslMigrationUtils, - fileUtils, commonGitFileUtils, gitExecutor, projectProperties, diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/git/autocommit/helpers/GitAutoCommitHelperImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/git/autocommit/helpers/GitAutoCommitHelperImpl.java index ecedb20d28..e4897cb0e7 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/git/autocommit/helpers/GitAutoCommitHelperImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/git/autocommit/helpers/GitAutoCommitHelperImpl.java @@ -10,10 +10,10 @@ import com.appsmith.server.dtos.AutoCommitProgressDTO; import com.appsmith.server.dtos.AutoCommitTriggerDTO; import com.appsmith.server.events.AutoCommitEvent; import com.appsmith.server.git.AutoCommitEventHandler; +import com.appsmith.server.git.common.CommonGitService; import com.appsmith.server.helpers.GitPrivateRepoHelper; import com.appsmith.server.helpers.GitUtils; import com.appsmith.server.helpers.RedisUtils; -import com.appsmith.server.services.CommonGitService; import com.appsmith.server.services.UserDataService; import com.appsmith.server.solutions.ApplicationPermission; import lombok.extern.slf4j.Slf4j; diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/git/common/CommonGitService.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/git/common/CommonGitService.java new file mode 100644 index 0000000000..ab226544f1 --- /dev/null +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/git/common/CommonGitService.java @@ -0,0 +1,3 @@ +package com.appsmith.server.git.common; + +public interface CommonGitService extends CommonGitServiceCECompatible {} diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/CommonGitServiceCE.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/git/common/CommonGitServiceCE.java similarity index 95% rename from app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/CommonGitServiceCE.java rename to app/server/appsmith-server/src/main/java/com/appsmith/server/git/common/CommonGitServiceCE.java index c7cca9e12a..aeaae728bc 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/CommonGitServiceCE.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/git/common/CommonGitServiceCE.java @@ -1,4 +1,4 @@ -package com.appsmith.server.services.ce; +package com.appsmith.server.git.common; import com.appsmith.external.dtos.GitBranchDTO; import com.appsmith.external.dtos.GitLogDTO; @@ -9,6 +9,7 @@ import com.appsmith.server.domains.Artifact; import com.appsmith.server.domains.GitArtifactMetadata; import com.appsmith.server.domains.GitAuth; import com.appsmith.server.domains.GitProfile; +import com.appsmith.server.dtos.ArtifactImportDTO; import com.appsmith.server.dtos.AutoCommitProgressDTO; import com.appsmith.server.dtos.GitCommitDTO; import com.appsmith.server.dtos.GitConnectDTO; @@ -107,4 +108,7 @@ public interface CommonGitServiceCE { Mono getAutoCommitProgress(String applicationId, ArtifactType artifactType); Mono autoCommitApplication(String defaultApplicationId, String branchName, ArtifactType artifactType); + + Mono importArtifactFromGit( + String workspaceId, GitConnectDTO gitConnectDTO, ArtifactType artifactType); } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/git/common/CommonGitServiceCECompatible.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/git/common/CommonGitServiceCECompatible.java new file mode 100644 index 0000000000..98a995db77 --- /dev/null +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/git/common/CommonGitServiceCECompatible.java @@ -0,0 +1,3 @@ +package com.appsmith.server.git.common; + +public interface CommonGitServiceCECompatible extends CommonGitServiceCE {} diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce_compatible/CommonGitServiceCECompatibleImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/git/common/CommonGitServiceCECompatibleImpl.java similarity index 81% rename from app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce_compatible/CommonGitServiceCECompatibleImpl.java rename to app/server/appsmith-server/src/main/java/com/appsmith/server/git/common/CommonGitServiceCECompatibleImpl.java index 5bed14486f..e4ad5978dd 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce_compatible/CommonGitServiceCECompatibleImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/git/common/CommonGitServiceCECompatibleImpl.java @@ -1,7 +1,8 @@ -package com.appsmith.server.services.ce_compatible; +package com.appsmith.server.git.common; import com.appsmith.external.git.GitExecutor; import com.appsmith.server.configurations.EmailConfig; +import com.appsmith.server.datasources.base.DatasourceService; import com.appsmith.server.domains.Application; import com.appsmith.server.exports.internal.ExportService; import com.appsmith.server.git.GitRedisUtils; @@ -9,13 +10,15 @@ import com.appsmith.server.git.autocommit.helpers.GitAutoCommitHelper; import com.appsmith.server.helpers.CommonGitFileUtils; import com.appsmith.server.helpers.GitPrivateRepoHelper; import com.appsmith.server.imports.internal.ImportService; +import com.appsmith.server.plugins.base.PluginService; import com.appsmith.server.repositories.GitDeployKeysRepository; import com.appsmith.server.services.AnalyticsService; import com.appsmith.server.services.GitArtifactHelper; import com.appsmith.server.services.SessionUserService; import com.appsmith.server.services.UserDataService; import com.appsmith.server.services.UserService; -import com.appsmith.server.services.ce.CommonGitServiceCEImpl; +import com.appsmith.server.services.WorkspaceService; +import com.appsmith.server.solutions.DatasourcePermission; import io.micrometer.observation.ObservationRegistry; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -37,6 +40,10 @@ public class CommonGitServiceCECompatibleImpl extends CommonGitServiceCEImpl imp TransactionalOperator transactionalOperator, AnalyticsService analyticsService, ObservationRegistry observationRegistry, + WorkspaceService workspaceService, + DatasourceService datasourceService, + DatasourcePermission datasourcePermission, + PluginService pluginService, ExportService exportService, ImportService importService, GitExecutor gitExecutor, @@ -54,6 +61,10 @@ public class CommonGitServiceCECompatibleImpl extends CommonGitServiceCEImpl imp transactionalOperator, analyticsService, observationRegistry, + workspaceService, + datasourceService, + datasourcePermission, + pluginService, exportService, importService, gitExecutor, diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/CommonGitServiceCEImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/git/common/CommonGitServiceCEImpl.java similarity index 91% rename from app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/CommonGitServiceCEImpl.java rename to app/server/appsmith-server/src/main/java/com/appsmith/server/git/common/CommonGitServiceCEImpl.java index 64fadbe832..9fe9ba316c 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/CommonGitServiceCEImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/git/common/CommonGitServiceCEImpl.java @@ -1,4 +1,4 @@ -package com.appsmith.server.services.ce; +package com.appsmith.server.git.common; import com.appsmith.external.constants.AnalyticsEvents; import com.appsmith.external.constants.ErrorReferenceDocUrl; @@ -10,12 +10,15 @@ import com.appsmith.external.git.GitExecutor; import com.appsmith.external.git.constants.GitConstants; import com.appsmith.external.git.constants.GitConstants.GitCommandConstants; import com.appsmith.external.git.constants.GitSpan; +import com.appsmith.external.models.Datasource; +import com.appsmith.external.models.DatasourceStorage; import com.appsmith.server.acl.AclPermission; import com.appsmith.server.configurations.EmailConfig; import com.appsmith.server.constants.ArtifactType; import com.appsmith.server.constants.Assets; import com.appsmith.server.constants.FieldName; import com.appsmith.server.constants.GitDefaultCommitMessage; +import com.appsmith.server.datasources.base.DatasourceService; import com.appsmith.server.domains.Application; import com.appsmith.server.domains.ApplicationMode; import com.appsmith.server.domains.Artifact; @@ -24,9 +27,13 @@ import com.appsmith.server.domains.GitArtifactMetadata; import com.appsmith.server.domains.GitAuth; import com.appsmith.server.domains.GitDeployKeys; import com.appsmith.server.domains.GitProfile; +import com.appsmith.server.domains.Plugin; import com.appsmith.server.domains.User; import com.appsmith.server.domains.UserData; +import com.appsmith.server.domains.Workspace; +import com.appsmith.server.dtos.ApplicationImportDTO; import com.appsmith.server.dtos.ArtifactExchangeJson; +import com.appsmith.server.dtos.ArtifactImportDTO; import com.appsmith.server.dtos.AutoCommitProgressDTO; import com.appsmith.server.dtos.GitCommitDTO; import com.appsmith.server.dtos.GitConnectDTO; @@ -44,12 +51,15 @@ import com.appsmith.server.helpers.GitDeployKeyGenerator; import com.appsmith.server.helpers.GitPrivateRepoHelper; import com.appsmith.server.helpers.GitUtils; import com.appsmith.server.imports.internal.ImportService; +import com.appsmith.server.plugins.base.PluginService; import com.appsmith.server.repositories.GitDeployKeysRepository; import com.appsmith.server.services.AnalyticsService; import com.appsmith.server.services.GitArtifactHelper; import com.appsmith.server.services.SessionUserService; import com.appsmith.server.services.UserDataService; import com.appsmith.server.services.UserService; +import com.appsmith.server.services.WorkspaceService; +import com.appsmith.server.solutions.DatasourcePermission; import io.micrometer.observation.ObservationRegistry; import lombok.NonNull; import lombok.RequiredArgsConstructor; @@ -95,6 +105,7 @@ import static com.appsmith.external.git.constants.ce.GitConstantsCE.MERGE_CONFLI import static com.appsmith.external.git.constants.ce.GitSpanCE.OPS_COMMIT; import static com.appsmith.external.git.constants.ce.GitSpanCE.OPS_STATUS; import static com.appsmith.git.constants.AppsmithBotAsset.APPSMITH_BOT_USERNAME; +import static com.appsmith.server.constants.ArtifactType.APPLICATION; import static com.appsmith.server.constants.SerialiseArtifactObjective.VERSION_CONTROL; import static com.appsmith.server.constants.ce.FieldNameCE.DEFAULT; import static java.lang.Boolean.FALSE; @@ -120,6 +131,11 @@ public class CommonGitServiceCEImpl implements CommonGitServiceCE { protected final AnalyticsService analyticsService; private final ObservationRegistry observationRegistry; + private final WorkspaceService workspaceService; + private final DatasourceService datasourceService; + private final DatasourcePermission datasourcePermission; + private final PluginService pluginService; + private final ExportService exportService; private final ImportService importService; @@ -899,7 +915,12 @@ public class CommonGitServiceCEImpl implements CommonGitServiceCE { .switchIfEmpty( Mono.error(new AppsmithException(AppsmithError.INVALID_GIT_CONFIGURATION, GIT_PROFILE_ERROR))); - final String browserSupportedUrl = GitUtils.convertSshUrlToBrowserSupportedUrl(gitConnectDTO.getRemoteUrl()); + String browserSupportedUrl; + try { + browserSupportedUrl = GitUtils.convertSshUrlToBrowserSupportedUrl(gitConnectDTO.getRemoteUrl()); + } catch (AppsmithException error) { + return Mono.error(error); + } Mono isPrivateRepoMono = GitUtils.isRepoPrivate(browserSupportedUrl).cache(); @@ -1017,7 +1038,7 @@ public class CommonGitServiceCEImpl implements CommonGitServiceCE { new AppsmithException(AppsmithError.INVALID_GIT_REPO))); } else { GitArtifactMetadata gitArtifactMetadata = artifact.getGitArtifactMetadata(); - gitArtifactMetadata.setDefaultArtifactId(artifactId); + gitArtifactMetadata.setDefaultApplicationId(artifactId); gitArtifactMetadata.setBranchName(defaultBranch); gitArtifactMetadata.setDefaultBranchName(defaultBranch); gitArtifactMetadata.setRemoteUrl(gitConnectDTO.getRemoteUrl()); @@ -1167,7 +1188,7 @@ public class CommonGitServiceCEImpl implements CommonGitServiceCE { /** * @param commitDTO information required for making a commit - * @param defaultArtifactId application branch on which the commit needs to be done + * @param defaultArtifactId application branch on which the commit needs to be done * @param branchName branch name for the commit flow * @param doAmend if we want to amend the commit with the earlier one, used in connect flow * @param isFileLock boolean value indicates whether the file lock is needed to complete the operation @@ -1233,7 +1254,6 @@ public class CommonGitServiceCEImpl implements CommonGitServiceCE { // Check if the repo is public for current artifact and if the user have changed the access after // the connection - final String workspaceId = defaultArtifact.getWorkspaceId(); return GitUtils.isRepoPrivate(defaultGitMetadata.getBrowserSupportedRemoteUrl()) .flatMap(isPrivate -> { // Check the repo limit if the visibility status is updated, or it is private @@ -1252,9 +1272,9 @@ public class CommonGitServiceCEImpl implements CommonGitServiceCE { gitArtifactHelper.isPrivateRepoLimitReached(savedArtifact, false)); }); }) - .then(gitArtifactHelper.getArtifactByDefaultIdAndBranchName( + .flatMap(artifact -> gitArtifactHelper.getArtifactByDefaultIdAndBranchName( defaultArtifactId, branchName, artifactEditPermission)) - .flatMap((branchedArtifact) -> { + .flatMap(branchedArtifact -> { GitArtifactMetadata gitArtifactMetadata = branchedArtifact.getGitArtifactMetadata(); if (gitArtifactMetadata == null) { return Mono.error( @@ -1286,14 +1306,8 @@ public class CommonGitServiceCEImpl implements CommonGitServiceCE { GitArtifactMetadata gitData = branchedArtifact.getGitArtifactMetadata(); Path baseRepoSuffix = gitArtifactHelper.getRepoSuffixPath( branchedArtifact.getWorkspaceId(), gitData.getDefaultArtifactId(), gitData.getRepoName()); - Mono repoPathMono; - - try { - repoPathMono = commonGitFileUtils.saveArtifactToLocalRepoWithAnalytics( - baseRepoSuffix, artifactExchangeJson, gitData.getBranchName()); - } catch (IOException | GitAPIException e) { - return Mono.error(e); - } + Mono repoPathMono = commonGitFileUtils.saveArtifactToLocalRepoWithAnalytics( + baseRepoSuffix, artifactExchangeJson, gitData.getBranchName()); gitData.setLastCommittedAt(Instant.now()); // We don't require to check for permission from this point because, permission is already @@ -1355,7 +1369,7 @@ public class CommonGitServiceCEImpl implements CommonGitServiceCE { .then(Mono.error(new AppsmithException( AppsmithError.GIT_ACTION_FAILED, "commit", error.getMessage()))); }); - return Mono.zip(gitCommitMono, Mono.just(branchedArtifact)); + return Mono.zip(gitCommitMono, gitArtifactHelper.getArtifactById(branchedArtifact.getId(), null)); }) .flatMap(tuple -> { String commitStatus = tuple.getT1(); @@ -1499,14 +1513,14 @@ public class CommonGitServiceCEImpl implements CommonGitServiceCE { .checkoutToBranch( baseRepoSuffix, artifact.getGitArtifactMetadata().getBranchName()) - .then(gitExecutor + .then(Mono.defer(() -> gitExecutor .pushApplication( baseRepoSuffix, gitData.getRemoteUrl(), gitAuth.getPublicKey(), gitAuth.getPrivateKey(), gitData.getBranchName()) - .zipWith(Mono.just(artifact))) + .zipWith(Mono.just(artifact)))) .onErrorResume(error -> addAnalyticsForGitOperation( AnalyticsEvents.GIT_PUSH, artifact, @@ -1559,7 +1573,7 @@ public class CommonGitServiceCEImpl implements CommonGitServiceCE { } GitArtifactHelper gitArtifactHelper = getArtifactGitService(artifact.getArtifactType()); - return gitArtifactHelper.publishArtifact(artifact); + return gitArtifactHelper.publishArtifact(artifact, true); } /** @@ -1612,8 +1626,8 @@ public class CommonGitServiceCEImpl implements CommonGitServiceCE { GitArtifactHelper gitArtifactHelper = getArtifactGitService(artifactType); AclPermission artifactEditPermission = gitArtifactHelper.getArtifactEditPermission(); - Mono sourceArtifactMono = gitArtifactHelper.getArtifactByDefaultIdAndBranchName( - defaultArtifactId, branchName, artifactEditPermission); + Mono sourceArtifactMono = + gitArtifactHelper.getArtifactById(defaultArtifactId, artifactEditPermission); return sourceArtifactMono.flatMap(sourceArtifact -> checkoutBranch(sourceArtifact, branchName, addFileLock)); } @@ -1689,11 +1703,8 @@ public class CommonGitServiceCEImpl implements CommonGitServiceCE { Mono defaultArtifactMono = gitArtifactHelper.getArtifactById(defaultArtifactId, artifactEditPermission); - Mono checkoutRemoteBranchMono = addFileLock( - defaultArtifactId, GitCommandConstants.CHECKOUT_BRANCH) - .zipWith(defaultArtifactMono) - .flatMap(tuple2 -> { - Artifact artifact = tuple2.getT2(); + Mono checkoutRemoteBranchMono = defaultArtifactMono + .flatMap(artifact -> { GitArtifactMetadata gitArtifactMetadata = artifact.getGitArtifactMetadata(); String repoName = gitArtifactMetadata.getRepoName(); @@ -1776,9 +1787,7 @@ public class CommonGitServiceCEImpl implements CommonGitServiceCE { artifact1, Boolean.TRUE.equals( artifact1.getGitArtifactMetadata().getIsRepoPrivate()))) - .map(gitArtifactHelper::updateArtifactWithDefaultReponseUtils) - .flatMap(artifact1 -> - releaseFileLock(defaultArtifactId).then(Mono.just(artifact1))); + .map(gitArtifactHelper::updateArtifactWithDefaultReponseUtils); }) .tag(GitConstants.GitMetricConstants.CHECKOUT_REMOTE, TRUE.toString()) .name(GitSpan.OPS_CHECKOUT_BRANCH) @@ -1978,8 +1987,7 @@ public class CommonGitServiceCEImpl implements CommonGitServiceCE { branchDTO.getBranchName()) .flatMap(newBranchArtifact -> { // Commit and push for new branch created this is to avoid issues when user tries to - // create a - // new branch from uncommitted branch + // create a new branch from uncommitted branch GitArtifactMetadata gitData = newBranchArtifact.getGitArtifactMetadata(); GitCommitDTO commitDTO = new GitCommitDTO(); commitDTO.setCommitMessage(DEFAULT_COMMIT_MESSAGE @@ -2957,16 +2965,11 @@ public class CommonGitServiceCEImpl implements CommonGitServiceCE { branchedGitArtifactMetadata.getDefaultArtifactId(), branchedGitArtifactMetadata.getRepoName()); - try { - return Mono.zip( - commonGitFileUtils.saveArtifactToLocalRepoWithAnalytics( - repoSuffix, artifactExchangeJson, branchName), - Mono.just(branchedGitArtifactMetadata), - Mono.just(repoSuffix)); - } catch (IOException | GitAPIException e) { - return Mono.error( - new AppsmithException(AppsmithError.GIT_ACTION_FAILED, "checkout", e.getMessage())); - } + return Mono.zip( + commonGitFileUtils.saveArtifactToLocalRepoWithAnalytics( + repoSuffix, artifactExchangeJson, branchName), + Mono.just(branchedGitArtifactMetadata), + Mono.just(repoSuffix)); }) .flatMap(tuple -> { GitArtifactMetadata gitData = tuple.getT2(); @@ -3489,4 +3492,281 @@ public class CommonGitServiceCEImpl implements CommonGitServiceCE { return Flux.merge(eventSenderMonos).then(); } + + @Override + public Mono importArtifactFromGit( + String workspaceId, GitConnectDTO gitConnectDTO, ArtifactType artifactType) { + // 1. Check private repo limit for workspace + // 2. Create dummy application, clone repo from remote + // 3. Re-hydrate application to DB from local repo + // 1. Save the ssh keys in application object with other details + // 2. During import-export need to handle the DS(empty vs non-empty) + // 4. Return application + + GitArtifactHelper gitArtifactHelper = getArtifactGitService(artifactType); + + if (StringUtils.isEmptyOrNull(gitConnectDTO.getRemoteUrl())) { + return Mono.error(new AppsmithException(AppsmithError.INVALID_PARAMETER, "Remote Url")); + } + + if (StringUtils.isEmptyOrNull(workspaceId)) { + return Mono.error(new AppsmithException(AppsmithError.INVALID_PARAMETER, "Invalid workspace id")); + } + + Mono workspaceMono = workspaceService + .findById(workspaceId, AclPermission.WORKSPACE_CREATE_APPLICATION) + .switchIfEmpty(Mono.error( + new AppsmithException(AppsmithError.NO_RESOURCE_FOUND, FieldName.WORKSPACE, workspaceId))); + + final String repoName = GitUtils.getRepoName(gitConnectDTO.getRemoteUrl()); + Mono isPrivateRepoMono = GitUtils.isRepoPrivate( + GitUtils.convertSshUrlToBrowserSupportedUrl(gitConnectDTO.getRemoteUrl())) + .cache(); + Mono importedArtifactMono = workspaceMono + .then(getSSHKeyForCurrentUser()) + .zipWith(isPrivateRepoMono) + .switchIfEmpty( + Mono.error( + new AppsmithException( + AppsmithError.INVALID_GIT_CONFIGURATION, + "Unable to find git configuration for logged-in user. Please contact Appsmith team for support"))) + // Check the limit for number of private repo + .flatMap(tuple -> { + // Check if the repo is public + GitAuth gitAuth = tuple.getT1(); + boolean isRepoPrivate = tuple.getT2(); + + Mono createArtifactMono = gitArtifactHelper + .createArtifactForImport(workspaceId, repoName) + .cache(); + + if (!isRepoPrivate) { + return Mono.just(gitAuth).zipWith(createArtifactMono); + } + + return gitPrivateRepoHelper + .isRepoLimitReached(workspaceId, true) + .zipWith(createArtifactMono) + .flatMap(tuple2 -> { + Boolean isRepoLimitReached = tuple2.getT1(); + Artifact createdArtifact = tuple2.getT2(); + + if (FALSE.equals(isRepoLimitReached)) { + return Mono.just(gitAuth).zipWith(createArtifactMono); + } + + return addAnalyticsForGitOperation( + AnalyticsEvents.GIT_IMPORT, + createdArtifact, + AppsmithError.GIT_APPLICATION_LIMIT_ERROR.getErrorType(), + AppsmithError.GIT_APPLICATION_LIMIT_ERROR.getMessage(), + true) + .flatMap(user -> Mono.error( + new AppsmithException(AppsmithError.GIT_APPLICATION_LIMIT_ERROR))); + }); + }) + .flatMap(tuple -> { + GitAuth gitAuth = tuple.getT1(); + Artifact artifact = tuple.getT2(); + Path repoSuffix = + gitArtifactHelper.getRepoSuffixPath(artifact.getWorkspaceId(), artifact.getId(), repoName); + Mono> profileMono = + updateOrCreateGitProfileForCurrentUser(gitConnectDTO.getGitProfile(), artifact.getId()); + + Mono defaultBranchMono = gitExecutor + .cloneRemoteIntoArtifactRepo( + repoSuffix, + gitConnectDTO.getRemoteUrl(), + gitAuth.getPrivateKey(), + gitAuth.getPublicKey()) + .onErrorResume(error -> { + log.error("Error while cloning the remote repo, {}", error.getMessage()); + return addAnalyticsForGitOperation( + AnalyticsEvents.GIT_IMPORT, + artifact, + error.getClass().getName(), + error.getMessage(), + false) + .flatMap(user -> commonGitFileUtils + .deleteLocalRepo(repoSuffix) + .then(gitArtifactHelper.deleteArtifact(artifact.getId()))) + .flatMap(artifact1 -> { + if (error instanceof TransportException) { + return Mono.error(new AppsmithException( + AppsmithError.INVALID_GIT_SSH_CONFIGURATION)); + } else if (error instanceof InvalidRemoteException) { + return Mono.error(new AppsmithException( + AppsmithError.INVALID_PARAMETER, "remote url")); + } else if (error instanceof TimeoutException) { + return Mono.error( + new AppsmithException(AppsmithError.GIT_EXECUTION_TIMEOUT)); + } + return Mono.error(new AppsmithException( + AppsmithError.GIT_ACTION_FAILED, "clone", error)); + }); + }); + + return defaultBranchMono.zipWith(isPrivateRepoMono).flatMap(tuple2 -> { + String defaultBranch = tuple2.getT1(); + boolean isRepoPrivate = tuple2.getT2(); + GitArtifactMetadata gitArtifactMetadata = new GitArtifactMetadata(); + gitArtifactMetadata.setGitAuth(gitAuth); + gitArtifactMetadata.setDefaultApplicationId(artifact.getId()); + gitArtifactMetadata.setBranchName(defaultBranch); + gitArtifactMetadata.setDefaultBranchName(defaultBranch); + gitArtifactMetadata.setRemoteUrl(gitConnectDTO.getRemoteUrl()); + gitArtifactMetadata.setRepoName(repoName); + gitArtifactMetadata.setBrowserSupportedRemoteUrl( + GitUtils.convertSshUrlToBrowserSupportedUrl(gitConnectDTO.getRemoteUrl())); + gitArtifactMetadata.setIsRepoPrivate(isRepoPrivate); + gitArtifactMetadata.setLastCommittedAt(Instant.now()); + + artifact.setGitArtifactMetadata(gitArtifactMetadata); + return Mono.just(artifact).zipWith(profileMono); + }); + }) + .flatMap(objects -> { + Artifact artifact = objects.getT1(); + GitArtifactMetadata gitArtifactMetadata = artifact.getGitArtifactMetadata(); + String defaultBranch = gitArtifactMetadata.getDefaultBranchName(); + + Mono> datasourceMono = datasourceService + .getAllByWorkspaceIdWithStorages(workspaceId, datasourcePermission.getEditPermission()) + .collectList(); + Mono> pluginMono = + pluginService.getDefaultPlugins().collectList(); + Mono applicationJsonMono = commonGitFileUtils + .reconstructArtifactExchangeJsonFromGitRepoWithAnalytics( + workspaceId, + artifact.getId(), + gitArtifactMetadata.getRepoName(), + defaultBranch, + artifactType) + .onErrorResume(error -> { + log.error("Error while constructing artifact from git repo", error); + return deleteArtifactCreatedFromGitImport( + artifact.getId(), + artifact.getWorkspaceId(), + gitArtifactMetadata.getRepoName(), + artifactType) + .flatMap(application1 -> Mono.error(new AppsmithException( + AppsmithError.GIT_FILE_SYSTEM_ERROR, error.getMessage()))); + }); + + return Mono.zip(applicationJsonMono, datasourceMono, pluginMono) + .flatMap(data -> { + ArtifactExchangeJson artifactExchangeJson = data.getT1(); + List datasourceList = data.getT2(); + List pluginList = data.getT3(); + + if (Optional.ofNullable(artifactExchangeJson.getArtifact()) + .isEmpty() + || gitArtifactHelper.isContextInArtifactEmpty(artifactExchangeJson)) { + + return deleteArtifactCreatedFromGitImport( + artifact.getId(), + artifact.getWorkspaceId(), + gitArtifactMetadata.getRepoName(), + artifactType) + .then(Mono.error(new AppsmithException( + AppsmithError.GIT_ACTION_FAILED, + "import", + "Cannot import app from an empty repo"))); + } + + // If there is an existing datasource with the same name but a different type from that + // in the repo, the import api should fail + if (checkIsDatasourceNameConflict( + datasourceList, artifactExchangeJson.getDatasourceList(), pluginList)) { + return deleteArtifactCreatedFromGitImport( + artifact.getId(), + artifact.getWorkspaceId(), + gitArtifactMetadata.getRepoName(), + artifactType) + .then(Mono.error(new AppsmithException( + AppsmithError.GIT_ACTION_FAILED, + "import", + "Datasource already exists with the same name"))); + } + + artifactExchangeJson.getArtifact().setGitArtifactMetadata(gitArtifactMetadata); + return importService + .importArtifactInWorkspaceFromGit( + workspaceId, artifact.getId(), artifactExchangeJson, defaultBranch) + .onErrorResume(throwable -> deleteArtifactCreatedFromGitImport( + artifact.getId(), + artifact.getWorkspaceId(), + gitArtifactMetadata.getRepoName(), + artifactType) + .flatMap(application1 -> Mono.error(new AppsmithException( + AppsmithError.GIT_FILE_SYSTEM_ERROR, throwable.getMessage())))); + }); + }) + .flatMap(artifact -> gitArtifactHelper.publishArtifact(artifact, false)) + // Add un-configured datasource to the list to response + .flatMap(artifact -> importService.getArtifactImportDTO( + artifact.getWorkspaceId(), artifact.getId(), artifact, APPLICATION)) + .map(importableArtifactDTO -> (ApplicationImportDTO) importableArtifactDTO) + // Add analytics event + .flatMap(applicationImportDTO -> { + Application application = applicationImportDTO.getApplication(); + return addAnalyticsForGitOperation( + AnalyticsEvents.GIT_IMPORT, + application, + application.getGitApplicationMetadata().getIsRepoPrivate()) + .thenReturn(applicationImportDTO); + }); + + return Mono.create( + sink -> importedArtifactMono.subscribe(sink::success, sink::error, null, sink.currentContext())); + } + + private Mono getSSHKeyForCurrentUser() { + return sessionUserService + .getCurrentUser() + .flatMap(user -> gitDeployKeysRepository.findByEmail(user.getEmail())) + .map(GitDeployKeys::getGitAuth); + } + + private Mono deleteArtifactCreatedFromGitImport( + String artifactId, String workspaceId, String repoName, ArtifactType artifactType) { + + GitArtifactHelper gitArtifactHelper = getArtifactGitService(artifactType); + + Path repoSuffix = Paths.get(workspaceId, artifactId, repoName); + return commonGitFileUtils.deleteLocalRepo(repoSuffix).then(gitArtifactHelper.deleteArtifact(artifactId)); + } + + private boolean checkIsDatasourceNameConflict( + List existingDatasources, + List importedDatasources, + List pluginList) { + // If we have an existing datasource with the same name but a different type from that in the repo, the import + // api should fail + for (DatasourceStorage datasourceStorage : importedDatasources) { + // Collect the datasource(existing in workspace) which has same as of imported datasource + // As names are unique we will need filter first element to check if the plugin id is matched + Datasource filteredDatasource = existingDatasources.stream() + .filter(datasource1 -> datasource1.getName().equals(datasourceStorage.getName())) + .findFirst() + .orElse(null); + + // Check if both of the datasource's are of the same plugin type + if (filteredDatasource != null) { + long matchCount = pluginList.stream() + .filter(plugin -> { + final String pluginReference = + plugin.getPluginName() == null ? plugin.getPackageName() : plugin.getPluginName(); + + return plugin.getId().equals(filteredDatasource.getPluginId()) + && !datasourceStorage.getPluginId().equals(pluginReference); + }) + .count(); + if (matchCount > 0) { + return true; + } + } + } + return false; + } } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/CommonGitServiceImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/git/common/CommonGitServiceImpl.java similarity index 73% rename from app/server/appsmith-server/src/main/java/com/appsmith/server/services/CommonGitServiceImpl.java rename to app/server/appsmith-server/src/main/java/com/appsmith/server/git/common/CommonGitServiceImpl.java index 4128ee9389..c5b0eee3f3 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/CommonGitServiceImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/git/common/CommonGitServiceImpl.java @@ -1,8 +1,9 @@ -package com.appsmith.server.services; +package com.appsmith.server.git.common; import com.appsmith.external.git.GitExecutor; import com.appsmith.git.service.GitExecutorImpl; import com.appsmith.server.configurations.EmailConfig; +import com.appsmith.server.datasources.base.DatasourceService; import com.appsmith.server.domains.Application; import com.appsmith.server.exports.internal.ExportService; import com.appsmith.server.git.GitRedisUtils; @@ -10,8 +11,15 @@ import com.appsmith.server.git.autocommit.helpers.GitAutoCommitHelper; import com.appsmith.server.helpers.CommonGitFileUtils; import com.appsmith.server.helpers.GitPrivateRepoHelper; import com.appsmith.server.imports.internal.ImportService; +import com.appsmith.server.plugins.base.PluginService; import com.appsmith.server.repositories.GitDeployKeysRepository; -import com.appsmith.server.services.ce_compatible.CommonGitServiceCECompatibleImpl; +import com.appsmith.server.services.AnalyticsService; +import com.appsmith.server.services.GitArtifactHelper; +import com.appsmith.server.services.SessionUserService; +import com.appsmith.server.services.UserDataService; +import com.appsmith.server.services.UserService; +import com.appsmith.server.services.WorkspaceService; +import com.appsmith.server.solutions.DatasourcePermission; import io.micrometer.observation.ObservationRegistry; import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Import; @@ -35,6 +43,10 @@ public class CommonGitServiceImpl extends CommonGitServiceCECompatibleImpl imple TransactionalOperator transactionalOperator, AnalyticsService analyticsService, ObservationRegistry observationRegistry, + WorkspaceService workspaceService, + DatasourceService datasourceService, + DatasourcePermission datasourcePermission, + PluginService pluginService, ExportService exportService, ImportService importService, GitExecutor gitExecutor, @@ -52,6 +64,10 @@ public class CommonGitServiceImpl extends CommonGitServiceCECompatibleImpl imple transactionalOperator, analyticsService, observationRegistry, + workspaceService, + datasourceService, + datasourcePermission, + pluginService, exportService, importService, gitExecutor, diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/CommonGitFileUtils.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/CommonGitFileUtils.java index d9710bd520..1a3cfb78e6 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/CommonGitFileUtils.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/CommonGitFileUtils.java @@ -6,7 +6,6 @@ import com.appsmith.server.applications.git.ApplicationGitFileUtilsImpl; import com.appsmith.server.helpers.ce.CommonGitFileUtilsCE; import com.appsmith.server.services.AnalyticsService; import com.appsmith.server.services.SessionUserService; -import com.google.gson.Gson; import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Import; import org.springframework.stereotype.Component; @@ -20,8 +19,7 @@ public class CommonGitFileUtils extends CommonGitFileUtilsCE { ApplicationGitFileUtilsImpl applicationGitFileUtils, FileInterface fileUtils, AnalyticsService analyticsService, - SessionUserService sessionUserService, - Gson gson) { - super(applicationGitFileUtils, fileUtils, analyticsService, sessionUserService, gson); + SessionUserService sessionUserService) { + super(applicationGitFileUtils, fileUtils, analyticsService, sessionUserService); } } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/GitFileUtils.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/GitFileUtils.java deleted file mode 100644 index c07f611559..0000000000 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/GitFileUtils.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.appsmith.server.helpers; - -import com.appsmith.external.git.FileInterface; -import com.appsmith.git.files.FileUtilsImpl; -import com.appsmith.server.actioncollections.base.ActionCollectionService; -import com.appsmith.server.helpers.ce.GitFileUtilsCE; -import com.appsmith.server.newactions.base.NewActionService; -import com.appsmith.server.services.AnalyticsService; -import com.appsmith.server.services.SessionUserService; -import com.google.gson.Gson; -import lombok.extern.slf4j.Slf4j; -import org.springframework.context.annotation.Import; -import org.springframework.stereotype.Component; - -@Slf4j -@Component -@Import({FileUtilsImpl.class}) -public class GitFileUtils extends GitFileUtilsCE { - - public GitFileUtils( - FileInterface fileUtils, - AnalyticsService analyticsService, - SessionUserService sessionUserService, - NewActionService newActionService, - ActionCollectionService actionCollectionService, - Gson gson) { - super(fileUtils, analyticsService, sessionUserService, newActionService, actionCollectionService, gson); - } -} diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/GitUtils.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/GitUtils.java index 644ad0cf6f..fd7ecc01f9 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/GitUtils.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/GitUtils.java @@ -161,7 +161,7 @@ public class GitUtils { public static boolean isApplicationConnectedToGit(Application application) { GitArtifactMetadata metadata = application.getGitApplicationMetadata(); return metadata != null - && !StringUtils.isEmptyOrNull(metadata.getDefaultApplicationId()) + && !StringUtils.isEmptyOrNull(metadata.getDefaultArtifactId()) && !StringUtils.isEmptyOrNull(metadata.getRemoteUrl()); } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/ce/CommonGitFileUtilsCE.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/ce/CommonGitFileUtilsCE.java index d415590b04..819c225898 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/ce/CommonGitFileUtilsCE.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/ce/CommonGitFileUtilsCE.java @@ -52,7 +52,6 @@ public class CommonGitFileUtilsCE { private final FileInterface fileUtils; private final AnalyticsService analyticsService; private final SessionUserService sessionUserService; - private final Gson gson; // Number of seconds after lock file is stale @Value("${appsmith.index.lock.file.time}") @@ -93,8 +92,7 @@ public class CommonGitFileUtilsCE { } public Mono saveArtifactToLocalRepoWithAnalytics( - Path baseRepoSuffix, ArtifactExchangeJson artifactExchangeJson, String branchName) - throws IOException, GitAPIException { + Path baseRepoSuffix, ArtifactExchangeJson artifactExchangeJson, String branchName) { /* 1. Checkout to branch diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/ce/GitFileUtilsCE.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/ce/GitFileUtilsCE.java deleted file mode 100644 index b9b8f1ca72..0000000000 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/ce/GitFileUtilsCE.java +++ /dev/null @@ -1,743 +0,0 @@ -package com.appsmith.server.helpers.ce; - -import com.appsmith.external.constants.AnalyticsEvents; -import com.appsmith.external.git.FileInterface; -import com.appsmith.external.helpers.Stopwatch; -import com.appsmith.external.models.ActionDTO; -import com.appsmith.external.models.ApplicationGitReference; -import com.appsmith.external.models.BaseDomain; -import com.appsmith.external.models.DatasourceStorage; -import com.appsmith.external.models.PluginType; -import com.appsmith.git.files.FileUtilsImpl; -import com.appsmith.server.actioncollections.base.ActionCollectionService; -import com.appsmith.server.constants.FieldName; -import com.appsmith.server.domains.ActionCollection; -import com.appsmith.server.domains.Application; -import com.appsmith.server.domains.ApplicationPage; -import com.appsmith.server.domains.CustomJSLib; -import com.appsmith.server.domains.NewAction; -import com.appsmith.server.domains.NewPage; -import com.appsmith.server.domains.Theme; -import com.appsmith.server.dtos.ActionCollectionDTO; -import com.appsmith.server.dtos.ApplicationJson; -import com.appsmith.server.dtos.PageDTO; -import com.appsmith.server.exceptions.AppsmithError; -import com.appsmith.server.exceptions.AppsmithException; -import com.appsmith.server.helpers.CollectionUtils; -import com.appsmith.server.newactions.base.NewActionService; -import com.appsmith.server.services.AnalyticsService; -import com.appsmith.server.services.SessionUserService; -import com.google.gson.Gson; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import net.minidev.json.JSONObject; -import net.minidev.json.parser.JSONParser; -import net.minidev.json.parser.ParseException; -import org.apache.commons.collections.PredicateUtils; -import org.apache.commons.lang3.StringUtils; -import org.eclipse.jgit.api.errors.GitAPIException; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Import; -import org.springframework.stereotype.Component; -import reactor.core.Exceptions; -import reactor.core.publisher.Mono; - -import java.io.IOException; -import java.lang.reflect.Field; -import java.lang.reflect.Type; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import static com.appsmith.external.git.constants.GitConstants.NAME_SEPARATOR; -import static com.appsmith.external.helpers.AppsmithBeanUtils.copyNestedNonNullProperties; -import static com.appsmith.external.helpers.AppsmithBeanUtils.copyProperties; -import static com.appsmith.server.constants.FieldName.ACTION_COLLECTION_LIST; -import static com.appsmith.server.constants.FieldName.ACTION_LIST; -import static com.appsmith.server.constants.FieldName.CUSTOM_JS_LIB_LIST; -import static com.appsmith.server.constants.FieldName.DATASOURCE_LIST; -import static com.appsmith.server.constants.FieldName.DECRYPTED_FIELDS; -import static com.appsmith.server.constants.FieldName.EDIT_MODE_THEME; -import static com.appsmith.server.constants.FieldName.EXPORTED_APPLICATION; -import static com.appsmith.server.constants.FieldName.PAGE_LIST; - -@Slf4j -@RequiredArgsConstructor -@Component -@Import({FileUtilsImpl.class}) -public class GitFileUtilsCE { - - private final FileInterface fileUtils; - private final AnalyticsService analyticsService; - private final SessionUserService sessionUserService; - private final NewActionService newActionService; - private final ActionCollectionService actionCollectionService; - private final Gson gson; - - // Number of seconds after lock file is stale - @Value("${appsmith.index.lock.file.time}") - public final int INDEX_LOCK_FILE_STALE_TIME = 300; - - // Only include the application helper fields in metadata object - protected Set getBlockedMetadataFields() { - return Set.of( - EXPORTED_APPLICATION, - DATASOURCE_LIST, - PAGE_LIST, - ACTION_LIST, - ACTION_COLLECTION_LIST, - DECRYPTED_FIELDS, - EDIT_MODE_THEME, - CUSTOM_JS_LIB_LIST); - } - - /** - * This method will save the complete application in the local repo directory. - * Path to repo will be : ./container-volumes/git-repo/workspaceId/defaultApplicationId/repoName/{application_data} - * - * @param baseRepoSuffix path suffix used to create a local repo path - * @param applicationJson application reference object from which entire application can be rehydrated - * @param branchName name of the branch for the current application - * @return repo path where the application is stored - */ - public Mono saveApplicationToLocalRepo( - Path baseRepoSuffix, ApplicationJson applicationJson, String branchName) - throws IOException, GitAPIException { - /* - 1. Checkout to branch - 2. Create application reference for appsmith-git module - 3. Save application to git repo - */ - - ApplicationGitReference applicationReference = createApplicationReference(applicationJson); - // Save application to git repo - try { - return fileUtils.saveApplicationToGitRepo(baseRepoSuffix, applicationReference, branchName); - } catch (IOException | GitAPIException e) { - log.error("Error occurred while saving files to local git repo: ", e); - throw Exceptions.propagate(e); - } - } - - public Mono saveApplicationToLocalRepoWithAnalytics( - Path baseRepoSuffix, ApplicationJson applicationJson, String branchName) - throws IOException, GitAPIException { - - /* - 1. Checkout to branch - 2. Create application reference for appsmith-git module - 3. Save application to git repo - */ - Stopwatch stopwatch = new Stopwatch(AnalyticsEvents.GIT_SERIALIZE_APP_RESOURCES_TO_LOCAL_FILE.getEventName()); - // Save application to git repo - try { - Mono repoPathMono = saveApplicationToLocalRepo(baseRepoSuffix, applicationJson, branchName); - return Mono.zip(repoPathMono, sessionUserService.getCurrentUser()).flatMap(tuple -> { - stopwatch.stopTimer(); - Path repoPath = tuple.getT1(); - // Path to repo will be : ./container-volumes/git-repo/workspaceId/defaultApplicationId/repoName/ - final Map data = Map.of( - FieldName.APPLICATION_ID, - repoPath.getParent().getFileName().toString(), - FieldName.ORGANIZATION_ID, - repoPath.getParent().getParent().getFileName().toString(), - FieldName.FLOW_NAME, - stopwatch.getFlow(), - "executionTime", - stopwatch.getExecutionTime()); - return analyticsService - .sendEvent( - AnalyticsEvents.UNIT_EXECUTION_TIME.getEventName(), - tuple.getT2().getUsername(), - data) - .thenReturn(repoPath); - }); - } catch (IOException | GitAPIException e) { - log.error("Error occurred while saving files to local git repo: ", e); - throw Exceptions.propagate(e); - } - } - - public Mono saveApplicationToLocalRepo( - String workspaceId, - String defaultApplicationId, - String repoName, - ApplicationJson applicationJson, - String branchName) - throws GitAPIException, IOException { - Path baseRepoSuffix = Paths.get(workspaceId, defaultApplicationId, repoName); - - return saveApplicationToLocalRepo(baseRepoSuffix, applicationJson, branchName); - } - - /** - * Method to convert application resources to the structure which can be serialised by appsmith-git module for - * serialisation - * - * @param applicationJson application resource including actions, jsobjects, pages - * @return resource which can be saved to file system - */ - public ApplicationGitReference createApplicationReference(ApplicationJson applicationJson) { - ApplicationGitReference applicationReference = new ApplicationGitReference(); - applicationReference.setModifiedResources(applicationJson.getModifiedResources()); - - setApplicationInApplicationReference(applicationJson, applicationReference); - - setThemesInApplicationReference(applicationJson, applicationReference); - - setApplicationMetadataInApplicationReference(applicationJson, applicationReference); - - setNewPagesInApplicationReference(applicationJson, applicationReference); - - setNewActionsInApplicationReference(applicationJson, applicationReference); - - setActionCollectionsInApplicationReference(applicationJson, applicationReference); - - setDatasourcesInApplicationReference(applicationJson, applicationReference); - - setCustomJSLibsInApplicationReference(applicationJson, applicationReference); - - return applicationReference; - } - - private void setApplicationInApplicationReference( - ApplicationJson applicationJson, ApplicationGitReference applicationReference) { - Application application = applicationJson.getExportedApplication(); - removeUnwantedFieldsFromApplication(application); - // Pass application reference - applicationReference.setApplication(applicationJson.getExportedApplication()); - } - - private void setApplicationMetadataInApplicationReference( - ApplicationJson applicationJson, ApplicationGitReference applicationReference) { - // Pass metadata - Iterable keys = getAllFields(applicationJson) - .map(Field::getName) - .filter(name -> !getBlockedMetadataFields().contains(name)) - .collect(Collectors.toList()); - - ApplicationJson applicationMetadata = new ApplicationJson(); - applicationJson.setModifiedResources(null); - copyProperties(applicationJson, applicationMetadata, keys); - applicationReference.setMetadata(applicationMetadata); - } - - private void setThemesInApplicationReference( - ApplicationJson applicationJson, ApplicationGitReference applicationReference) { - // No need to commit publish mode theme as it leads to conflict resolution at both the places if any - applicationJson.setPublishedTheme(null); - - // Remove internal fields from the themes - removeUnwantedFieldsFromBaseDomain(applicationJson.getEditModeTheme()); - - applicationReference.setTheme(applicationJson.getEditModeTheme()); - } - - private void setCustomJSLibsInApplicationReference( - ApplicationJson applicationJson, ApplicationGitReference applicationReference) { - Map resourceMap = new HashMap<>(); - applicationJson.getCustomJSLibList().forEach(jsLib -> { - removeUnwantedFieldsFromBaseDomain(jsLib); - resourceMap.put(jsLib.getUidString(), jsLib); - }); - applicationReference.setJsLibraries(resourceMap); - } - - private void setDatasourcesInApplicationReference( - ApplicationJson applicationJson, ApplicationGitReference applicationReference) { - Map resourceMap = new HashMap<>(); - // Send datasources - applicationJson.getDatasourceList().forEach(datasource -> { - removeUnwantedFieldsFromDatasource(datasource); - resourceMap.put(datasource.getName(), datasource); - }); - applicationReference.setDatasources(resourceMap); - } - - private void setActionCollectionsInApplicationReference( - ApplicationJson applicationJson, ApplicationGitReference applicationReference) { - Map resourceMap = new HashMap<>(); - Map resourceMapActionCollectionBody = new HashMap<>(); - // Insert JSOObjects and also assign the keys which later will be used for saving the resource in actual - // filepath - // JSObjectName_pageName => nomenclature for the keys - applicationJson.getActionCollectionList().stream() - // As we are expecting the commit will happen only after the application is published, so we can safely - // assume if the unpublished version is deleted entity should not be committed to git - .filter(collection -> collection.getUnpublishedCollection() != null - && collection.getUnpublishedCollection().getDeletedAt() == null) - .peek(actionCollection -> - actionCollectionService.generateActionCollectionByViewMode(actionCollection, false)) - .forEach(actionCollection -> { - String prefix = actionCollection.getUnpublishedCollection().getUserExecutableName() - + NAME_SEPARATOR - + actionCollection.getUnpublishedCollection().getPageId(); - removeUnwantedFieldFromActionCollection(actionCollection); - - String body = actionCollection.getUnpublishedCollection().getBody() != null - ? actionCollection.getUnpublishedCollection().getBody() - : ""; - actionCollection.getUnpublishedCollection().setBody(null); - resourceMapActionCollectionBody.put(prefix, body); - resourceMap.put(prefix, actionCollection); - }); - applicationReference.setActionCollections(resourceMap); - applicationReference.setActionCollectionBody(resourceMapActionCollectionBody); - } - - private void setNewPagesInApplicationReference( - ApplicationJson applicationJson, ApplicationGitReference applicationReference) { - // Insert only active pages which will then be committed to repo as individual file - Map resourceMap = new HashMap<>(); - Map dslBody = new HashMap<>(); - applicationJson.getPageList().stream() - // As we are expecting the commit will happen only after the application is published, so we can safely - // assume if the unpublished version is deleted entity should not be committed to git - .filter(newPage -> newPage.getUnpublishedPage() != null - && newPage.getUnpublishedPage().getDeletedAt() == null) - .forEach(newPage -> { - String pageName = newPage.getUnpublishedPage() != null - ? newPage.getUnpublishedPage().getName() - : newPage.getPublishedPage().getName(); - removeUnwantedFieldsFromPage(newPage); - JSONObject dsl = - newPage.getUnpublishedPage().getLayouts().get(0).getDsl(); - // Get MainContainer widget data, remove the children and club with Canvas.json file - JSONObject mainContainer = new JSONObject(dsl); - mainContainer.remove("children"); - newPage.getUnpublishedPage().getLayouts().get(0).setDsl(mainContainer); - // pageName will be used for naming the json file - dslBody.put(pageName, dsl.toString()); - resourceMap.put(pageName, newPage); - }); - - applicationReference.setPages(resourceMap); - applicationReference.setPageDsl(dslBody); - } - - private void setNewActionsInApplicationReference( - ApplicationJson applicationJson, ApplicationGitReference applicationReference) { - Map resourceMap = new HashMap<>(); - Map resourceMapBody = new HashMap<>(); - // Insert active actions and also assign the keys which later will be used for saving the resource in actual - // filepath - // For actions, we are referring to validNames to maintain unique file names as just name - // field don't guarantee unique constraint for actions within JSObject - // queryValidName_pageName => nomenclature for the keys - applicationJson.getActionList().stream() - // As we are expecting the commit will happen only after the application is published, so we can safely - // assume if the unpublished version is deleted entity should not be committed to git - .filter(newAction -> newAction.getUnpublishedAction() != null - && newAction.getUnpublishedAction().getDeletedAt() == null) - .peek(newAction -> newActionService.generateActionByViewMode(newAction, false)) - .forEach(newAction -> { - String prefix; - if (newAction.getUnpublishedAction() != null) { - prefix = newAction.getUnpublishedAction().getUserExecutableName() - + NAME_SEPARATOR - + newAction.getUnpublishedAction().getPageId(); - } else { - prefix = newAction.getPublishedAction().getUserExecutableName() - + NAME_SEPARATOR - + newAction.getPublishedAction().getPageId(); - } - removeUnwantedFieldFromAction(newAction); - String body = newAction.getUnpublishedAction().getActionConfiguration() != null - && newAction - .getUnpublishedAction() - .getActionConfiguration() - .getBody() - != null - ? newAction - .getUnpublishedAction() - .getActionConfiguration() - .getBody() - : ""; - - // This is a special case where we are handling REMOTE type plugins based actions such as Twilio - // The user configured values are stored in a attribute called formData which is a map unlike the - // body - if (PluginType.REMOTE.equals(newAction.getPluginType()) - && newAction.getUnpublishedAction().getActionConfiguration() != null - && newAction - .getUnpublishedAction() - .getActionConfiguration() - .getFormData() - != null) { - body = new Gson() - .toJson( - newAction - .getUnpublishedAction() - .getActionConfiguration() - .getFormData(), - Map.class); - newAction - .getUnpublishedAction() - .getActionConfiguration() - .setFormData(null); - } - // This is a special case where we are handling JS actions as we don't want to commit the body of JS - // actions - if (PluginType.JS.equals(newAction.getPluginType())) { - if (newAction.getUnpublishedAction().getActionConfiguration() != null) { - newAction - .getUnpublishedAction() - .getActionConfiguration() - .setBody(null); - newAction.getUnpublishedAction().setJsonPathKeys(null); - } - } else { - // For the regular actions we save the body field to git repo - resourceMapBody.put(prefix, body); - } - resourceMap.put(prefix, newAction); - }); - applicationReference.setActions(resourceMap); - applicationReference.setActionBody(resourceMapBody); - } - - protected Stream getAllFields(ApplicationJson applicationJson) { - Class currentType = applicationJson.getClass(); - - Set> classes = new HashSet<>(); - - while (currentType != null) { - classes.add(currentType); - currentType = currentType.getSuperclass(); - } - - return classes.stream().flatMap(currentClass -> Arrays.stream(currentClass.getDeclaredFields())); - } - - /** - * Method to reconstruct the application from the local git repo - * - * @param workspaceId To which workspace application needs to be rehydrated - * @param defaultApplicationId Root application for the current branched application - * @param branchName for which branch the application needs to rehydrate - * @return application reference from which entire application can be rehydrated - */ - public Mono reconstructApplicationJsonFromGitRepoWithAnalytics( - String workspaceId, String defaultApplicationId, String repoName, String branchName) { - Stopwatch stopwatch = new Stopwatch(AnalyticsEvents.GIT_DESERIALIZE_APP_RESOURCES_FROM_FILE.getEventName()); - - return Mono.zip( - reconstructApplicationJsonFromGitRepo(workspaceId, defaultApplicationId, repoName, branchName), - sessionUserService.getCurrentUser()) - .flatMap(tuple -> { - stopwatch.stopTimer(); - final Map data = Map.of( - FieldName.APPLICATION_ID, - defaultApplicationId, - FieldName.ORGANIZATION_ID, - workspaceId, - FieldName.FLOW_NAME, - stopwatch.getFlow(), - "executionTime", - stopwatch.getExecutionTime()); - return analyticsService - .sendEvent( - AnalyticsEvents.UNIT_EXECUTION_TIME.getEventName(), - tuple.getT2().getUsername(), - data) - .thenReturn(tuple.getT1()); - }); - } - - public Mono reconstructApplicationJsonFromGitRepo( - String workspaceId, String defaultApplicationId, String repoName, String branchName) { - - Mono appReferenceMono = fileUtils.reconstructApplicationReferenceFromGitRepo( - workspaceId, defaultApplicationId, repoName, branchName); - return appReferenceMono.map(applicationReference -> { - // Extract application metadata from the json - ApplicationJson metadata = - getApplicationResource(applicationReference.getMetadata(), ApplicationJson.class); - ApplicationJson applicationJson = getApplicationJsonFromGitReference(applicationReference); - copyNestedNonNullProperties(metadata, applicationJson); - return applicationJson; - }); - } - - protected List getApplicationResource(Map resources, Type type) { - - List deserializedResources = new ArrayList<>(); - if (!CollectionUtils.isNullOrEmpty(resources)) { - for (Map.Entry resource : resources.entrySet()) { - deserializedResources.add(getApplicationResource(resource.getValue(), type)); - } - } - return deserializedResources; - } - - public T getApplicationResource(Object resource, Type type) { - if (resource == null) { - return null; - } - return gson.fromJson(gson.toJson(resource), type); - } - - /** - * Once the user connects the existing application to a remote repo, we will initialize the repo with Readme.md - - * Url to the deployed app(view and edit mode) - * Link to discord channel for support - * Link to appsmith documentation for Git related operations - * Welcome message - * - * @param baseRepoSuffix path suffix used to create a branch repo path as per worktree implementation - * @param viewModeUrl URL to deployed version of the application view only mode - * @param editModeUrl URL to deployed version of the application edit mode - * @return Path where the Application is stored - */ - public Mono initializeReadme(Path baseRepoSuffix, String viewModeUrl, String editModeUrl) throws IOException { - return fileUtils - .initializeReadme(baseRepoSuffix, viewModeUrl, editModeUrl) - .onErrorResume(e -> Mono.error(new AppsmithException(AppsmithError.GIT_FILE_SYSTEM_ERROR, e))); - } - - /** - * When the user clicks on detach remote, we need to remove the repo from the file system - * - * @param baseRepoSuffix path suffix used to create a branch repo path as per worktree implementation - * @return success on remove of file system - */ - public Mono deleteLocalRepo(Path baseRepoSuffix) { - return fileUtils.deleteLocalRepo(baseRepoSuffix); - } - - public Mono checkIfDirectoryIsEmpty(Path baseRepoSuffix) throws IOException { - return fileUtils - .checkIfDirectoryIsEmpty(baseRepoSuffix) - .onErrorResume(e -> Mono.error(new AppsmithException(AppsmithError.GIT_FILE_SYSTEM_ERROR, e))); - } - - protected void removeUnwantedFieldsFromBaseDomain(BaseDomain baseDomain) { - baseDomain.setPolicies(null); - baseDomain.setUserPermissions(null); - } - - private void removeUnwantedFieldsFromDatasource(DatasourceStorage datasource) { - datasource.setInvalids(null); - removeUnwantedFieldsFromBaseDomain(datasource); - } - - private void removeUnwantedFieldsFromPage(NewPage page) { - // As we are publishing the app and then committing to git we expect the published and unpublished PageDTO will - // be same, so we only commit unpublished PageDTO. - page.setPublishedPage(null); - removeUnwantedFieldsFromBaseDomain(page); - } - - private void removeUnwantedFieldsFromApplication(Application application) { - // Don't commit application name as while importing we are using the repoName as application name - application.setName(null); - application.setPublishedPages(null); - application.setIsPublic(null); - application.setSlug(null); - application.setPublishedApplicationDetail(null); - removeUnwantedFieldsFromBaseDomain(application); - // we can call the sanitiseToExportDBObject() from BaseDomain as well here - } - - private void removeUnwantedFieldFromAction(NewAction action) { - // As we are publishing the app and then committing to git we expect the published and unpublished ActionDTO - // will be same, so we only commit unpublished ActionDTO. - action.setPublishedAction(null); - action.getUnpublishedAction().sanitiseToExportDBObject(); - removeUnwantedFieldsFromBaseDomain(action); - } - - private void removeUnwantedFieldFromActionCollection(ActionCollection actionCollection) { - // As we are publishing the app and then committing to git we expect the published and unpublished - // ActionCollectionDTO will be same, so we only commit unpublished ActionCollectionDTO. - actionCollection.setPublishedCollection(null); - actionCollection.getUnpublishedCollection().sanitiseForExport(); - removeUnwantedFieldsFromBaseDomain(actionCollection); - } - - protected ApplicationJson getApplicationJsonFromGitReference(ApplicationGitReference applicationReference) { - ApplicationJson applicationJson = new ApplicationJson(); - - setApplicationInApplicationJson(applicationReference, applicationJson); - - setThemesInApplicationJson(applicationReference, applicationJson); - - setCustomJsLibsInApplicationJson(applicationReference, applicationJson); - - setNewPagesInApplicationJson(applicationReference, applicationJson); - - setNewActionsInApplicationJson(applicationReference, applicationJson); - - setActionCollectionsInApplicationJson(applicationReference, applicationJson); - - setDatasourcesInApplicationJson(applicationReference, applicationJson); - - return applicationJson; - } - - private void setApplicationInApplicationJson( - ApplicationGitReference applicationReference, ApplicationJson applicationJson) { - // Extract application data from the json - Application application = getApplicationResource(applicationReference.getApplication(), Application.class); - applicationJson.setExportedApplication(application); - - if (application != null && !CollectionUtils.isNullOrEmpty(application.getPages())) { - // Remove null values - org.apache.commons.collections.CollectionUtils.filter( - application.getPages(), PredicateUtils.notNullPredicate()); - // Create a deep clone of application pages to update independently - application.setViewMode(false); - final List applicationPages = - new ArrayList<>(application.getPages().size()); - application - .getPages() - .forEach(applicationPage -> - applicationPages.add(gson.fromJson(gson.toJson(applicationPage), ApplicationPage.class))); - application.setPublishedPages(applicationPages); - } - } - - private void setThemesInApplicationJson( - ApplicationGitReference applicationReference, ApplicationJson applicationJson) { - applicationJson.setEditModeTheme(getApplicationResource(applicationReference.getTheme(), Theme.class)); - // Clone the edit mode theme to published theme as both should be same for git connected application because we - // do deploy and push as a single operation - applicationJson.setPublishedTheme(applicationJson.getEditModeTheme()); - } - - private void setDatasourcesInApplicationJson( - ApplicationGitReference applicationReference, ApplicationJson applicationJson) { - // Extract datasources - applicationJson.setDatasourceList( - getApplicationResource(applicationReference.getDatasources(), DatasourceStorage.class)); - } - - private void setActionCollectionsInApplicationJson( - ApplicationGitReference applicationReference, ApplicationJson applicationJson) { - // Extract actionCollection - if (CollectionUtils.isNullOrEmpty(applicationReference.getActionCollections())) { - applicationJson.setActionCollectionList(new ArrayList<>()); - } else { - Map actionCollectionBody = applicationReference.getActionCollectionBody(); - List actionCollections = - getApplicationResource(applicationReference.getActionCollections(), ActionCollection.class); - // Remove null values if present - org.apache.commons.collections.CollectionUtils.filter(actionCollections, PredicateUtils.notNullPredicate()); - actionCollections.forEach(actionCollection -> { - // Set the js object body to the unpublished collection - // Since file version v3 we are splitting the js object code and metadata separately - String keyName = actionCollection.getUnpublishedCollection().getName() - + actionCollection.getUnpublishedCollection().getPageId(); - if (actionCollectionBody != null && actionCollectionBody.containsKey(keyName)) { - actionCollection.getUnpublishedCollection().setBody(actionCollectionBody.get(keyName)); - } - // As we are publishing the app and then committing to git we expect the published and unpublished - // actionCollectionDTO will be same, so we create a deep copy for the published version for - // actionCollection from unpublishedActionCollectionDTO - actionCollection.setPublishedCollection(gson.fromJson( - gson.toJson(actionCollection.getUnpublishedCollection()), ActionCollectionDTO.class)); - }); - applicationJson.setActionCollectionList(actionCollections); - } - } - - private void setNewActionsInApplicationJson( - ApplicationGitReference applicationReference, ApplicationJson applicationJson) { - // Extract actions - if (CollectionUtils.isNullOrEmpty(applicationReference.getActions())) { - applicationJson.setActionList(new ArrayList<>()); - } else { - Map actionBody = applicationReference.getActionBody(); - List actions = getApplicationResource(applicationReference.getActions(), NewAction.class); - // Remove null values if present - org.apache.commons.collections.CollectionUtils.filter(actions, PredicateUtils.notNullPredicate()); - actions.forEach(newAction -> { - // With the file version v4 we have split the actions and metadata separately into two files - // So we need to set the body to the unpublished action - String keyName = newAction.getUnpublishedAction().getName() - + newAction.getUnpublishedAction().getPageId(); - if (actionBody != null - && (actionBody.containsKey(keyName)) - && !StringUtils.isEmpty(actionBody.get(keyName))) { - // For REMOTE plugin like Twilio the user actions are stored in key value pairs and hence they need - // to be - // deserialized separately unlike the body which is stored as string in the db. - if (newAction.getPluginType().toString().equals("REMOTE")) { - Map formData = gson.fromJson(actionBody.get(keyName), Map.class); - newAction - .getUnpublishedAction() - .getActionConfiguration() - .setFormData(formData); - } else { - newAction - .getUnpublishedAction() - .getActionConfiguration() - .setBody(actionBody.get(keyName)); - } - } - // As we are publishing the app and then committing to git we expect the published and unpublished - // actionDTO will be same, so we create a deep copy for the published version for action from - // unpublishedActionDTO - newAction.setPublishedAction( - gson.fromJson(gson.toJson(newAction.getUnpublishedAction()), ActionDTO.class)); - }); - applicationJson.setActionList(actions); - } - } - - private void setCustomJsLibsInApplicationJson( - ApplicationGitReference applicationReference, ApplicationJson applicationJson) { - List customJSLibList = - getApplicationResource(applicationReference.getJsLibraries(), CustomJSLib.class); - - // remove the duplicate js libraries if there is any - List customJSLibListWithoutDuplicates = new ArrayList<>(new HashSet<>(customJSLibList)); - - applicationJson.setCustomJSLibList(customJSLibListWithoutDuplicates); - } - - private void setNewPagesInApplicationJson( - ApplicationGitReference applicationReference, ApplicationJson applicationJson) { - // Extract pages - List pages = getApplicationResource(applicationReference.getPages(), NewPage.class); - // Remove null values - org.apache.commons.collections.CollectionUtils.filter(pages, PredicateUtils.notNullPredicate()); - // Set the DSL to page object before saving - Map pageDsl = applicationReference.getPageDsl(); - pages.forEach(page -> { - JSONParser jsonParser = new JSONParser(); - try { - if (pageDsl != null && pageDsl.get(page.getUnpublishedPage().getName()) != null) { - page.getUnpublishedPage().getLayouts().get(0).setDsl((JSONObject) jsonParser.parse( - pageDsl.get(page.getUnpublishedPage().getName()))); - } - } catch (ParseException e) { - log.error( - "Error parsing the page dsl for page: {}", - page.getUnpublishedPage().getName(), - e); - throw new AppsmithException( - AppsmithError.JSON_PROCESSING_ERROR, - page.getUnpublishedPage().getName()); - } - }); - pages.forEach(newPage -> { - // As we are publishing the app and then committing to git we expect the published and unpublished PageDTO - // will be same, so we create a deep copy for the published version for page from the unpublishedPageDTO - newPage.setPublishedPage(gson.fromJson(gson.toJson(newPage.getUnpublishedPage()), PageDTO.class)); - }); - applicationJson.setPageList(pages); - } - - public Mono deleteIndexLockFile(Path path) { - return fileUtils.deleteIndexLockFile(path, INDEX_LOCK_FILE_STALE_TIME); - } -} diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/ce/ResponseUtilsCE.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/ce/ResponseUtilsCE.java index ac965ed404..8cf4a00cd8 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/ce/ResponseUtilsCE.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/ce/ResponseUtilsCE.java @@ -295,8 +295,8 @@ public class ResponseUtilsCE { public Application updateApplicationWithDefaultResources(Application application) { if (application.getGitApplicationMetadata() != null - && !StringUtils.isEmpty(application.getGitApplicationMetadata().getDefaultApplicationId())) { - application.setId(application.getGitApplicationMetadata().getDefaultApplicationId()); + && !StringUtils.isEmpty(application.getGitApplicationMetadata().getDefaultArtifactId())) { + application.setId(application.getGitApplicationMetadata().getDefaultArtifactId()); } if (!CollectionUtils.isEmpty(application.getPages())) { application.getPages().forEach(page -> { diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/newpages/importable/NewPageImportableServiceCEImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/newpages/importable/NewPageImportableServiceCEImpl.java index 297393ff44..9f22c3a1eb 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/newpages/importable/NewPageImportableServiceCEImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/newpages/importable/NewPageImportableServiceCEImpl.java @@ -479,7 +479,7 @@ public class NewPageImportableServiceCEImpl implements ImportableServiceCE { GitArtifactMetadata gitData = application.getGitApplicationMetadata(); if (gitData != null - && !StringUtils.isEmpty(gitData.getDefaultApplicationId()) + && !StringUtils.isEmpty(gitData.getDefaultArtifactId()) && !StringUtils.isEmpty(gitData.getRepoName())) { String repoName = gitData.getRepoName(); Path repoPath = - Paths.get(application.getWorkspaceId(), gitData.getDefaultApplicationId(), repoName); + Paths.get(application.getWorkspaceId(), gitData.getDefaultArtifactId(), repoName); // Delete git repo from local - return gitFileUtils.deleteLocalRepo(repoPath).then(Mono.just(application)); + return commonGitFileUtils.deleteLocalRepo(repoPath).then(Mono.just(application)); } return Mono.just(application); }); @@ -741,7 +741,7 @@ public class ApplicationPageServiceCEImpl implements ApplicationPageServiceCE { DefaultResources defaults = new DefaultResources(); GitArtifactMetadata gitData = application.getGitApplicationMetadata(); if (gitData != null) { - defaults.setApplicationId(gitData.getDefaultApplicationId()); + defaults.setApplicationId(gitData.getDefaultArtifactId()); defaults.setBranchName(gitData.getBranchName()); } else { defaults.setApplicationId(applicationId); diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/GitArtifactHelperCE.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/GitArtifactHelperCE.java index 3d1d75fbea..240a61c6e4 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/GitArtifactHelperCE.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/GitArtifactHelperCE.java @@ -2,6 +2,7 @@ package com.appsmith.server.services.ce; import com.appsmith.server.acl.AclPermission; import com.appsmith.server.domains.Artifact; +import com.appsmith.server.dtos.ArtifactExchangeJson; import com.appsmith.server.dtos.GitAuthDTO; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -57,5 +58,11 @@ public interface GitArtifactHelperCE { Mono isPrivateRepoLimitReached(Artifact artifact, boolean isClearCache); - Mono publishArtifact(Artifact artifact); + Mono publishArtifact(Artifact artifact, Boolean publishedManually); + + Mono createArtifactForImport(String workspaceId, String repoName); + + Mono deleteArtifact(String artifactId); + + Boolean isContextInArtifactEmpty(ArtifactExchangeJson artifactExchangeJson); } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/GitServiceCE.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/GitServiceCE.java deleted file mode 100644 index 20fae6cafe..0000000000 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/GitServiceCE.java +++ /dev/null @@ -1,93 +0,0 @@ -package com.appsmith.server.services.ce; - -import com.appsmith.external.dtos.GitBranchDTO; -import com.appsmith.external.dtos.GitLogDTO; -import com.appsmith.external.dtos.GitStatusDTO; -import com.appsmith.external.dtos.MergeStatusDTO; -import com.appsmith.server.domains.Application; -import com.appsmith.server.domains.GitArtifactMetadata; -import com.appsmith.server.domains.GitAuth; -import com.appsmith.server.domains.GitProfile; -import com.appsmith.server.dtos.ApplicationImportDTO; -import com.appsmith.server.dtos.AutoCommitProgressDTO; -import com.appsmith.server.dtos.GitCommitDTO; -import com.appsmith.server.dtos.GitConnectDTO; -import com.appsmith.server.dtos.GitDocsDTO; -import com.appsmith.server.dtos.GitMergeDTO; -import com.appsmith.server.dtos.GitPullDTO; -import org.eclipse.jgit.lib.BranchTrackingStatus; -import reactor.core.publisher.Mono; - -import java.util.List; -import java.util.Map; - -public interface GitServiceCE { - - Mono> updateOrCreateGitProfileForCurrentUser(GitProfile gitProfile); - - Mono> updateOrCreateGitProfileForCurrentUser( - GitProfile gitProfile, String defaultApplicationId); - - Mono getDefaultGitProfileOrCreateIfEmpty(); - - Mono getGitProfileForUser(String defaultApplicationId); - - Mono connectApplicationToGit(String defaultApplicationId, GitConnectDTO gitConnectDTO, String origin); - - Mono updateGitMetadata(String applicationId, GitArtifactMetadata gitArtifactMetadata); - - Mono commitApplication( - GitCommitDTO commitDTO, String defaultApplicationId, String branchName, boolean doAmend); - - Mono commitApplication(GitCommitDTO commitDTO, String defaultApplicationId, String branchName); - - Mono> getCommitHistory(String defaultApplicationId, String branchName); - - Mono pushApplication(String defaultApplicationId, String branchName); - - Mono detachRemote(String defaultApplicationId); - - Mono createBranch(String defaultApplicationId, GitBranchDTO branchDTO, String srcBranch); - - Mono checkoutBranch(String defaultApplicationId, String branchName, boolean addFileLock); - - Mono pullApplication(String defaultApplicationId, String branchName); - - Mono> listBranchForApplication( - String defaultApplicationId, Boolean pruneBranches, String currentBranch); - - Mono getGitApplicationMetadata(String defaultApplicationId); - - Mono getStatus(String defaultApplicationId, boolean compareRemote, String branchName); - - Mono mergeBranch(String applicationId, GitMergeDTO gitMergeDTO); - - Mono isBranchMergeable(String applicationId, GitMergeDTO gitMergeDTO); - - Mono createConflictedBranch(String defaultApplicationId, String branchName); - - // TODO - Mono importApplicationFromGit(String organisationId, GitConnectDTO gitConnectDTO); - - Mono generateSSHKey(String keyType); - - Mono testConnection(String defaultApplicationId); - - Mono deleteBranch(String defaultApplicationId, String branchName); - - Mono discardChanges(String defaultApplicationId, String branchName); - - Mono> getGitDocUrls(); - - Mono fetchRemoteChanges(String defaultApplicationId, String branchName, boolean isFileLock); - - Mono> updateProtectedBranches(String defaultApplicationId, List branchNames); - - Mono> getProtectedBranches(String defaultApplicationId); - - Mono getAutoCommitProgress(String applicationId); - - Mono autoCommitApplication(String defaultApplicationId, String branchName); - - Mono toggleAutoCommitEnabled(String defaultApplicationId); -} diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/GitServiceCEImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/GitServiceCEImpl.java deleted file mode 100644 index e1a78588bf..0000000000 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/GitServiceCEImpl.java +++ /dev/null @@ -1,3431 +0,0 @@ -package com.appsmith.server.services.ce; - -import com.appsmith.external.constants.AnalyticsEvents; -import com.appsmith.external.constants.ErrorReferenceDocUrl; -import com.appsmith.external.dtos.GitBranchDTO; -import com.appsmith.external.dtos.GitLogDTO; -import com.appsmith.external.dtos.GitStatusDTO; -import com.appsmith.external.dtos.MergeStatusDTO; -import com.appsmith.external.git.GitExecutor; -import com.appsmith.external.git.constants.GitConstants; -import com.appsmith.external.git.constants.GitConstants.GitCommandConstants; -import com.appsmith.external.git.constants.GitSpan; -import com.appsmith.external.models.Datasource; -import com.appsmith.external.models.DatasourceStorage; -import com.appsmith.git.service.GitExecutorImpl; -import com.appsmith.server.acl.AclPermission; -import com.appsmith.server.actioncollections.base.ActionCollectionService; -import com.appsmith.server.applications.base.ApplicationService; -import com.appsmith.server.configurations.EmailConfig; -import com.appsmith.server.constants.Assets; -import com.appsmith.server.constants.Entity; -import com.appsmith.server.constants.FieldName; -import com.appsmith.server.constants.GitDefaultCommitMessage; -import com.appsmith.server.datasources.base.DatasourceService; -import com.appsmith.server.domains.Application; -import com.appsmith.server.domains.ApplicationMode; -import com.appsmith.server.domains.AutoCommitConfig; -import com.appsmith.server.domains.GitArtifactMetadata; -import com.appsmith.server.domains.GitAuth; -import com.appsmith.server.domains.GitDeployKeys; -import com.appsmith.server.domains.GitProfile; -import com.appsmith.server.domains.Plugin; -import com.appsmith.server.domains.User; -import com.appsmith.server.domains.UserData; -import com.appsmith.server.domains.Workspace; -import com.appsmith.server.dtos.ApplicationImportDTO; -import com.appsmith.server.dtos.ApplicationJson; -import com.appsmith.server.dtos.AutoCommitProgressDTO; -import com.appsmith.server.dtos.GitCommitDTO; -import com.appsmith.server.dtos.GitConnectDTO; -import com.appsmith.server.dtos.GitDocsDTO; -import com.appsmith.server.dtos.GitMergeDTO; -import com.appsmith.server.dtos.GitPullDTO; -import com.appsmith.server.exceptions.AppsmithError; -import com.appsmith.server.exceptions.AppsmithException; -import com.appsmith.server.exports.internal.ExportService; -import com.appsmith.server.git.GitRedisUtils; -import com.appsmith.server.git.autocommit.helpers.GitAutoCommitHelper; -import com.appsmith.server.helpers.CollectionUtils; -import com.appsmith.server.helpers.GitDeployKeyGenerator; -import com.appsmith.server.helpers.GitFileUtils; -import com.appsmith.server.helpers.GitPrivateRepoHelper; -import com.appsmith.server.helpers.GitUtils; -import com.appsmith.server.helpers.ResponseUtils; -import com.appsmith.server.imports.internal.ImportService; -import com.appsmith.server.migrations.JsonSchemaVersions; -import com.appsmith.server.newactions.base.NewActionService; -import com.appsmith.server.newpages.base.NewPageService; -import com.appsmith.server.plugins.base.PluginService; -import com.appsmith.server.repositories.GitDeployKeysRepository; -import com.appsmith.server.services.AnalyticsService; -import com.appsmith.server.services.ApplicationPageService; -import com.appsmith.server.services.SessionUserService; -import com.appsmith.server.services.UserDataService; -import com.appsmith.server.services.UserService; -import com.appsmith.server.services.WorkspaceService; -import com.appsmith.server.solutions.ApplicationPermission; -import com.appsmith.server.solutions.DatasourcePermission; -import com.appsmith.server.solutions.WorkspacePermission; -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.CheckoutConflictException; -import org.eclipse.jgit.api.errors.EmptyCommitException; -import org.eclipse.jgit.api.errors.GitAPIException; -import org.eclipse.jgit.api.errors.InvalidRemoteException; -import org.eclipse.jgit.api.errors.TransportException; -import org.eclipse.jgit.errors.RepositoryNotFoundException; -import org.eclipse.jgit.lib.BranchTrackingStatus; -import org.eclipse.jgit.util.StringUtils; -import org.springframework.context.annotation.Import; -import org.springframework.dao.DuplicateKeyException; -import org.springframework.stereotype.Service; -import org.springframework.transaction.reactive.TransactionalOperator; -import reactor.core.Exceptions; -import reactor.core.observability.micrometer.Micrometer; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -import java.io.IOException; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.time.Instant; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.concurrent.TimeoutException; -import java.util.stream.Collectors; - -import static com.appsmith.external.constants.AnalyticsEvents.GIT_ADD_PROTECTED_BRANCH; -import static com.appsmith.external.constants.AnalyticsEvents.GIT_REMOVE_PROTECTED_BRANCH; -import static com.appsmith.external.git.constants.GitConstants.CONFLICTED_SUCCESS_MESSAGE; -import static com.appsmith.external.git.constants.GitConstants.DEFAULT_COMMIT_MESSAGE; -import static com.appsmith.external.git.constants.GitConstants.EMPTY_COMMIT_ERROR_MESSAGE; -import static com.appsmith.external.git.constants.GitConstants.GIT_CONFIG_ERROR; -import static com.appsmith.external.git.constants.GitConstants.GIT_PROFILE_ERROR; -import static com.appsmith.external.git.constants.GitConstants.MERGE_CONFLICT_BRANCH_NAME; -import static com.appsmith.external.git.constants.GitSpan.OPS_COMMIT; -import static com.appsmith.external.git.constants.GitSpan.OPS_STATUS; -import static com.appsmith.git.constants.AppsmithBotAsset.APPSMITH_BOT_USERNAME; -import static com.appsmith.server.constants.ArtifactType.APPLICATION; -import static com.appsmith.server.constants.FieldName.DEFAULT; -import static com.appsmith.server.constants.SerialiseArtifactObjective.VERSION_CONTROL; -import static com.appsmith.server.helpers.DefaultResourcesUtils.createDefaultIdsOrUpdateWithGivenResourceIds; -import static java.lang.Boolean.FALSE; -import static java.lang.Boolean.TRUE; -import static org.apache.commons.lang.ObjectUtils.defaultIfNull; - -/** - * Git APIs are slow today because these needs to communicate with remote repo and/or serialise and de-serialise the - * application. This process takes time and the client may cancel the request. This leads to the flow getting stopped - * midway producing corrupted states. - * We use the synchronous sink to ensure that even though the client may have cancelled the flow, git operations should - * proceed uninterrupted and whenever the user refreshes the page, we will have the sane state. synchronous sink does - * not take subscription cancellations into account. This means that even if the subscriber has cancelled its - * subscription, the create method still generates its event. - */ -@Slf4j -@RequiredArgsConstructor -@Import({GitExecutorImpl.class}) -@Service -public class GitServiceCEImpl implements GitServiceCE { - - private final UserService userService; - private final UserDataService userDataService; - private final SessionUserService sessionUserService; - private final ApplicationService applicationService; - private final ApplicationPageService applicationPageService; - private final NewPageService newPageService; - private final NewActionService newActionService; - private final ActionCollectionService actionCollectionService; - private final GitFileUtils fileUtils; - private final ImportService importService; - private final ExportService exportService; - private final GitExecutor gitExecutor; - private final ResponseUtils responseUtils; - private final EmailConfig emailConfig; - private final AnalyticsService analyticsService; - private final GitDeployKeysRepository gitDeployKeysRepository; - private final DatasourceService datasourceService; - private final PluginService pluginService; - private final DatasourcePermission datasourcePermission; - private final ApplicationPermission applicationPermission; - private final WorkspacePermission workspacePermission; - private final WorkspaceService workspaceService; - private final GitRedisUtils gitRedisUtils; - private final ObservationRegistry observationRegistry; - private final GitPrivateRepoHelper gitPrivateRepoHelper; - private final TransactionalOperator transactionalOperator; - private final GitAutoCommitHelper gitAutoCommitHelper; - - @Override - public Mono updateGitMetadata(String applicationId, GitArtifactMetadata gitArtifactMetadata) { - - if (Optional.ofNullable(gitArtifactMetadata).isEmpty()) { - return Mono.error( - new AppsmithException(AppsmithError.INVALID_PARAMETER, "Git metadata values cannot be null")); - } - - // For default application we expect a GitAuth to be a part of gitMetadata. We are using save method to leverage - // @Encrypted annotation used for private SSH keys - // applicationService.save sets the transient fields so no need to set it again from this method - return applicationService - .findById(applicationId, applicationPermission.getEditPermission()) - .flatMap(application -> { - application.setGitApplicationMetadata(gitArtifactMetadata); - return applicationService.save(application); - }); - } - - @Override - public Mono getGitApplicationMetadata(String defaultApplicationId) { - return Mono.zip( - getApplicationById(defaultApplicationId, applicationPermission.getEditPermission()), - userDataService.getForCurrentUser()) - .map(tuple -> { - Application application = tuple.getT1(); - UserData userData = tuple.getT2(); - Map gitProfiles = new HashMap<>(); - GitArtifactMetadata gitData = application.getGitApplicationMetadata(); - if (!CollectionUtils.isNullOrEmpty(userData.getGitProfiles())) { - gitProfiles.put(DEFAULT, userData.getGitProfileByKey(DEFAULT)); - gitProfiles.put(defaultApplicationId, userData.getGitProfileByKey(defaultApplicationId)); - } - if (gitData == null) { - GitArtifactMetadata res = new GitArtifactMetadata(); - res.setGitProfiles(gitProfiles); - return res; - } - gitData.setGitProfiles(gitProfiles); - if (gitData.getGitAuth() != null) { - gitData.setPublicKey(gitData.getGitAuth().getPublicKey()); - } - gitData.setDocUrl(Assets.GIT_DEPLOY_KEY_DOC_URL); - return gitData; - }); - } - - @Override - public Mono> updateOrCreateGitProfileForCurrentUser( - GitProfile gitProfile, String defaultApplicationId) { - - // Throw error in following situations: - // 1. Updating or creating global git profile (defaultApplicationId = "default") and update is made with empty - // authorName or authorEmail - // 2. Updating or creating repo specific profile and user want to use repo specific profile but provided empty - // values for authorName and email - - if ((DEFAULT.equals(defaultApplicationId) || FALSE.equals(gitProfile.getUseGlobalProfile())) - && StringUtils.isEmptyOrNull(gitProfile.getAuthorName())) { - return Mono.error(new AppsmithException(AppsmithError.INVALID_PARAMETER, "Author Name")); - } else if ((DEFAULT.equals(defaultApplicationId) || FALSE.equals(gitProfile.getUseGlobalProfile())) - && StringUtils.isEmptyOrNull(gitProfile.getAuthorEmail())) { - return Mono.error(new AppsmithException(AppsmithError.INVALID_PARAMETER, "Author Email")); - } else if (StringUtils.isEmptyOrNull(defaultApplicationId)) { - return Mono.error(new AppsmithException(AppsmithError.INVALID_PARAMETER, FieldName.APPLICATION_ID)); - } - - if (DEFAULT.equals(defaultApplicationId)) { - gitProfile.setUseGlobalProfile(null); - } else if (!TRUE.equals(gitProfile.getUseGlobalProfile())) { - gitProfile.setUseGlobalProfile(FALSE); - } - - return sessionUserService - .getCurrentUser() - .flatMap(user -> userService.findByEmail(user.getEmail())) - .flatMap(user -> userDataService - .getForUser(user.getId()) - .flatMap(userData -> { - // GitProfiles will be null if the user has not created any git profile. - GitProfile savedProfile = userData.getGitProfileByKey(defaultApplicationId); - GitProfile defaultGitProfile = userData.getGitProfileByKey(DEFAULT); - - if (savedProfile == null || !savedProfile.equals(gitProfile) || defaultGitProfile == null) { - userData.setGitProfiles(userData.setGitProfileByKey(defaultApplicationId, gitProfile)); - - // Assign appsmith user profile as a fallback git profile - if (defaultGitProfile == null) { - GitProfile userProfile = new GitProfile(); - String authorName = StringUtils.isEmptyOrNull(user.getName()) - ? user.getUsername().split("@")[0] - : user.getName(); - userProfile.setAuthorEmail(user.getEmail()); - userProfile.setAuthorName(authorName); - userProfile.setUseGlobalProfile(null); - userData.setGitProfiles(userData.setGitProfileByKey(DEFAULT, userProfile)); - } - - // Update userData here - UserData requiredUpdates = new UserData(); - requiredUpdates.setGitProfiles(userData.getGitProfiles()); - return userDataService - .updateForUser(user, requiredUpdates) - .map(UserData::getGitProfiles); - } - return Mono.just(userData.getGitProfiles()); - }) - .switchIfEmpty(Mono.defer(() -> { - // If profiles are empty use Appsmith's user profile as git default profile - GitProfile profile = new GitProfile(); - String authorName = StringUtils.isEmptyOrNull(user.getName()) - ? user.getUsername().split("@")[0] - : user.getName(); - - profile.setAuthorName(authorName); - profile.setAuthorEmail(user.getEmail()); - - UserData requiredUpdates = new UserData(); - requiredUpdates.setGitProfiles(Map.of(DEFAULT, gitProfile)); - return userDataService - .updateForUser(user, requiredUpdates) - .map(UserData::getGitProfiles); - })) - .filter(profiles -> !CollectionUtils.isNullOrEmpty(profiles))); - } - - @Override - public Mono> updateOrCreateGitProfileForCurrentUser(GitProfile gitProfile) { - gitProfile.setUseGlobalProfile(null); - return updateOrCreateGitProfileForCurrentUser(gitProfile, DEFAULT); - } - - @Override - public Mono getDefaultGitProfileOrCreateIfEmpty() { - // Get default git profile if the default is empty then use Appsmith profile as a fallback value - return getGitProfileForUser(DEFAULT).flatMap(gitProfile -> { - if (StringUtils.isEmptyOrNull(gitProfile.getAuthorName()) - || StringUtils.isEmptyOrNull(gitProfile.getAuthorEmail())) { - return updateGitProfileWithAppsmithProfile(DEFAULT); - } - gitProfile.setUseGlobalProfile(null); - return Mono.just(gitProfile); - }); - } - - @Override - public Mono getGitProfileForUser(String defaultApplicationId) { - return userDataService.getForCurrentUser().map(userData -> { - GitProfile gitProfile = userData.getGitProfileByKey(defaultApplicationId); - if (gitProfile != null && gitProfile.getUseGlobalProfile() == null) { - gitProfile.setUseGlobalProfile(true); - } else if (gitProfile == null) { - // If the profile is requested for repo specific using the applicationId - GitProfile gitProfile1 = new GitProfile(); - gitProfile1.setAuthorName(""); - gitProfile1.setAuthorEmail(""); - gitProfile1.setUseGlobalProfile(true); - return gitProfile1; - } - return gitProfile; - }); - } - - private Mono updateGitProfileWithAppsmithProfile(String key) { - return sessionUserService - .getCurrentUser() - .flatMap(user -> userService.findByEmail(user.getEmail())) - .flatMap(currentUser -> { - GitProfile gitProfile = new GitProfile(); - String authorName = StringUtils.isEmptyOrNull(currentUser.getName()) - ? currentUser.getUsername().split("@")[0] - : currentUser.getName(); - gitProfile.setAuthorEmail(currentUser.getEmail()); - gitProfile.setAuthorName(authorName); - gitProfile.setUseGlobalProfile(null); - return userDataService.getForUser(currentUser).flatMap(userData -> { - UserData updates = new UserData(); - if (CollectionUtils.isNullOrEmpty(userData.getGitProfiles())) { - updates.setGitProfiles(Map.of(key, gitProfile)); - } else { - userData.getGitProfiles().put(key, gitProfile); - updates.setGitProfiles(userData.getGitProfiles()); - } - return userDataService - .updateForUser(currentUser, updates) - .thenReturn(gitProfile); - }); - }); - } - - /** - * This method will make a commit to local repo - * This is used directly from client, and we need to acquire file lock before starting to keep the application in a sane state - * - * @param commitDTO information required for making a commit - * @param defaultApplicationId application branch on which the commit needs to be done - * @param doAmend if we want to amend the commit with the earlier one, used in connect flow - * @return success message - */ - @Override - public Mono commitApplication( - GitCommitDTO commitDTO, String defaultApplicationId, String branchName, boolean doAmend) { - return this.commitApplication(commitDTO, defaultApplicationId, branchName, doAmend, true); - } - - /** - * This method will make a commit to local repo and is used internally in flows like create, merge branch - * Since the lock is already acquired by the other flows, we do not need to acquire file lock again - * - * @param commitDTO information required for making a commit - * @param defaultApplicationId application branch on which the commit needs to be done - * @return success message - */ - public Mono commitApplication(GitCommitDTO commitDTO, String defaultApplicationId, String branchName) { - return this.commitApplication(commitDTO, defaultApplicationId, branchName, false, false); - } - - /** - * @param commitDTO information required for making a commit - * @param defaultApplicationId application branch on which the commit needs to be done - * @param branchName branch name for the commit flow - * @param doAmend if we want to amend the commit with the earlier one, used in connect flow - * @param isFileLock boolean value indicates whether the file lock is needed to complete the operation - * @return success message - */ - private Mono commitApplication( - GitCommitDTO commitDTO, - String defaultApplicationId, - String branchName, - boolean doAmend, - boolean isFileLock) { - /* - 1. Check if application exists and user have sufficient permissions - 2. Check if branch name exists in git metadata - 3. Save application to the existing local repo - 4. Commit application : git add, git commit (Also check if git init required) - */ - - String commitMessage = commitDTO.getCommitMessage(); - StringBuilder result = new StringBuilder(); - - if (commitMessage == null || commitMessage.isEmpty()) { - commitDTO.setCommitMessage(DEFAULT_COMMIT_MESSAGE + GitDefaultCommitMessage.CONNECT_FLOW.getReason()); - } - if (StringUtils.isEmptyOrNull(branchName)) { - throw new AppsmithException(AppsmithError.INVALID_PARAMETER, FieldName.BRANCH_NAME); - } - - boolean isSystemGeneratedTemp = false; - if (commitDTO.getCommitMessage().contains(DEFAULT_COMMIT_MESSAGE)) { - isSystemGeneratedTemp = true; - } - - boolean isSystemGenerated = isSystemGeneratedTemp; - Mono commitMono = this.getApplicationById( - defaultApplicationId, applicationPermission.getEditPermission()) - .zipWhen(application -> - gitPrivateRepoHelper.isBranchProtected(application.getGitApplicationMetadata(), branchName)) - .map(objects -> { - if (objects.getT2()) { - throw new AppsmithException( - AppsmithError.GIT_ACTION_FAILED, - "commit", - "Cannot commit to protected branch " + branchName); - } - return objects.getT1(); - }) - .flatMap(application -> { - GitArtifactMetadata gitData = application.getGitApplicationMetadata(); - if (TRUE.equals(isFileLock)) { - return addFileLock(gitData.getDefaultApplicationId(), GitCommandConstants.COMMIT) - .then(Mono.just(application)); - } - return Mono.just(application); - }) - .flatMap(defaultApplication -> { - GitArtifactMetadata defaultGitMetadata = defaultApplication.getGitApplicationMetadata(); - if (Optional.ofNullable(defaultGitMetadata).isEmpty()) { - return Mono.error( - new AppsmithException(AppsmithError.INVALID_GIT_CONFIGURATION, GIT_CONFIG_ERROR)); - } - // Check if the repo is public for current application and if the user have changed the access after - // the connection - final String workspaceId = defaultApplication.getWorkspaceId(); - return GitUtils.isRepoPrivate(defaultGitMetadata.getBrowserSupportedRemoteUrl()) - .flatMap(isPrivate -> { - // Check the repo limit if the visibility status is updated, or it is private - if (!isPrivate.equals(defaultGitMetadata.getIsRepoPrivate()) - || isPrivate.equals(TRUE)) { - defaultGitMetadata.setIsRepoPrivate(isPrivate); - defaultApplication.setGitApplicationMetadata(defaultGitMetadata); - return applicationService - .save(defaultApplication) - // Check if the private repo count is less than the allowed repo count - .flatMap(application -> - gitPrivateRepoHelper.isRepoLimitReached(workspaceId, false)) - .flatMap(isRepoLimitReached -> { - if (FALSE.equals(isRepoLimitReached)) { - return Mono.just(defaultApplication); - } - throw new AppsmithException(AppsmithError.GIT_APPLICATION_LIMIT_ERROR); - }); - } else { - return Mono.just(defaultApplication); - } - }); - }) - .then(applicationService.findByBranchNameAndDefaultApplicationId( - branchName, defaultApplicationId, applicationPermission.getEditPermission())) - .flatMap((branchedApplication) -> { - GitArtifactMetadata gitArtifactMetadata = branchedApplication.getGitApplicationMetadata(); - if (gitArtifactMetadata == null) { - return Mono.error( - new AppsmithException(AppsmithError.INVALID_GIT_CONFIGURATION, GIT_CONFIG_ERROR)); - } - String errorEntity = ""; - if (StringUtils.isEmptyOrNull(gitArtifactMetadata.getBranchName())) { - errorEntity = "branch name"; - } else if (StringUtils.isEmptyOrNull(gitArtifactMetadata.getDefaultApplicationId())) { - errorEntity = "default application"; - } else if (StringUtils.isEmptyOrNull(gitArtifactMetadata.getRepoName())) { - errorEntity = "repository name"; - } - - if (!errorEntity.isEmpty()) { - return Mono.error(new AppsmithException( - AppsmithError.INVALID_GIT_CONFIGURATION, "Unable to find " + errorEntity)); - } - return Mono.zip( - exportService.exportByArtifactId(branchedApplication.getId(), VERSION_CONTROL, APPLICATION), - Mono.just(branchedApplication)); - }) - .flatMap(tuple -> { - ApplicationJson applicationJson = (ApplicationJson) tuple.getT1(); - Application childApplication = tuple.getT2(); - GitArtifactMetadata gitData = childApplication.getGitApplicationMetadata(); - Path baseRepoSuffix = Paths.get( - childApplication.getWorkspaceId(), - gitData.getDefaultApplicationId(), - gitData.getRepoName()); - Mono repoPathMono; - try { - repoPathMono = fileUtils.saveApplicationToLocalRepoWithAnalytics( - baseRepoSuffix, applicationJson, gitData.getBranchName()); - } catch (IOException | GitAPIException e) { - return Mono.error(e); - } - gitData.setLastCommittedAt(Instant.now()); - Mono branchedApplicationMono = updateGitMetadata(childApplication.getId(), gitData); - return Mono.zip( - repoPathMono, - userDataService.getGitProfileForCurrentUser(defaultApplicationId), - branchedApplicationMono, - Mono.just(childApplication)); - }) - .onErrorResume(e -> { - log.error("Error in commit flow: ", e); - if (e instanceof RepositoryNotFoundException) { - return Mono.error( - new AppsmithException(AppsmithError.REPOSITORY_NOT_FOUND, defaultApplicationId)); - } else if (e instanceof AppsmithException) { - return Mono.error(e); - } - return Mono.error(new AppsmithException(AppsmithError.GIT_FILE_SYSTEM_ERROR, e.getMessage())); - }) - .flatMap(tuple -> { - Path baseRepoPath = tuple.getT1(); - GitProfile authorProfile = tuple.getT2(); - Application childApplication = tuple.getT3(); - GitArtifactMetadata gitApplicationData = childApplication.getGitApplicationMetadata(); - - if (authorProfile == null || StringUtils.isEmptyOrNull(authorProfile.getAuthorName())) { - String errorMessage = "Unable to find git author configuration for logged-in user. You can set " - + "up a git profile from the user profile section."; - return addAnalyticsForGitOperation( - AnalyticsEvents.GIT_COMMIT, - childApplication, - AppsmithError.INVALID_GIT_CONFIGURATION.getErrorType(), - AppsmithError.INVALID_GIT_CONFIGURATION.getMessage(errorMessage), - childApplication - .getGitApplicationMetadata() - .getIsRepoPrivate()) - .flatMap(user -> Mono.error( - new AppsmithException(AppsmithError.INVALID_GIT_CONFIGURATION, errorMessage))); - } - result.append("Commit Result : "); - Mono gitCommitMono = gitExecutor - .commitArtifact( - baseRepoPath, - commitMessage, - authorProfile.getAuthorName(), - authorProfile.getAuthorEmail(), - false, - doAmend) - .onErrorResume(error -> { - if (error instanceof EmptyCommitException) { - return Mono.just(EMPTY_COMMIT_ERROR_MESSAGE); - } - return addAnalyticsForGitOperation( - AnalyticsEvents.GIT_COMMIT, - childApplication, - error.getClass().getName(), - error.getMessage(), - childApplication - .getGitApplicationMetadata() - .getIsRepoPrivate()) - .then(Mono.error(new AppsmithException( - AppsmithError.GIT_ACTION_FAILED, "commit", error.getMessage()))); - }); - - return Mono.zip(gitCommitMono, Mono.just(childApplication)); - }) - .flatMap(tuple -> { - Application childApplication = tuple.getT2(); - String commitStatus = tuple.getT1(); - result.append(commitStatus); - - if (TRUE.equals(commitDTO.getDoPush())) { - // Push flow - result.append(".\nPush Result : "); - return pushApplication(childApplication.getId(), false, false) - .map(pushResult -> result.append(pushResult).toString()) - .zipWith(Mono.just(childApplication)); - } - return Mono.zip(Mono.just(result.toString()), Mono.just(childApplication)); - }) - .flatMap(tuple -> { - Application childApplication = tuple.getT2(); - String status = tuple.getT1(); - return Mono.zip( - Mono.just(status), - publishAndOrGetApplication(childApplication.getId(), commitDTO.getDoPush())); - }) - // Add BE analytics - .flatMap(tuple -> { - String status = tuple.getT1(); - Application childApplication = tuple.getT2(); - // Update json schema versions so that we can detect if the next update was made by DB migration or - // by the user - Application update = new Application(); - // Reset migration related fields before commit to detect the updates correctly between the commits - update.setClientSchemaVersion(JsonSchemaVersions.clientVersion); - update.setServerSchemaVersion(JsonSchemaVersions.serverVersion); - update.setIsManualUpdate(false); - - return applicationService - .update(childApplication.getId(), update) - // Release the file lock on git repo - .flatMap(application -> { - if (TRUE.equals(isFileLock)) { - return releaseFileLock(childApplication - .getGitApplicationMetadata() - .getDefaultApplicationId()); - } - return Mono.just(application); - }) - .then(addAnalyticsForGitOperation( - AnalyticsEvents.GIT_COMMIT, - childApplication, - "", - "", - childApplication.getGitApplicationMetadata().getIsRepoPrivate(), - isSystemGenerated)) - .thenReturn(status) - .name(OPS_COMMIT) - .tap(Micrometer.observation(observationRegistry)); - }); - - return Mono.create(sink -> { - commitMono.subscribe(sink::success, sink::error, null, sink.currentContext()); - }); - } - - /** - * Method to get commit history for application branch - * - * @param defaultApplicationId application for which the commit history is needed - * @return list of commits - */ - @Override - public Mono> getCommitHistory(String defaultApplicationId, String branchName) { - - Mono> commitHistoryMono = applicationService - .findByBranchNameAndDefaultApplicationId( - branchName, defaultApplicationId, applicationPermission.getReadPermission()) - .flatMap(application -> { - GitArtifactMetadata gitData = application.getGitApplicationMetadata(); - if (gitData == null - || StringUtils.isEmptyOrNull( - application.getGitApplicationMetadata().getBranchName())) { - return Mono.error( - new AppsmithException(AppsmithError.INVALID_GIT_CONFIGURATION, GIT_CONFIG_ERROR)); - } - Path baseRepoSuffix = Paths.get( - application.getWorkspaceId(), gitData.getDefaultApplicationId(), gitData.getRepoName()); - // Checkout to branch - return Mono.zip( - gitExecutor - .checkoutToBranch(baseRepoSuffix, gitData.getBranchName()) - .onErrorResume(e -> Mono.error(new AppsmithException( - AppsmithError.GIT_ACTION_FAILED, "checkout", e.getMessage()))), - Mono.just(baseRepoSuffix)); - }) - .flatMap(tuple -> { - Path baseRepoSuffix = tuple.getT2(); - return gitExecutor - .getCommitHistory(baseRepoSuffix) - .onErrorResume(e -> Mono.error( - new AppsmithException(AppsmithError.GIT_ACTION_FAILED, "log", e.getMessage()))); - }); - - return Mono.create( - sink -> commitHistoryMono.subscribe(sink::success, sink::error, null, sink.currentContext())); - } - - /** - * Connect the application from Appsmith to a git repo - * This is the prerequisite step needed to perform all the git operation for an application - * We are implementing the deployKey approach and since the deploy-keys are repo level these keys are store under application. - * Each application is equal to a repo in the git(and each branch creates a new application with default application as parent) - * - * @param gitConnectDTO applicationId - this is used to link the local git repo to an application - * remoteUrl - used for connecting to remote repo etc - * @return Application object with the updated data - */ - @Override - public Mono connectApplicationToGit( - String defaultApplicationId, GitConnectDTO gitConnectDTO, String originHeader) { - /* - * Connecting the application for the first time - * The ssh keys is already present in application object from generate SSH key step - * We would be updating the remote url and default branchName - * */ - - if (StringUtils.isEmptyOrNull(gitConnectDTO.getRemoteUrl())) { - return Mono.error(new AppsmithException(AppsmithError.INVALID_PARAMETER, "Remote Url")); - } - - if (StringUtils.isEmptyOrNull(originHeader)) { - return Mono.error(new AppsmithException(AppsmithError.INVALID_PARAMETER, FieldName.ORIGIN)); - } - - Mono currentUserMono = userDataService - .getForCurrentUser() - .filter(userData -> !CollectionUtils.isNullOrEmpty(userData.getGitProfiles())) - .switchIfEmpty( - Mono.error(new AppsmithException(AppsmithError.INVALID_GIT_CONFIGURATION, GIT_PROFILE_ERROR))); - - Mono> profileMono = updateOrCreateGitProfileForCurrentUser( - gitConnectDTO.getGitProfile(), defaultApplicationId) - .switchIfEmpty( - Mono.error(new AppsmithException(AppsmithError.INVALID_GIT_CONFIGURATION, GIT_PROFILE_ERROR))); - - final String browserSupportedUrl; - try { - browserSupportedUrl = GitUtils.convertSshUrlToBrowserSupportedUrl(gitConnectDTO.getRemoteUrl()); - } catch (AppsmithException error) { - return Mono.error(error); - } - - Mono isPrivateRepoMono = - GitUtils.isRepoPrivate(browserSupportedUrl).cache(); - - Mono connectApplicationMono = profileMono - .then(getApplicationById(defaultApplicationId, applicationPermission.getGitConnectPermission())) - .zipWith(isPrivateRepoMono) - .flatMap(tuple -> { - Application application = tuple.getT1(); - boolean isRepoPrivate = tuple.getT2(); - // Check if the repo is public - if (!isRepoPrivate) { - return Mono.just(application); - } - // Check the limit for number of private repo - return gitPrivateRepoHelper - .isRepoLimitReached(application.getWorkspaceId(), true) - .flatMap(isRepoLimitReached -> { - if (FALSE.equals(isRepoLimitReached)) { - return Mono.just(application); - } - return addAnalyticsForGitOperation( - AnalyticsEvents.GIT_PRIVATE_REPO_LIMIT_EXCEEDED, - application, - AppsmithError.GIT_APPLICATION_LIMIT_ERROR.getErrorType(), - AppsmithError.GIT_APPLICATION_LIMIT_ERROR.getMessage(), - true) - .flatMap(ignore -> Mono.error( - new AppsmithException(AppsmithError.GIT_APPLICATION_LIMIT_ERROR))); - }); - }) - .flatMap(application -> { - GitArtifactMetadata gitArtifactMetadata = application.getGitApplicationMetadata(); - if (isInvalidDefaultApplicationGitMetadata(application.getGitApplicationMetadata())) { - return Mono.error(new AppsmithException(AppsmithError.INVALID_GIT_SSH_CONFIGURATION)); - } else { - String repoName = GitUtils.getRepoName(gitConnectDTO.getRemoteUrl()); - Path repoSuffix = Paths.get(application.getWorkspaceId(), defaultApplicationId, repoName); - Mono defaultBranchMono = gitExecutor - .cloneRemoteIntoArtifactRepo( - repoSuffix, - gitConnectDTO.getRemoteUrl(), - gitArtifactMetadata.getGitAuth().getPrivateKey(), - gitArtifactMetadata.getGitAuth().getPublicKey()) - .onErrorResume(error -> { - log.error("Error while cloning the remote repo, ", error); - return fileUtils - .deleteLocalRepo(repoSuffix) - .then(addAnalyticsForGitOperation( - AnalyticsEvents.GIT_CONNECT, - application, - error.getClass().getName(), - error.getMessage(), - application - .getGitApplicationMetadata() - .getIsRepoPrivate())) - .flatMap(app -> { - if (error instanceof TransportException) { - return Mono.error(new AppsmithException( - AppsmithError.INVALID_GIT_SSH_CONFIGURATION)); - } - if (error instanceof InvalidRemoteException) { - return Mono.error(new AppsmithException( - AppsmithError.INVALID_GIT_CONFIGURATION, - error.getMessage())); - } - if (error instanceof TimeoutException) { - return Mono.error( - new AppsmithException(AppsmithError.GIT_EXECUTION_TIMEOUT)); - } - if (error instanceof ClassCastException) { - // To catch TransportHttp cast error in case HTTP URL is passed - // instead of SSH URL - if (error.getMessage().contains("TransportHttp")) { - return Mono.error(new AppsmithException( - AppsmithError.INVALID_GIT_SSH_URL)); - } - } - return Mono.error(new AppsmithException( - AppsmithError.GIT_GENERIC_ERROR, error.getMessage())); - }); - }); - return Mono.zip( - Mono.just(application), defaultBranchMono, Mono.just(repoName), Mono.just(repoSuffix)); - } - }) - .flatMap(tuple -> { - Application application = tuple.getT1(); - String defaultBranch = tuple.getT2(); - String repoName = tuple.getT3(); - Path repoPath = tuple.getT4(); - final String applicationId = application.getId(); - final String workspaceId = application.getWorkspaceId(); - try { - return fileUtils - .checkIfDirectoryIsEmpty(repoPath) - .zipWith(isPrivateRepoMono) - .flatMap(objects -> { - boolean isEmpty = objects.getT1(); - boolean isRepoPrivate = objects.getT2(); - if (!isEmpty) { - return addAnalyticsForGitOperation( - AnalyticsEvents.GIT_CONNECT, - application, - AppsmithError.INVALID_GIT_REPO.getErrorType(), - AppsmithError.INVALID_GIT_REPO.getMessage(), - isRepoPrivate) - .then(Mono.error( - new AppsmithException(AppsmithError.INVALID_GIT_REPO))); - } else { - GitArtifactMetadata gitArtifactMetadata = - application.getGitApplicationMetadata(); - gitArtifactMetadata.setDefaultApplicationId(applicationId); - gitArtifactMetadata.setBranchName(defaultBranch); - gitArtifactMetadata.setDefaultBranchName(defaultBranch); - gitArtifactMetadata.setRemoteUrl(gitConnectDTO.getRemoteUrl()); - gitArtifactMetadata.setRepoName(repoName); - gitArtifactMetadata.setBrowserSupportedRemoteUrl(browserSupportedUrl); - - gitArtifactMetadata.setIsRepoPrivate(isRepoPrivate); - gitArtifactMetadata.setLastCommittedAt(Instant.now()); - - // Set branchName for each application resource - return exportService - .exportByArtifactId(applicationId, VERSION_CONTROL, APPLICATION) - .flatMap(artifactExchangeJson -> { - ApplicationJson applicationJson = - (ApplicationJson) artifactExchangeJson; - applicationJson - .getExportedApplication() - .setGitApplicationMetadata(gitArtifactMetadata); - return importService - .importArtifactInWorkspaceFromGit( - workspaceId, - applicationId, - applicationJson, - defaultBranch) - .map(importableArtifact -> - (Application) importableArtifact); - }); - } - }) - .onErrorResume(e -> { - if (e instanceof IOException) { - return Mono.error(new AppsmithException( - AppsmithError.GIT_FILE_SYSTEM_ERROR, e.getMessage())); - } - return Mono.error(e); - }); - } catch (IOException e) { - log.error("Error while cloning the remote repo, {}", e.getMessage()); - return Mono.error(new AppsmithException(AppsmithError.GIT_FILE_SYSTEM_ERROR, e.getMessage())); - } - }) - .flatMap(application -> { - String repoName = GitUtils.getRepoName(gitConnectDTO.getRemoteUrl()); - String defaultPageId = ""; - if (!application.getPages().isEmpty()) { - defaultPageId = application.getPages().stream() - .filter(applicationPage -> - applicationPage.getIsDefault().equals(TRUE)) - .collect(Collectors.toList()) - .get(0) - .getId(); - } - String viewModeUrl = Paths.get( - "/", Entity.APPLICATIONS, "/", application.getId(), Entity.PAGES, defaultPageId) - .toString(); - String editModeUrl = Paths.get(viewModeUrl, "edit").toString(); - // Initialize the repo with readme file - try { - return Mono.zip( - fileUtils - .initializeReadme( - Paths.get( - application.getWorkspaceId(), - defaultApplicationId, - repoName, - "README.md"), - originHeader + viewModeUrl, - originHeader + editModeUrl) - .onErrorMap(throwable -> { - log.error("Error while initialising git repo, {0}", throwable); - return new AppsmithException( - AppsmithError.GIT_FILE_SYSTEM_ERROR, - Exceptions.unwrap(throwable) - .getMessage()); - }), - currentUserMono) - .flatMap(tuple -> { - UserData userData = tuple.getT2(); - GitProfile profile = userData.getGitProfileByKey(defaultApplicationId); - if (profile == null - || StringUtils.isEmptyOrNull(profile.getAuthorName()) - || TRUE.equals(profile.getUseGlobalProfile())) { - - profile = userData.getGitProfileByKey(DEFAULT); - } - return gitExecutor.commitArtifact( - tuple.getT1(), - DEFAULT_COMMIT_MESSAGE + GitDefaultCommitMessage.CONNECT_FLOW.getReason(), - profile.getAuthorName(), - profile.getAuthorEmail(), - false, - false); - }) - .flatMap(ignore -> { - // Commit and push application to check if the SSH key has the write access - GitCommitDTO commitDTO = new GitCommitDTO(); - commitDTO.setDoPush(true); - commitDTO.setCommitMessage( - DEFAULT_COMMIT_MESSAGE + GitDefaultCommitMessage.CONNECT_FLOW.getReason()); - - return this.commitApplication( - commitDTO, - defaultApplicationId, - application - .getGitApplicationMetadata() - .getBranchName(), - true) - .onErrorResume(error -> - // If the push fails remove all the cloned files from local repo - this.detachRemote(defaultApplicationId) - .flatMap(isDeleted -> { - if (error instanceof TransportException) { - return addAnalyticsForGitOperation( - AnalyticsEvents.GIT_CONNECT, - application, - error.getClass() - .getName(), - error.getMessage(), - application - .getGitApplicationMetadata() - .getIsRepoPrivate()) - .then(Mono.error(new AppsmithException( - AppsmithError - .INVALID_GIT_SSH_CONFIGURATION, - error.getMessage()))); - } - return Mono.error(new AppsmithException( - AppsmithError.GIT_ACTION_FAILED, - "push", - error.getMessage())); - })); - }) - .then(addAnalyticsForGitOperation( - AnalyticsEvents.GIT_CONNECT, - application, - application.getGitApplicationMetadata().getIsRepoPrivate())) - .map(responseUtils::updateApplicationWithDefaultResources); - } catch (IOException e) { - log.error("Error while cloning the remote repo, {}", e.getMessage()); - return Mono.error(new AppsmithException(AppsmithError.GIT_FILE_SYSTEM_ERROR, e.getMessage())); - } - }); - - return Mono.create( - sink -> connectApplicationMono.subscribe(sink::success, sink::error, null, sink.currentContext())); - } - - @Override - public Mono pushApplication(String defaultApplicationId, String branchName) { - - if (StringUtils.isEmptyOrNull(branchName)) { - throw new AppsmithException(AppsmithError.INVALID_PARAMETER, FieldName.BRANCH_NAME); - } - return applicationService - .findBranchedApplicationId(branchName, defaultApplicationId, applicationPermission.getEditPermission()) - .flatMap(applicationId -> pushApplication(applicationId, true, true)); - } - - /** - * Push flow for dehydrated apps - * - * @param applicationId application which needs to be pushed to remote repo - * @return Success message - */ - private Mono pushApplication(String applicationId, boolean doPublish, boolean isFileLock) { - - Mono pushStatusMono = publishAndOrGetApplication(applicationId, doPublish) - .flatMap(application -> { - if (applicationId.equals( - application.getGitApplicationMetadata().getDefaultApplicationId())) { - return Mono.just(application); - } - return applicationService - .findById(application.getGitApplicationMetadata().getDefaultApplicationId()) - .map(defaultApp -> { - application - .getGitApplicationMetadata() - .setGitAuth(defaultApp - .getGitApplicationMetadata() - .getGitAuth()); - return application; - }); - }) - .flatMap(application -> { - if (TRUE.equals(isFileLock)) { - return addFileLock( - application.getGitApplicationMetadata().getDefaultApplicationId(), - GitCommandConstants.PUSH) - .map(status -> application); - } - return Mono.just(application); - }) - .flatMap(application -> { - GitArtifactMetadata gitData = application.getGitApplicationMetadata(); - - if (gitData == null - || StringUtils.isEmptyOrNull(gitData.getBranchName()) - || StringUtils.isEmptyOrNull(gitData.getDefaultApplicationId()) - || StringUtils.isEmptyOrNull(gitData.getGitAuth().getPrivateKey())) { - - return Mono.error( - new AppsmithException(AppsmithError.INVALID_GIT_CONFIGURATION, GIT_CONFIG_ERROR)); - } - Path baseRepoSuffix = Paths.get( - application.getWorkspaceId(), gitData.getDefaultApplicationId(), gitData.getRepoName()); - - GitAuth gitAuth = gitData.getGitAuth(); - return gitExecutor - .checkoutToBranch( - baseRepoSuffix, - application.getGitApplicationMetadata().getBranchName()) - .then(gitExecutor - .pushApplication( - baseRepoSuffix, - gitData.getRemoteUrl(), - gitAuth.getPublicKey(), - gitAuth.getPrivateKey(), - gitData.getBranchName()) - .zipWith(Mono.just(application))) - .onErrorResume(error -> addAnalyticsForGitOperation( - AnalyticsEvents.GIT_PUSH, - application, - error.getClass().getName(), - error.getMessage(), - application - .getGitApplicationMetadata() - .getIsRepoPrivate()) - .flatMap(application1 -> { - if (error instanceof TransportException) { - return Mono.error( - new AppsmithException(AppsmithError.INVALID_GIT_SSH_CONFIGURATION)); - } - return Mono.error(new AppsmithException( - AppsmithError.GIT_ACTION_FAILED, "push", error.getMessage())); - })); - }) - .flatMap(tuple -> { - String pushResult = tuple.getT1(); - Application application = tuple.getT2(); - return pushApplicationErrorRecovery(pushResult, application).zipWith(Mono.just(tuple.getT2())); - }) - // Add BE analytics - .flatMap(tuple -> { - String pushStatus = tuple.getT1(); - Application application = tuple.getT2(); - if (TRUE.equals(isFileLock)) { - return releaseFileLock( - application.getGitApplicationMetadata().getDefaultApplicationId()) - .map(status -> tuple); - } - return Mono.zip(Mono.just(pushStatus), Mono.just(application)); - }) - .flatMap(tuple -> { - String pushStatus = tuple.getT1(); - Application application = tuple.getT2(); - return addAnalyticsForGitOperation( - AnalyticsEvents.GIT_PUSH, - application, - application.getGitApplicationMetadata().getIsRepoPrivate()) - .thenReturn(pushStatus); - }) - .name(GitSpan.OPS_PUSH) - .tap(Micrometer.observation(observationRegistry)); - - return Mono.create(sink -> pushStatusMono.subscribe(sink::success, sink::error, null, sink.currentContext())); - } - - /** - * This method is used to recover from the errors that can occur during the push operation - * Mostly happens when the remote branch is protected or any specific rules in place on the branch. - * Since the users will be in a bad state where the changes are committed locally, but they are - * not able to push them changes or revert the changes either. - * 1. Push rejected due to branch protection rules on remote, reset hard prev commit - * - * @param pushResult status of git push operation - * @param application application data to be used for analytics - * @return status of the git push flow - */ - private Mono pushApplicationErrorRecovery(String pushResult, Application application) { - if (pushResult.contains("REJECTED_NONFASTFORWARD")) { - - return addAnalyticsForGitOperation( - AnalyticsEvents.GIT_PUSH, - application, - AppsmithError.GIT_UPSTREAM_CHANGES.getErrorType(), - AppsmithError.GIT_UPSTREAM_CHANGES.getMessage(), - application.getGitApplicationMetadata().getIsRepoPrivate()) - .flatMap(application1 -> Mono.error(new AppsmithException(AppsmithError.GIT_UPSTREAM_CHANGES))); - } else if (pushResult.contains("REJECTED_OTHERREASON") || pushResult.contains("pre-receive hook declined")) { - Path path = Paths.get( - application.getWorkspaceId(), - application.getGitApplicationMetadata().getDefaultApplicationId(), - application.getGitApplicationMetadata().getRepoName()); - return gitExecutor - .resetHard(path, application.getGitApplicationMetadata().getBranchName()) - .then(Mono.error(new AppsmithException( - AppsmithError.GIT_ACTION_FAILED, - "push", - "Unable to push changes as pre-receive hook declined. Please make sure that you don't have any rules enabled on the branch " - + application.getGitApplicationMetadata().getBranchName()))); - } - return Mono.just(pushResult); - } - - /** - * Method to remove all the git metadata for the application and connected resources. This will remove: - * - local repo - * - all the branched applications present in DB except for default application - * - * @param defaultApplicationId application which needs to be disconnected from git connection - * @return Application data - */ - @Override - public Mono detachRemote(String defaultApplicationId) { - Mono disconnectMono = getApplicationById( - defaultApplicationId, applicationPermission.getGitConnectPermission()) - .flatMap(defaultApplication -> { - if (Optional.ofNullable(defaultApplication.getGitApplicationMetadata()) - .isEmpty() - || isInvalidDefaultApplicationGitMetadata(defaultApplication.getGitApplicationMetadata())) { - return Mono.error(new AppsmithException( - AppsmithError.INVALID_GIT_CONFIGURATION, - "Please reconfigure the application to connect to git repo")); - } - // Remove the git contents from file system - GitArtifactMetadata gitArtifactMetadata = defaultApplication.getGitApplicationMetadata(); - String repoName = gitArtifactMetadata.getRepoName(); - Path repoSuffix = Paths.get( - defaultApplication.getWorkspaceId(), - gitArtifactMetadata.getDefaultApplicationId(), - repoName); - String defaultApplicationBranchName = gitArtifactMetadata.getBranchName(); - String remoteUrl = gitArtifactMetadata.getRemoteUrl(); - String privateKey = gitArtifactMetadata.getGitAuth().getPrivateKey(); - String publicKey = gitArtifactMetadata.getGitAuth().getPublicKey(); - return Mono.zip( - gitExecutor.listBranches(repoSuffix), - Mono.just(defaultApplication), - Mono.just(repoSuffix), - Mono.just(defaultApplicationBranchName)); - }) - .flatMap(tuple -> { - Application defaultApplication = tuple.getT2(); - Path repoSuffix = tuple.getT3(); - List branch = tuple.getT1().stream() - .map(GitBranchDTO::getBranchName) - .filter(branchName -> !branchName.startsWith("origin")) - .collect(Collectors.toList()); - - // Remove the parent application branch name from the list - branch.remove(tuple.getT4()); - defaultApplication.setGitApplicationMetadata(null); - defaultApplication.getPages().forEach(page -> page.setDefaultPageId(page.getId())); - if (!CollectionUtils.isNullOrEmpty(defaultApplication.getPublishedPages())) { - defaultApplication.getPublishedPages().forEach(page -> page.setDefaultPageId(page.getId())); - } - return fileUtils.deleteLocalRepo(repoSuffix).flatMap(status -> Flux.fromIterable(branch) - .flatMap(gitBranch -> applicationService - .findByBranchNameAndDefaultApplicationId( - gitBranch, defaultApplicationId, applicationPermission.getEditPermission()) - .flatMap(applicationPageService::deleteApplicationByResource)) - .then(applicationService.save(defaultApplication))); - }) - .flatMap(application -> - // Update all the resources to replace defaultResource Ids with the resource Ids as branchName - // will be deleted - Flux.fromIterable(application.getPages()) - .flatMap(page -> newPageService.findById(page.getId(), Optional.empty())) - .map(newPage -> { - newPage.setDefaultResources(null); - return createDefaultIdsOrUpdateWithGivenResourceIds(newPage, null); - }) - .collectList() - .flatMapMany(newPageService::saveAll) - .flatMap(newPage -> newActionService - .findByPageId(newPage.getId(), Optional.empty()) - .map(newAction -> { - newAction.setDefaultResources(null); - if (newAction.getUnpublishedAction() != null) { - newAction.getUnpublishedAction().setDefaultResources(null); - } - if (newAction.getPublishedAction() != null) { - newAction.getPublishedAction().setDefaultResources(null); - } - return createDefaultIdsOrUpdateWithGivenResourceIds(newAction, null); - }) - .collectList() - .flatMapMany(newActionService::saveAll) - .thenMany(actionCollectionService.findByPageId(newPage.getId())) - .map(actionCollection -> { - actionCollection.setDefaultResources(null); - if (actionCollection.getUnpublishedCollection() != null) { - actionCollection - .getUnpublishedCollection() - .setDefaultResources(null); - } - if (actionCollection.getPublishedCollection() != null) { - actionCollection - .getPublishedCollection() - .setDefaultResources(null); - } - return createDefaultIdsOrUpdateWithGivenResourceIds(actionCollection, null); - }) - .collectList() - .flatMapMany(actionCollectionService::saveAll)) - .then(addAnalyticsForGitOperation(AnalyticsEvents.GIT_DISCONNECT, application, false)) - .map(responseUtils::updateApplicationWithDefaultResources)) - .name(GitSpan.OPS_DETACH_REMOTE) - .tap(Micrometer.observation(observationRegistry)); - - return Mono.create(sink -> disconnectMono.subscribe(sink::success, sink::error, null, sink.currentContext())); - } - - public Mono createBranch(String defaultApplicationId, GitBranchDTO branchDTO, String srcBranch) { - - /* - 1. Check if the src application is available and user have sufficient permissions - 2. Create and checkout to requested branch - 3. Rehydrate the application from source application reference - */ - - if (StringUtils.isEmptyOrNull(srcBranch) - || srcBranch.startsWith("origin/") - || branchDTO.getBranchName().startsWith("origin/")) { - return Mono.error(new AppsmithException(AppsmithError.INVALID_PARAMETER, FieldName.BRANCH_NAME)); - } - - Mono createBranchMono = applicationService - .findByBranchNameAndDefaultApplicationId( - srcBranch, defaultApplicationId, applicationPermission.getEditPermission()) - .zipWhen(srcApplication -> { - GitArtifactMetadata gitData = srcApplication.getGitApplicationMetadata(); - if (gitData.getDefaultApplicationId().equals(srcApplication.getId())) { - return Mono.just( - srcApplication.getGitApplicationMetadata().getGitAuth()); - } - return applicationService - .getSshKey(gitData.getDefaultApplicationId()) - .map(gitAuthDTO -> { - GitAuth gitAuth = new GitAuth(); - gitAuth.setPrivateKey(gitAuthDTO.getPrivateKey()); - gitAuth.setPublicKey(gitAuthDTO.getPublicKey()); - gitAuth.setDocUrl(gitAuthDTO.getDocUrl()); - return gitAuth; - }); - }) - .flatMap(tuple -> { - Application srcApplication = tuple.getT1(); - GitAuth defaultGitAuth = tuple.getT2(); - GitArtifactMetadata srcBranchGitData = srcApplication.getGitApplicationMetadata(); - if (srcBranchGitData == null - || StringUtils.isEmptyOrNull(srcBranchGitData.getDefaultApplicationId()) - || StringUtils.isEmptyOrNull(srcBranchGitData.getRepoName())) { - return Mono.error( - new AppsmithException( - AppsmithError.INVALID_GIT_CONFIGURATION, - "Unable to find the parent branch. Please create a branch from other available branches")); - } - Path repoSuffix = Paths.get( - srcApplication.getWorkspaceId(), - srcBranchGitData.getDefaultApplicationId(), - srcBranchGitData.getRepoName()); - // Create a new branch from the parent checked out branch - - return addFileLock(srcBranchGitData.getDefaultApplicationId(), GitCommandConstants.CREATE_BRANCH) - .flatMap(status -> gitExecutor.checkoutToBranch(repoSuffix, srcBranch)) - .onErrorResume(error -> Mono.error(new AppsmithException( - AppsmithError.GIT_ACTION_FAILED, "checkout", "Unable to find " + srcBranch))) - .zipWhen(isCheckedOut -> gitExecutor - .fetchRemote( - repoSuffix, - defaultGitAuth.getPublicKey(), - defaultGitAuth.getPrivateKey(), - false, - srcBranch, - true) - .onErrorResume(error -> Mono.error( - new AppsmithException(AppsmithError.GIT_ACTION_FAILED, "fetch", error)))) - .flatMap(ignore -> gitExecutor - .listBranches(repoSuffix) - .flatMap(branchList -> { - boolean isDuplicateName = branchList.stream() - // We are only supporting origin as the remote name so this is safe - // but needs to be altered if we start supporting user defined remote - // names - .anyMatch(branch -> branch.getBranchName() - .replaceFirst("origin/", "") - .equals(branchDTO.getBranchName())); - - if (isDuplicateName) { - return Mono.error(new AppsmithException( - AppsmithError.DUPLICATE_KEY_USER_ERROR, - "remotes/origin/" + branchDTO.getBranchName(), - FieldName.BRANCH_NAME)); - } - return gitExecutor.createAndCheckoutToBranch( - repoSuffix, branchDTO.getBranchName()); - })) - .flatMap(branchName -> { - final String srcApplicationId = srcApplication.getId(); - srcBranchGitData.setBranchName(branchName); - srcBranchGitData.setIsRepoPrivate(null); - // Save a new application in DB and update from the parent branch application - srcBranchGitData.setGitAuth(null); - srcBranchGitData.setLastCommittedAt(Instant.now()); - srcApplication.setId(null); - srcApplication.setPages(null); - srcApplication.setPublishedPages(null); - srcApplication.setEditModeThemeId(null); - srcApplication.setPublishedModeThemeId(null); - srcApplication.setGitApplicationMetadata(srcBranchGitData); - return Mono.zip( - applicationService.save(srcApplication), - exportService.exportByArtifactId( - srcApplicationId, VERSION_CONTROL, APPLICATION)); - }) - .onErrorResume(error -> Mono.error(new AppsmithException( - AppsmithError.GIT_ACTION_FAILED, "branch", error.getMessage()))); - }) - .flatMap(tuple -> { - Application savedApplication = tuple.getT1(); - return importService - .importArtifactInWorkspaceFromGit( - savedApplication.getWorkspaceId(), - savedApplication.getId(), - tuple.getT2(), - branchDTO.getBranchName()) - .map(importableArtifact -> (Application) importableArtifact) - .flatMap(application -> { - // Commit and push for new branch created this is to avoid issues when user tries to - // create a - // new branch from uncommitted branch - GitArtifactMetadata gitData = application.getGitApplicationMetadata(); - GitCommitDTO commitDTO = new GitCommitDTO(); - commitDTO.setCommitMessage(DEFAULT_COMMIT_MESSAGE - + GitDefaultCommitMessage.BRANCH_CREATED.getReason() - + gitData.getBranchName()); - commitDTO.setDoPush(true); - return commitApplication( - commitDTO, gitData.getDefaultApplicationId(), gitData.getBranchName()) - .thenReturn(application); - }); - }) - .flatMap(application -> releaseFileLock( - application.getGitApplicationMetadata().getDefaultApplicationId()) - .then(addAnalyticsForGitOperation( - AnalyticsEvents.GIT_CREATE_BRANCH, - application, - application.getGitApplicationMetadata().getIsRepoPrivate()))) - .map(responseUtils::updateApplicationWithDefaultResources) - .name(GitSpan.OPS_CREATE_BRANCH) - .tap(Micrometer.observation(observationRegistry)); - - return Mono.create(sink -> createBranchMono.subscribe(sink::success, sink::error, null, sink.currentContext())); - } - - public Mono checkoutBranch(String defaultApplicationId, String branchName, boolean addFileLock) { - - if (StringUtils.isEmptyOrNull(branchName)) { - return Mono.error(new AppsmithException(AppsmithError.INVALID_PARAMETER, FieldName.BRANCH_NAME)); - } - - // get the root application - Mono rootAppMono = - getApplicationById(defaultApplicationId, applicationPermission.getEditPermission()); - if (addFileLock) { - rootAppMono = rootAppMono.flatMap( - application -> addFileLock(defaultApplicationId, GitCommandConstants.CHECKOUT_BRANCH) - .thenReturn(application)); - } - - // If the user is trying to check out remote branch, create a new branch if the branch does not exist already - if (branchName.startsWith("origin/")) { - String finalBranchName = branchName.replaceFirst("origin/", ""); - rootAppMono = rootAppMono - .flatMap(application -> { - GitArtifactMetadata gitArtifactMetadata = application.getGitApplicationMetadata(); - Path repoPath = Paths.get( - application.getWorkspaceId(), - gitArtifactMetadata.getDefaultApplicationId(), - gitArtifactMetadata.getRepoName()); - return gitExecutor.listBranches(repoPath); - }) - .flatMap(gitBranchDTOList -> { - long branchMatchCount = gitBranchDTOList.stream() - .filter(gitBranchDTO -> - gitBranchDTO.getBranchName().equals(finalBranchName)) - .count(); - if (branchMatchCount == 0) { - return checkoutRemoteBranch(defaultApplicationId, finalBranchName); - } else { - return Mono.error(new AppsmithException( - AppsmithError.GIT_ACTION_FAILED, - "checkout", - branchName + " already exists in local - " - + branchName.replaceFirst("origin/", ""))); - } - }); - } else { - rootAppMono = rootAppMono - .flatMap(application -> { - if (isInvalidDefaultApplicationGitMetadata(application.getGitApplicationMetadata())) { - return Mono.error(new AppsmithException(AppsmithError.INVALID_GIT_SSH_CONFIGURATION)); - } - return applicationService.findByBranchNameAndDefaultApplicationId( - branchName, defaultApplicationId, applicationPermission.getReadPermission()); - }) - .flatMap(application -> addAnalyticsForGitOperation( - AnalyticsEvents.GIT_CHECKOUT_BRANCH, - application, - application.getGitApplicationMetadata().getIsRepoPrivate())) - .map(responseUtils::updateApplicationWithDefaultResources); - } - - return rootAppMono - .flatMap(result -> { - if (addFileLock) { - return releaseFileLock(defaultApplicationId).thenReturn(result); - } - return Mono.just(result); - }) - .tag(GitConstants.GitMetricConstants.CHECKOUT_REMOTE, FALSE.toString()) - .name(GitSpan.OPS_CHECKOUT_BRANCH) - .tap(Micrometer.observation(observationRegistry)); - } - - private Mono checkoutRemoteBranch(String defaultApplicationId, String branchName) { - Mono checkoutRemoteBranchMono = getApplicationById( - defaultApplicationId, applicationPermission.getEditPermission()) - .flatMap(application -> { - GitArtifactMetadata gitArtifactMetadata = application.getGitApplicationMetadata(); - String repoName = gitArtifactMetadata.getRepoName(); - Path repoPath = Paths.get(application.getWorkspaceId(), defaultApplicationId, repoName); - return gitExecutor - .fetchRemote( - repoPath, - gitArtifactMetadata.getGitAuth().getPublicKey(), - gitArtifactMetadata.getGitAuth().getPrivateKey(), - false, - branchName, - true) - .flatMap(fetchStatus -> gitExecutor - .checkoutRemoteBranch(repoPath, branchName) - .zipWith(Mono.just(application)) - .onErrorResume(error -> Mono.error(new AppsmithException( - AppsmithError.GIT_ACTION_FAILED, "checkout branch", error.getMessage())))); - }) - .flatMap(tuple -> { - /* - * create a new application(each application => git branch) - * Populate the application from the file system - * Check if the existing branch track the given remote branch using the StoredConfig - * Use the create branch method with isRemoteFlag or use the setStartPoint ,method in createBranch method - * */ - Application srcApplication = tuple.getT2(); - Mono applicationMono; - - // Create a new Application - GitArtifactMetadata srcBranchGitData = srcApplication.getGitApplicationMetadata(); - if (branchName.equals(srcBranchGitData.getBranchName())) { - /* - in this case, user deleted the initial default branch and now wants to check out to that branch. - as we didn't delete the application object but only the branch from git repo, - we can just use this existing application without creating a new one. - */ - applicationMono = Mono.just(srcApplication); - } else { - srcBranchGitData.setBranchName(branchName); - srcBranchGitData.setDefaultApplicationId(defaultApplicationId); - // Save a new application in DB and update from the parent branch application - srcBranchGitData.setGitAuth(null); - srcBranchGitData.setIsRepoPrivate(null); - srcBranchGitData.setLastCommittedAt(Instant.now()); - srcApplication.setId(null); - srcApplication.setPages(null); - srcApplication.setPublishedPages(null); - srcApplication.setGitApplicationMetadata(srcBranchGitData); - srcApplication.setEditModeThemeId(null); - srcApplication.setPublishedModeThemeId(null); - applicationMono = applicationService.save(srcApplication); - } - - return applicationMono - .flatMap(application1 -> fileUtils - .reconstructApplicationJsonFromGitRepoWithAnalytics( - srcApplication.getWorkspaceId(), - defaultApplicationId, - srcApplication - .getGitApplicationMetadata() - .getRepoName(), - branchName) - .zipWith(Mono.just(application1))) - // We need to handle the case specifically for default branch of Appsmith - // if user switches default branch and tries to delete the default branch we do not delete - // resource from db - // This is an exception only for the above case and in such case if the user tries to check - // out the branch again - // It results in an error as the resources are already present in db - // So we just rehydrate from the file system to the existing resource on the db - .onErrorResume(throwable -> { - if (throwable instanceof DuplicateKeyException) { - return fileUtils - .reconstructApplicationJsonFromGitRepoWithAnalytics( - srcApplication.getWorkspaceId(), - defaultApplicationId, - srcApplication - .getGitApplicationMetadata() - .getRepoName(), - branchName) - .zipWith(Mono.just(tuple.getT2())); - } - log.error(" Git checkout remote branch failed {}", throwable.getMessage()); - return Mono.error(new AppsmithException( - AppsmithError.GIT_ACTION_FAILED, " --checkout", throwable.getMessage())); - }); - }) - .flatMap(tuple -> { - // Get the latest application mono with all the changes - ApplicationJson applicationJson = tuple.getT1(); - Application application = tuple.getT2(); - return importService - .importArtifactInWorkspaceFromGit( - application.getWorkspaceId(), application.getId(), applicationJson, branchName) - .map(importableArtifact -> (Application) importableArtifact) - .flatMap(application1 -> addAnalyticsForGitOperation( - AnalyticsEvents.GIT_CHECKOUT_REMOTE_BRANCH, - application1, - TRUE.equals(application1 - .getGitApplicationMetadata() - .getIsRepoPrivate()))) - .map(responseUtils::updateApplicationWithDefaultResources); - }) - .tag(GitConstants.GitMetricConstants.CHECKOUT_REMOTE, TRUE.toString()) - .name(GitSpan.OPS_CHECKOUT_BRANCH) - .tap(Micrometer.observation(observationRegistry)); - - return Mono.create( - sink -> checkoutRemoteBranchMono.subscribe(sink::success, sink::error, null, sink.currentContext())); - } - - private Mono publishAndOrGetApplication(String applicationId, boolean publish) { - if (TRUE.equals(publish)) { - return applicationPageService - .publish(applicationId, true) - // Get application here to decrypt the git private key if present - .then(getApplicationById(applicationId, applicationPermission.getEditPermission())); - } - return getApplicationById(applicationId, applicationPermission.getEditPermission()); - } - - /** - * This method is deprecated and will be removed in next release. Please use the following method: - * getApplicationById(String applicationId, AclPermission aclPermission) - * - * @param applicationId ID of the application - * @return Mono of Application - */ - @Deprecated - public Mono getApplicationById(String applicationId) { - return getApplicationById(applicationId, applicationPermission.getEditPermission()); - } - - public Mono getApplicationById(String applicationId, AclPermission aclPermission) { - return applicationService - .findById(applicationId, aclPermission) - .switchIfEmpty(Mono.error(new AppsmithException( - AppsmithError.ACL_NO_RESOURCE_FOUND, FieldName.APPLICATION, applicationId))); - } - - @Deprecated - protected Mono checkPermissionOnWorkspace( - String workspaceId, AclPermission aclPermission, String operationName) { - return workspaceService - .findById(workspaceId, aclPermission) - .switchIfEmpty( - Mono.error(new AppsmithException(AppsmithError.ACTION_IS_NOT_AUTHORIZED, operationName))); - } - - /** - * Method to pull application json files from remote repo, make a commit with the changes present in local DB and - * make a system commit to remote repo - * - * @param defaultApplicationId application for which we want to pull remote changes and merge - * @param branchName remoteBranch from which the changes will be pulled and merged - * @return return the status of pull operation - */ - @Override - public Mono pullApplication(String defaultApplicationId, String branchName) { - /* - * 1.Dehydrate the application from DB so that the file system has the latest application data - * 2.Do git pull after the rehydration and merge the remote changes to the current branch - * On Merge conflict - throw exception and ask user to resolve these conflicts on remote - * TODO create new branch and push the changes to remote and ask the user to resolve it on github/gitlab UI - * 3.Then rehydrate from the file system to DB so that the latest changes from remote are rendered to the application - * 4.Get the latest application from the DB and send it back to client - * */ - - Mono pullMono = getApplicationById(defaultApplicationId, applicationPermission.getEditPermission()) - .flatMap(application -> { - GitArtifactMetadata gitData = application.getGitApplicationMetadata(); - return addFileLock(gitData.getDefaultApplicationId(), GitCommandConstants.PULL) - .then(Mono.just(application)); - }) - .flatMap(defaultApplication -> { - GitArtifactMetadata defaultGitMetadata = defaultApplication.getGitApplicationMetadata(); - return Mono.zip( - Mono.just(defaultApplication), - getStatus(defaultGitMetadata.getDefaultApplicationId(), branchName, false)); - }) - .flatMap(tuple -> { - Application defaultApplication = tuple.getT1(); - GitStatusDTO status = tuple.getT2(); - // Check if the repo is not clean - if (!status.getIsClean()) { - return Mono.error( - new AppsmithException( - AppsmithError.GIT_ACTION_FAILED, - "pull", - "There are uncommitted changes present in your local. Please commit them first and then try git pull")); - } - return pullAndRehydrateApplication(defaultApplication, branchName) - // Release file lock after the pull operation - .flatMap(gitPullDTO -> - releaseFileLock(defaultApplicationId).then(Mono.just(gitPullDTO))); - }) - .name(GitSpan.OPS_PULL) - .tap(Micrometer.observation(observationRegistry)); - - return Mono.create(sink -> pullMono.subscribe(sink::success, sink::error, null, sink.currentContext())); - } - - private Flux updateDefaultBranchName( - Path repoPath, String defaultBranchName, String defaultApplicationId) { - // Get the application from DB by new defaultBranch name - return applicationService - .findByBranchNameAndDefaultApplicationId( - defaultBranchName, defaultApplicationId, applicationPermission.getEditPermission()) - // Check if the branch is already present, If not follow checkout remote flow - .onErrorResume(throwable -> checkoutRemoteBranch(defaultApplicationId, defaultBranchName)) - // ensure the local branch exists - .then(gitExecutor - .createAndCheckoutToBranch(repoPath, defaultBranchName) - .onErrorComplete()) - // Update the default branch name in all the child applications - .thenMany(applicationService - .findAllApplicationsByDefaultApplicationId( - defaultApplicationId, applicationPermission.getEditPermission()) - .flatMap(application2 -> { - application2.getGitApplicationMetadata().setDefaultBranchName(defaultBranchName); - // clear the branch protection rules as the default branch name has been changed - application2.getGitApplicationMetadata().setBranchProtectionRules(null); - return applicationService.save(application2); - })); - } - - private Mono> handleRepoNotFoundException(String defaultApplicationId) { - - // clone application to the local filesystem again and update the defaultBranch for the application - // list branch and compare with branch applications and checkout if not exists - - return getApplicationById(defaultApplicationId, applicationPermission.getEditPermission()) - .flatMap(application -> { - GitArtifactMetadata gitArtifactMetadata = application.getGitApplicationMetadata(); - Path repoPath = Paths.get( - application.getWorkspaceId(), application.getId(), gitArtifactMetadata.getRepoName()); - GitAuth gitAuth = gitArtifactMetadata.getGitAuth(); - return gitExecutor - .cloneRemoteIntoArtifactRepo( - repoPath, - gitArtifactMetadata.getRemoteUrl(), - gitAuth.getPrivateKey(), - gitAuth.getPublicKey()) - .flatMap(defaultBranch -> gitExecutor.listBranches(repoPath)) - .flatMap(gitBranchDTOList -> { - List branchesToCheckout = new ArrayList<>(); - for (GitBranchDTO gitBranchDTO : gitBranchDTOList) { - if (gitBranchDTO.getBranchName().startsWith("origin/")) { - // remove origin/ prefix from the remote branch name - String branchName = - gitBranchDTO.getBranchName().replace("origin/", ""); - // The root application is always there, no need to check out it again - if (!branchName.equals(gitArtifactMetadata.getBranchName())) { - branchesToCheckout.add(branchName); - } - } else if (gitBranchDTO - .getBranchName() - .equals(gitArtifactMetadata.getDefaultBranchName())) { - /* - We just cloned from the remote default branch. - Update the isDefault flag If it's also set as default in DB - */ - gitBranchDTO.setDefault(true); - } - } - - return Flux.fromIterable(branchesToCheckout) - .flatMap(branchName -> applicationService - .findByBranchNameAndDefaultApplicationId( - branchName, - application.getId(), - applicationPermission.getReadPermission()) - // checkout the branch locally - .flatMap(application1 -> { - // Add the locally checked out branch to the branchList - GitBranchDTO gitBranchDTO = new GitBranchDTO(); - gitBranchDTO.setBranchName(branchName); - // set the default branch flag if there's a match. - // This can happen when user has changed the default branch other - // than - // remote - gitBranchDTO.setDefault(gitArtifactMetadata - .getDefaultBranchName() - .equals(branchName)); - gitBranchDTOList.add(gitBranchDTO); - return gitExecutor.checkoutRemoteBranch(repoPath, branchName); - }) - // Return empty mono when the branched application is not in db - .onErrorResume(throwable -> Mono.empty())) - .then(Mono.just(gitBranchDTOList)); - }); - }); - } - - private Mono syncDefaultBranchNameFromRemote(Path repoPath, Application rootApp) { - GitArtifactMetadata metadata = rootApp.getGitApplicationMetadata(); - GitAuth gitAuth = metadata.getGitAuth(); - return addFileLock(metadata.getDefaultApplicationId(), GitCommandConstants.SYNC_BRANCH) - .then(gitExecutor.getRemoteDefaultBranch( - repoPath, metadata.getRemoteUrl(), gitAuth.getPrivateKey(), gitAuth.getPublicKey())) - .flatMap(defaultBranchNameInRemote -> { - String defaultBranchInDb = GitUtils.getDefaultBranchName(metadata); - if (StringUtils.isEmptyOrNull(defaultBranchNameInRemote)) { - // If the default branch name in remote is empty or same as the one in DB, nothing to do - return Mono.just(defaultBranchInDb); - } - // check if default branch has been changed in remote - if (defaultBranchNameInRemote.equals(defaultBranchInDb)) { - return Mono.just(defaultBranchNameInRemote); - } - return updateDefaultBranchName( - repoPath, defaultBranchNameInRemote, metadata.getDefaultApplicationId()) - .then() - .thenReturn(defaultBranchNameInRemote); - }) - .flatMap(branchName -> - releaseFileLock(metadata.getDefaultApplicationId()).thenReturn(branchName)); - } - - @Override - public Mono> listBranchForApplication( - String defaultApplicationId, Boolean pruneBranches, String currentBranch) { - return getBranchList(defaultApplicationId, pruneBranches, currentBranch, true); - } - - protected Mono> getBranchList( - String defaultApplicationId, - Boolean pruneBranches, - String currentBranch, - boolean syncDefaultBranchWithRemote) { - // get the root application - Mono> branchMono = getApplicationById( - defaultApplicationId, applicationPermission.getEditPermission()) - .flatMap(rootApplication -> { - Path repoPath = getRepoPath(rootApplication); - Mono defaultBranchMono; - if (TRUE.equals(pruneBranches) && syncDefaultBranchWithRemote) { - defaultBranchMono = syncDefaultBranchNameFromRemote(repoPath, rootApplication); - } else { - defaultBranchMono = - Mono.just(GitUtils.getDefaultBranchName(rootApplication.getGitApplicationMetadata())); - } - return Mono.zip(defaultBranchMono, Mono.just(rootApplication), Mono.just(repoPath)); - }) - .flatMap(objects -> { - String defaultBranchName = objects.getT1(); - Application rootApplication = objects.getT2(); - Path repoPath = objects.getT3(); - return getBranchListWithDefaultBranchName( - rootApplication, repoPath, defaultBranchName, currentBranch, pruneBranches); - }) - .onErrorResume(throwable -> { - if (throwable instanceof RepositoryNotFoundException) { - // this will clone the repo again - return handleRepoNotFoundException(defaultApplicationId); - } - return Mono.error(throwable); - }); - - return Mono.create(sink -> branchMono.subscribe(sink::success, sink::error, null, sink.currentContext())); - } - - private Path getRepoPath(Application rootApplication) { - GitArtifactMetadata gitArtifactMetadata = rootApplication.getGitApplicationMetadata(); - if (gitArtifactMetadata == null - || gitArtifactMetadata.getDefaultApplicationId() == null - || gitArtifactMetadata.getRepoName() == null) { - log.error("Git config is not present for application {}", rootApplication.getId()); - throw new AppsmithException(AppsmithError.INVALID_GIT_CONFIGURATION, GIT_CONFIG_ERROR); - } - return Paths.get( - rootApplication.getWorkspaceId(), - gitArtifactMetadata.getDefaultApplicationId(), - gitArtifactMetadata.getRepoName()); - } - - private Mono> getBranchListWithDefaultBranchName( - Application rootApp, Path repoPath, String defaultBranchName, String currentBranch, boolean pruneBranches) { - return addFileLock(rootApp.getId(), GitCommandConstants.LIST_BRANCH) - .flatMap(objects -> { - GitArtifactMetadata gitArtifactMetadata = rootApp.getGitApplicationMetadata(); - - if (TRUE.equals(pruneBranches)) { - return gitExecutor - .fetchRemote( - repoPath, - gitArtifactMetadata.getGitAuth().getPublicKey(), - gitArtifactMetadata.getGitAuth().getPrivateKey(), - false, - currentBranch, - true) - .then(gitExecutor.listBranches(repoPath)); - } else { - return gitExecutor.listBranches(repoPath); - } - }) - .flatMap(branchDTOList -> releaseFileLock(rootApp.getId()).thenReturn(branchDTOList)) - .map(branchDTOList -> { - for (GitBranchDTO branchDTO : branchDTOList) { - if (StringUtils.equalsIgnoreCase(branchDTO.getBranchName(), defaultBranchName)) { - branchDTO.setDefault(true); - break; - } - } - return branchDTOList; - }) - .flatMap(gitBranchDTOList -> FALSE.equals(pruneBranches) - ? Mono.just(gitBranchDTOList) - : addAnalyticsForGitOperation( - AnalyticsEvents.GIT_PRUNE, - rootApp, - rootApp.getGitApplicationMetadata().getIsRepoPrivate()) - .thenReturn(gitBranchDTOList)); - } - - private Mono getStatus(String defaultApplicationId, String branchName, boolean isFileLock) { - return getStatus(defaultApplicationId, branchName, isFileLock, true); - } - - /** - * Get the status of the mentioned branch - * - * @param defaultApplicationId root/default application - * @param branchName for which the status is required - * @param isFileLock if the locking is required, since the status API is used in the other flows of git - * Only for the direct hits from the client the locking will be added - * @return Map of json file names which are added, modified, conflicting, removed and the working tree if this is clean - */ - private Mono getStatus( - String defaultApplicationId, String branchName, boolean isFileLock, boolean compareRemote) { - - if (StringUtils.isEmptyOrNull(branchName)) { - return Mono.error(new AppsmithException(AppsmithError.INVALID_PARAMETER, FieldName.BRANCH_NAME)); - } - - final String finalBranchName = branchName.replaceFirst("origin/", ""); - Mono branchedAppMono = applicationService - .findByBranchNameAndDefaultApplicationId( - finalBranchName, defaultApplicationId, applicationPermission.getEditPermission()) - .onErrorResume(error -> { - // if the branch does not exist in local, checkout remote branch - // Why would we require this is another question - return Mono.defer(() -> checkoutBranch(defaultApplicationId, finalBranchName, false)); - }) - .switchIfEmpty(Mono.error(new AppsmithException(AppsmithError.GIT_GENERIC_ERROR))) - .cache(); - - /* - 1. Copy resources from DB to local repo - 2. Fetch the current status from local repo - */ - Mono statusMono = getGitApplicationMetadata(defaultApplicationId) - .zipWhen(gitArtifactMetadata -> branchedAppMono) - .flatMap(tuple2 -> { - GitArtifactMetadata gitArtifactMetadata = tuple2.getT1(); - Application branchedApplication = tuple2.getT2(); - Mono exportAppMono = exportService - .exportByArtifactId(branchedApplication.getId(), VERSION_CONTROL, APPLICATION) - .map(artifactExchangeJson -> (ApplicationJson) artifactExchangeJson); - - return Mono.zip(Mono.just(gitArtifactMetadata), Mono.just(branchedApplication), exportAppMono); - }) - .flatMap(tuple3 -> { - Mono fileLockMono = Mono.empty(); - if (isFileLock) { - fileLockMono = Mono.defer(() -> addFileLock(defaultApplicationId, GitCommandConstants.STATUS)); - } - - return fileLockMono.thenReturn(tuple3); - }) - .flatMap(tuple3 -> { - GitArtifactMetadata defaultApplicationMetadata = tuple3.getT1(); - Application application = tuple3.getT2(); - ApplicationJson applicationJson = tuple3.getT3(); - - GitArtifactMetadata gitData = application.getGitApplicationMetadata(); - gitData.setGitAuth(defaultApplicationMetadata.getGitAuth()); - Path repoSuffix = Paths.get( - application.getWorkspaceId(), gitData.getDefaultApplicationId(), gitData.getRepoName()); - - try { - // Create a Mono to fetch the status from remote - Path repoSuffixForFetchRemote = Paths.get( - application.getWorkspaceId(), gitData.getDefaultApplicationId(), gitData.getRepoName()); - GitAuth gitAuth = gitData.getGitAuth(); - Mono fetchRemoteMono; - - if (compareRemote) { - fetchRemoteMono = Mono.defer(() -> gitExecutor.fetchRemote( - repoSuffixForFetchRemote, - gitAuth.getPublicKey(), - gitAuth.getPrivateKey(), - false, - branchName, - false)) - .onErrorResume(error -> Mono.error(new AppsmithException( - AppsmithError.GIT_GENERIC_ERROR, error.getMessage()))); - } else { - fetchRemoteMono = Mono.just("ignored"); - } - return Mono.zip( - fileUtils.saveApplicationToLocalRepoWithAnalytics( - repoSuffix, applicationJson, finalBranchName), - Mono.just(repoSuffix), - fetchRemoteMono); - } catch (IOException | GitAPIException e) { - return Mono.error( - new AppsmithException(AppsmithError.GIT_ACTION_FAILED, "status", e.getMessage())); - } - }) - .flatMap(tuple3 -> { - return gitExecutor - .getStatus(tuple3.getT1(), finalBranchName) - .flatMap(result -> { - // Remove any files which are copied by hard resetting the repo - try { - return gitExecutor - .resetToLastCommit(tuple3.getT2(), branchName) - .thenReturn(result); - } catch (Exception e) { - log.error( - "failed to reset to last commit for application: {}, branch: {}", - defaultApplicationId, - branchName, - e); - return Mono.error(new AppsmithException( - AppsmithError.GIT_ACTION_FAILED, "status", e.getMessage())); - } - }); - }) - .flatMap(result -> { - // release the lock if there's a successful response - if (isFileLock) { - return releaseFileLock(defaultApplicationId).thenReturn(result); - } - return Mono.just(result); - }) - .onErrorResume(throwable -> { - /* - in case of any error, the global exception handler will release the lock - hence we don't need to do that manually - */ - log.error( - "Error to get status for application: {}, branch: {}", - defaultApplicationId, - branchName, - throwable); - return Mono.error(new AppsmithException(AppsmithError.GIT_GENERIC_ERROR, throwable.getMessage())); - }) - .name(OPS_STATUS) - .tap(Micrometer.observation(observationRegistry)); - - return Mono.zip(statusMono, sessionUserService.getCurrentUser(), branchedAppMono) - .elapsed() - .flatMap(objects -> { - Long elapsedTime = objects.getT1(); - GitStatusDTO gitStatusDTO = objects.getT2().getT1(); - User currentUser = objects.getT2().getT2(); - Application app = objects.getT2().getT3(); - String flowName; - if (compareRemote) { - flowName = AnalyticsEvents.GIT_STATUS.getEventName(); - } else { - flowName = AnalyticsEvents.GIT_STATUS_WITHOUT_FETCH.getEventName(); - } - return sendUnitExecutionTimeAnalyticsEvent(flowName, elapsedTime, currentUser, app) - .thenReturn(gitStatusDTO); - }); - } - - private Mono sendUnitExecutionTimeAnalyticsEvent( - String flowName, Long elapsedTime, User currentUser, Application app) { - final Map data = Map.of( - FieldName.FLOW_NAME, - flowName, - FieldName.APPLICATION_ID, - app.getGitApplicationMetadata().getDefaultApplicationId(), - "appId", - app.getGitApplicationMetadata().getDefaultApplicationId(), - FieldName.BRANCH_NAME, - app.getGitApplicationMetadata().getBranchName(), - "organizationId", - app.getWorkspaceId(), - "repoUrl", - app.getGitApplicationMetadata().getRemoteUrl(), - "executionTime", - elapsedTime); - return analyticsService.sendEvent( - AnalyticsEvents.UNIT_EXECUTION_TIME.getEventName(), currentUser.getUsername(), data); - } - - @Override - public Mono getStatus(String defaultApplicationId, boolean compareRemote, String branchName) { - return getStatus(defaultApplicationId, branchName, true, compareRemote); - } - - /** - * This method is responsible to compare the current branch with the remote branch. - * Comparing means finding two numbers - how many commits ahead and behind the local branch is. - * It'll do the following things - - * 1. Checkout (if required) to the branch to make sure we are comparing the right branch - * 2. Run a git fetch command to fetch the latest changes from the remote - * - * @param defaultApplicationId Default application id - * @param branchName name of the branch to compare with remote - * @param isFileLock whether to add file lock or not - * @return Mono of {@link BranchTrackingStatus} - */ - @Override - public Mono fetchRemoteChanges( - String defaultApplicationId, String branchName, boolean isFileLock) { - if (StringUtils.isEmptyOrNull(branchName)) { - return Mono.error(new AppsmithException(AppsmithError.INVALID_PARAMETER, FieldName.BRANCH_NAME)); - } - final String finalBranchName = branchName.replaceFirst("origin/", ""); - - Mono applicationMono = applicationService - .findByBranchNameAndDefaultApplicationId( - finalBranchName, defaultApplicationId, applicationPermission.getEditPermission()) - .cache(); // caching as it'll be also used when sending analytics - Mono currUserMono = sessionUserService.getCurrentUser(); // will be used to send analytics event - Mono fetchRemoteStatusMono = getGitApplicationMetadata(defaultApplicationId) - .flatMap(gitApplicationMetadata -> { - if (isFileLock) { - // Add file lock to avoid sending wrong info on the status - return addFileLock( - gitApplicationMetadata.getDefaultApplicationId(), - GitCommandConstants.FETCH_REMOTE) - .then(Mono.zip(Mono.just(gitApplicationMetadata), applicationMono)); - } - return Mono.zip(Mono.just(gitApplicationMetadata), applicationMono); - }) - .flatMap(tuple -> { - GitArtifactMetadata defaultApplicationMetadata = tuple.getT1(); - Application application = tuple.getT2(); - GitArtifactMetadata gitData = application.getGitApplicationMetadata(); - gitData.setGitAuth(defaultApplicationMetadata.getGitAuth()); - Path repoSuffix = Paths.get( - application.getWorkspaceId(), gitData.getDefaultApplicationId(), gitData.getRepoName()); - Path repoPath = gitExecutor.createRepoPath(repoSuffix); - - Mono checkoutBranchMono = gitExecutor.checkoutToBranch(repoSuffix, finalBranchName); - Mono fetchRemoteMono = gitExecutor.fetchRemote( - repoPath, - gitData.getGitAuth().getPublicKey(), - gitData.getGitAuth().getPrivateKey(), - true, - finalBranchName, - false); - Mono branchedStatusMono = - gitExecutor.getBranchTrackingStatus(repoPath, finalBranchName); - - return checkoutBranchMono - .then(fetchRemoteMono) - .then(branchedStatusMono) - .flatMap(branchTrackingStatus -> { - if (isFileLock) { - return releaseFileLock(defaultApplicationId).thenReturn(branchTrackingStatus); - } - return Mono.just(branchTrackingStatus); - }) - .onErrorResume(throwable -> { - /* - in case of any error, the global exception handler will release the lock - hence we don't need to do that manually - */ - log.error( - "Error to fetch from remote for application: {}, branch: {}", - defaultApplicationId, - branchName, - throwable); - return Mono.error(new AppsmithException( - AppsmithError.GIT_ACTION_FAILED, "fetch", throwable.getMessage())); - }); - }) - .elapsed() - .zipWith(Mono.zip(currUserMono, applicationMono)) - .flatMap(objects -> { - Long elapsedTime = objects.getT1().getT1(); - BranchTrackingStatus branchTrackingStatus = objects.getT1().getT2(); - User currentUser = objects.getT2().getT1(); - Application app = objects.getT2().getT2(); - return sendUnitExecutionTimeAnalyticsEvent( - AnalyticsEvents.GIT_FETCH.getEventName(), elapsedTime, currentUser, app) - .thenReturn(branchTrackingStatus); - }) - .name(GitSpan.OPS_FETCH_REMOTE) - .tap(Micrometer.observation(observationRegistry)); - - return Mono.create(sink -> { - fetchRemoteStatusMono.subscribe(sink::success, sink::error, null, sink.currentContext()); - }); - } - - @Override - public Mono mergeBranch(String defaultApplicationId, GitMergeDTO gitMergeDTO) { - /* - * 1.Dehydrate the application from Mongodb so that the file system has the latest application data for both the source and destination branch application - * 2.Do git checkout destinationBranch ---> git merge sourceBranch after the rehydration - * On Merge conflict - create new branch and push the changes to remote and ask the user to resolve it on Github/Gitlab UI - * 3.Then rehydrate from the file system to mongodb so that the latest changes from remote are rendered to the application - * 4.Get the latest application mono from the mongodb and send it back to client - * */ - - final String sourceBranch = gitMergeDTO.getSourceBranch(); - final String destinationBranch = gitMergeDTO.getDestinationBranch(); - - if (StringUtils.isEmptyOrNull(sourceBranch) || StringUtils.isEmptyOrNull(destinationBranch)) { - return Mono.error(new AppsmithException(AppsmithError.INVALID_PARAMETER, FieldName.BRANCH_NAME)); - } else if (sourceBranch.startsWith("origin/")) { - return Mono.error( - new AppsmithException(AppsmithError.UNSUPPORTED_OPERATION_FOR_REMOTE_BRANCH, sourceBranch)); - } else if (destinationBranch.startsWith("origin/")) { - return Mono.error( - new AppsmithException(AppsmithError.UNSUPPORTED_OPERATION_FOR_REMOTE_BRANCH, destinationBranch)); - } - - Mono mergeMono = getApplicationById( - defaultApplicationId, applicationPermission.getEditPermission()) - .flatMap(application -> { - GitArtifactMetadata gitData = application.getGitApplicationMetadata(); - return addFileLock(gitData.getDefaultApplicationId(), GitCommandConstants.MERGE_BRANCH) - .then(Mono.just(application)); - }) - .flatMap(defaultApplication -> { - GitArtifactMetadata gitArtifactMetadata = defaultApplication.getGitApplicationMetadata(); - if (isInvalidDefaultApplicationGitMetadata(defaultApplication.getGitApplicationMetadata())) { - return Mono.error(new AppsmithException(AppsmithError.INVALID_GIT_SSH_CONFIGURATION)); - } - Path repoSuffix = Paths.get( - defaultApplication.getWorkspaceId(), - gitArtifactMetadata.getDefaultApplicationId(), - gitArtifactMetadata.getRepoName()); - - // 1. Hydrate from db to file system for both branch Applications - Mono pathToFile = this.getStatus(defaultApplicationId, sourceBranch, false) - .flatMap(status -> { - if (!Integer.valueOf(0).equals(status.getBehindCount())) { - return Mono.error(new AppsmithException( - AppsmithError.GIT_MERGE_FAILED_REMOTE_CHANGES, - status.getBehindCount(), - sourceBranch)); - } else if (!status.getIsClean()) { - return Mono.error(new AppsmithException( - AppsmithError.GIT_MERGE_FAILED_LOCAL_CHANGES, sourceBranch)); - } - return this.getStatus(defaultApplicationId, destinationBranch, false) - .map(status1 -> { - if (!Integer.valueOf(0).equals(status.getBehindCount())) { - return Mono.error(new AppsmithException( - AppsmithError.GIT_MERGE_FAILED_REMOTE_CHANGES, - status.getBehindCount(), - destinationBranch)); - } else if (!status.getIsClean()) { - return Mono.error(new AppsmithException( - AppsmithError.GIT_MERGE_FAILED_LOCAL_CHANGES, - destinationBranch)); - } - return status1; - }); - }) - .thenReturn(repoSuffix); - - return Mono.zip(Mono.just(defaultApplication), pathToFile).onErrorResume(error -> { - log.error("Error in repo status check for application " + defaultApplicationId, error); - if (error instanceof AppsmithException) { - return Mono.error(error); - } - return Mono.error(new AppsmithException(AppsmithError.GIT_ACTION_FAILED, "status", error)); - }); - }) - .flatMap(tuple -> { - Application defaultApplication = tuple.getT1(); - Path repoSuffix = tuple.getT2(); - - // 2. git checkout destinationBranch ---> git merge sourceBranch - return Mono.zip( - gitExecutor.mergeBranch(repoSuffix, sourceBranch, destinationBranch), - Mono.just(defaultApplication)) - .onErrorResume(error -> addAnalyticsForGitOperation( - AnalyticsEvents.GIT_MERGE, - defaultApplication, - error.getClass().getName(), - error.getMessage(), - defaultApplication - .getGitApplicationMetadata() - .getIsRepoPrivate()) - .flatMap(application -> { - if (error instanceof GitAPIException) { - return Mono.error(new AppsmithException( - AppsmithError.GIT_MERGE_CONFLICTS, error.getMessage())); - } - return Mono.error(new AppsmithException( - AppsmithError.GIT_ACTION_FAILED, "merge", error.getMessage())); - })); - }) - .flatMap(mergeStatusTuple -> { - Application defaultApplication = mergeStatusTuple.getT2(); - String mergeStatus = mergeStatusTuple.getT1(); - - // 3. rehydrate from file system to db - Mono applicationJson = - fileUtils.reconstructApplicationJsonFromGitRepoWithAnalytics( - defaultApplication.getWorkspaceId(), - defaultApplication - .getGitApplicationMetadata() - .getDefaultApplicationId(), - defaultApplication - .getGitApplicationMetadata() - .getRepoName(), - destinationBranch); - return Mono.zip( - Mono.just(mergeStatus), - applicationService.findByBranchNameAndDefaultApplicationId( - destinationBranch, defaultApplicationId, applicationPermission.getEditPermission()), - applicationJson); - }) - .flatMap(tuple -> { - Application destApplication = tuple.getT2(); - ApplicationJson applicationJson = tuple.getT3(); - MergeStatusDTO mergeStatusDTO = new MergeStatusDTO(); - mergeStatusDTO.setStatus(tuple.getT1()); - mergeStatusDTO.setMergeAble(TRUE); - - // 4. Get the latest application mono with all the changes - return importService - .importArtifactInWorkspaceFromGit( - destApplication.getWorkspaceId(), - destApplication.getId(), - applicationJson, - destinationBranch.replaceFirst("origin/", "")) - .map(importableArtifact -> (Application) importableArtifact) - .flatMap(application1 -> { - GitCommitDTO commitDTO = new GitCommitDTO(); - commitDTO.setDoPush(true); - commitDTO.setCommitMessage(DEFAULT_COMMIT_MESSAGE - + GitDefaultCommitMessage.SYNC_REMOTE_AFTER_MERGE.getReason() - + sourceBranch); - return this.commitApplication(commitDTO, defaultApplicationId, destinationBranch) - .map(commitStatus -> mergeStatusDTO) - .zipWith(Mono.just(application1)); - }); - }) - .flatMap(tuple -> { - MergeStatusDTO mergeStatusDTO = tuple.getT1(); - Application application = tuple.getT2(); - // Send analytics event - return releaseFileLock(defaultApplicationId).flatMap(status -> addAnalyticsForGitOperation( - AnalyticsEvents.GIT_MERGE, - application, - application.getGitApplicationMetadata().getIsRepoPrivate()) - .thenReturn(mergeStatusDTO)); - }); - - return Mono.create(sink -> mergeMono.subscribe(sink::success, sink::error, null, sink.currentContext())); - } - - @Override - public Mono isBranchMergeable(String defaultApplicationId, GitMergeDTO gitMergeDTO) { - - final String sourceBranch = gitMergeDTO.getSourceBranch(); - final String destinationBranch = gitMergeDTO.getDestinationBranch(); - - if (StringUtils.isEmptyOrNull(sourceBranch) || StringUtils.isEmptyOrNull(destinationBranch)) { - return Mono.error(new AppsmithException(AppsmithError.INVALID_PARAMETER, FieldName.BRANCH_NAME)); - } else if (sourceBranch.startsWith("origin/")) { - return Mono.error( - new AppsmithException(AppsmithError.UNSUPPORTED_OPERATION_FOR_REMOTE_BRANCH, sourceBranch)); - } else if (destinationBranch.startsWith("origin/")) { - return Mono.error( - new AppsmithException(AppsmithError.UNSUPPORTED_OPERATION_FOR_REMOTE_BRANCH, destinationBranch)); - } - - Mono mergeableStatusMono = getApplicationById( - defaultApplicationId, applicationPermission.getEditPermission()) - .flatMap(application -> { - GitArtifactMetadata gitArtifactMetadata = application.getGitApplicationMetadata(); - if (isInvalidDefaultApplicationGitMetadata(application.getGitApplicationMetadata())) { - return Mono.error(new AppsmithException(AppsmithError.INVALID_GIT_SSH_CONFIGURATION)); - } - Path repoSuffix = Paths.get( - application.getWorkspaceId(), - gitArtifactMetadata.getDefaultApplicationId(), - gitArtifactMetadata.getRepoName()); - - // 1. Hydrate from db to file system for both branch Applications - // Update function call - return addFileLock(defaultApplicationId, GitCommandConstants.STATUS) - .flatMap(status -> this.getStatus(defaultApplicationId, sourceBranch, false)) - .flatMap(srcBranchStatus -> { - if (!Integer.valueOf(0).equals(srcBranchStatus.getBehindCount())) { - return addAnalyticsForGitOperation( - AnalyticsEvents.GIT_MERGE_CHECK, - application, - AppsmithError.GIT_MERGE_FAILED_LOCAL_CHANGES.name(), - AppsmithError.GIT_MERGE_FAILED_LOCAL_CHANGES.getMessage( - srcBranchStatus.getBehindCount(), destinationBranch), - application - .getGitApplicationMetadata() - .getIsRepoPrivate(), - false, - false) - .then(Mono.error(Exceptions.propagate(new AppsmithException( - AppsmithError.GIT_MERGE_FAILED_REMOTE_CHANGES, - srcBranchStatus.getBehindCount(), - sourceBranch)))); - } else if (!srcBranchStatus.getIsClean()) { - return addAnalyticsForGitOperation( - AnalyticsEvents.GIT_MERGE_CHECK, - application, - AppsmithError.GIT_MERGE_FAILED_LOCAL_CHANGES.name(), - AppsmithError.GIT_MERGE_FAILED_LOCAL_CHANGES.getMessage( - destinationBranch), - application - .getGitApplicationMetadata() - .getIsRepoPrivate(), - false, - false) - .then(Mono.error(Exceptions.propagate(new AppsmithException( - AppsmithError.GIT_MERGE_FAILED_LOCAL_CHANGES, sourceBranch)))); - } - return this.getStatus(defaultApplicationId, destinationBranch, false) - .map(destBranchStatus -> { - if (!Integer.valueOf(0).equals(destBranchStatus.getBehindCount())) { - return addAnalyticsForGitOperation( - AnalyticsEvents.GIT_MERGE_CHECK, - application, - AppsmithError.GIT_MERGE_FAILED_REMOTE_CHANGES.name(), - AppsmithError.GIT_MERGE_FAILED_REMOTE_CHANGES - .getMessage( - destBranchStatus.getBehindCount(), - destinationBranch), - application - .getGitApplicationMetadata() - .getIsRepoPrivate(), - false, - false) - .then(Mono.error(Exceptions.propagate(new AppsmithException( - AppsmithError.GIT_MERGE_FAILED_REMOTE_CHANGES, - destBranchStatus.getBehindCount(), - destinationBranch)))); - } else if (!destBranchStatus.getIsClean()) { - return addAnalyticsForGitOperation( - AnalyticsEvents.GIT_MERGE_CHECK, - application, - AppsmithError.GIT_MERGE_FAILED_LOCAL_CHANGES.name(), - AppsmithError.GIT_MERGE_FAILED_LOCAL_CHANGES.getMessage( - destinationBranch), - application - .getGitApplicationMetadata() - .getIsRepoPrivate(), - false, - false) - .then(Mono.error(Exceptions.propagate(new AppsmithException( - AppsmithError.GIT_MERGE_FAILED_LOCAL_CHANGES, - destinationBranch)))); - } - return destBranchStatus; - }); - }) - .onErrorResume(error -> { - log.error("Error in merge status check application " + defaultApplicationId, error); - if (error instanceof AppsmithException) { - return Mono.error(error); - } - return Mono.error( - new AppsmithException(AppsmithError.GIT_ACTION_FAILED, "status", error)); - }) - .then(gitExecutor - .isMergeBranch(repoSuffix, sourceBranch, destinationBranch) - .flatMap(mergeStatusDTO -> releaseFileLock(defaultApplicationId) - .flatMap(mergeStatus -> addAnalyticsForGitOperation( - AnalyticsEvents.GIT_MERGE_CHECK, - application, - null, - null, - application - .getGitApplicationMetadata() - .getIsRepoPrivate(), - false, - mergeStatusDTO.isMergeAble())) - .then(Mono.just(mergeStatusDTO)))) - .onErrorResume(error -> { - try { - return gitExecutor - .resetToLastCommit(repoSuffix, destinationBranch) - .map(reset -> { - MergeStatusDTO mergeStatus = new MergeStatusDTO(); - mergeStatus.setMergeAble(false); - mergeStatus.setStatus("Merge check failed!"); - mergeStatus.setMessage(error.getMessage()); - if (error instanceof CheckoutConflictException) { - mergeStatus.setConflictingFiles( - ((CheckoutConflictException) error).getConflictingPaths()); - } - mergeStatus.setReferenceDoc( - ErrorReferenceDocUrl.GIT_MERGE_CONFLICT.getDocUrl()); - return mergeStatus; - }) - .flatMap(mergeStatusDTO -> addAnalyticsForGitOperation( - AnalyticsEvents.GIT_MERGE_CHECK, - application, - error.getClass().getName(), - error.getMessage(), - application - .getGitApplicationMetadata() - .getIsRepoPrivate(), - false, - false) - .map(application1 -> mergeStatusDTO)); - } catch (GitAPIException | IOException e) { - log.error("Error while resetting to last commit", e); - return Mono.error(new AppsmithException( - AppsmithError.GIT_ACTION_FAILED, "reset --hard HEAD", e.getMessage())); - } - }); - }); - - return Mono.create( - sink -> mergeableStatusMono.subscribe(sink::success, sink::error, null, sink.currentContext())); - } - - @Override - public Mono createConflictedBranch(String defaultApplicationId, String branchName) { - if (StringUtils.isEmptyOrNull(branchName)) { - return Mono.error(new AppsmithException(AppsmithError.INVALID_PARAMETER, FieldName.BRANCH_NAME)); - } - - Mono conflictedBranchMono = Mono.zip( - getGitApplicationMetadata(defaultApplicationId), - applicationService - .findByBranchNameAndDefaultApplicationId( - branchName, defaultApplicationId, applicationPermission.getEditPermission()) - .zipWhen(application -> exportService - .exportByArtifactId(application.getId(), VERSION_CONTROL, APPLICATION) - .map(artifactExchangeJson -> (ApplicationJson) artifactExchangeJson))) - .flatMap(tuple -> { - GitArtifactMetadata defaultApplicationMetadata = tuple.getT1(); - Application application = tuple.getT2().getT1(); - ApplicationJson applicationJson = tuple.getT2().getT2(); - GitArtifactMetadata gitData = application.getGitApplicationMetadata(); - gitData.setGitAuth(defaultApplicationMetadata.getGitAuth()); - Path repoSuffix = Paths.get( - application.getWorkspaceId(), gitData.getDefaultApplicationId(), gitData.getRepoName()); - - try { - return Mono.zip( - fileUtils.saveApplicationToLocalRepoWithAnalytics( - repoSuffix, applicationJson, branchName), - Mono.just(gitData), - Mono.just(repoSuffix)); - } catch (IOException | GitAPIException e) { - return Mono.error( - new AppsmithException(AppsmithError.GIT_ACTION_FAILED, "checkout", e.getMessage())); - } - }) - .flatMap(tuple -> { - GitArtifactMetadata gitData = tuple.getT2(); - Path repoSuffix = tuple.getT3(); - return gitExecutor - .createAndCheckoutToBranch(repoSuffix, branchName + MERGE_CONFLICT_BRANCH_NAME) - .flatMap(conflictedBranchName -> commitAndPushWithDefaultCommit( - repoSuffix, - gitData.getGitAuth(), - gitData, - GitDefaultCommitMessage.CONFLICT_STATE) - .flatMap(successMessage -> gitExecutor.checkoutToBranch(repoSuffix, branchName)) - .flatMap(isCheckedOut -> gitExecutor.deleteBranch(repoSuffix, conflictedBranchName)) - .thenReturn(conflictedBranchName + CONFLICTED_SUCCESS_MESSAGE)); - }); - - return Mono.create( - sink -> conflictedBranchMono.subscribe(sink::success, sink::error, null, sink.currentContext())); - } - - @Override - public Mono importApplicationFromGit(String workspaceId, GitConnectDTO gitConnectDTO) { - // 1. Check private repo limit for workspace - // 2. Create dummy application, clone repo from remote - // 3. Re-hydrate application to DB from local repo - // 1. Save the ssh keys in application object with other details - // 2. During import-export need to handle the DS(empty vs non-empty) - // 4. Return application - - if (StringUtils.isEmptyOrNull(gitConnectDTO.getRemoteUrl())) { - return Mono.error(new AppsmithException(AppsmithError.INVALID_PARAMETER, "Remote Url")); - } - - if (StringUtils.isEmptyOrNull(workspaceId)) { - return Mono.error(new AppsmithException(AppsmithError.INVALID_PARAMETER, "Invalid workspace id")); - } - - Mono workspaceMono = workspaceService - .findById(workspaceId, AclPermission.WORKSPACE_CREATE_APPLICATION) - .switchIfEmpty(Mono.error( - new AppsmithException(AppsmithError.NO_RESOURCE_FOUND, FieldName.WORKSPACE, workspaceId))); - - final String repoName = GitUtils.getRepoName(gitConnectDTO.getRemoteUrl()); - Mono isPrivateRepoMono = GitUtils.isRepoPrivate( - GitUtils.convertSshUrlToBrowserSupportedUrl(gitConnectDTO.getRemoteUrl())) - .cache(); - Mono importedApplicationMono = workspaceMono - .then(getSSHKeyForCurrentUser()) - .zipWith(isPrivateRepoMono) - .switchIfEmpty( - Mono.error( - new AppsmithException( - AppsmithError.INVALID_GIT_CONFIGURATION, - "Unable to find git configuration for logged-in user. Please contact Appsmith team for support"))) - // Check the limit for number of private repo - .flatMap(tuple -> { - // Check if the repo is public - Application newApplication = new Application(); - newApplication.setName(repoName); - newApplication.setWorkspaceId(workspaceId); - newApplication.setGitApplicationMetadata(new GitArtifactMetadata()); - GitAuth gitAuth = tuple.getT1(); - boolean isRepoPrivate = tuple.getT2(); - Mono applicationMono = applicationPageService.createOrUpdateSuffixedApplication( - newApplication, newApplication.getName(), 0); - if (!isRepoPrivate) { - return Mono.just(gitAuth).zipWith(applicationMono); - } - return gitPrivateRepoHelper - .isRepoLimitReached(workspaceId, true) - .flatMap(isRepoLimitReached -> { - if (FALSE.equals(isRepoLimitReached)) { - return Mono.just(gitAuth).zipWith(applicationMono); - } - return addAnalyticsForGitOperation( - AnalyticsEvents.GIT_IMPORT, - newApplication, - AppsmithError.GIT_APPLICATION_LIMIT_ERROR.getErrorType(), - AppsmithError.GIT_APPLICATION_LIMIT_ERROR.getMessage(), - true) - .flatMap(user -> Mono.error( - new AppsmithException(AppsmithError.GIT_APPLICATION_LIMIT_ERROR))); - }); - }) - .flatMap(tuple -> { - GitAuth gitAuth = tuple.getT1(); - Application application = tuple.getT2(); - Path repoSuffix = Paths.get(application.getWorkspaceId(), application.getId(), repoName); - Mono> profileMono = - updateOrCreateGitProfileForCurrentUser(gitConnectDTO.getGitProfile(), application.getId()); - - Mono defaultBranchMono = gitExecutor - .cloneRemoteIntoArtifactRepo( - repoSuffix, - gitConnectDTO.getRemoteUrl(), - gitAuth.getPrivateKey(), - gitAuth.getPublicKey()) - .onErrorResume(error -> { - log.error("Error while cloning the remote repo, {}", error.getMessage()); - return addAnalyticsForGitOperation( - AnalyticsEvents.GIT_IMPORT, - application, - error.getClass().getName(), - error.getMessage(), - false) - .flatMap(user -> fileUtils - .deleteLocalRepo(repoSuffix) - .then(applicationPageService.deleteApplication(application.getId()))) - .flatMap(application1 -> { - if (error instanceof TransportException) { - return Mono.error(new AppsmithException( - AppsmithError.INVALID_GIT_SSH_CONFIGURATION)); - } else if (error instanceof InvalidRemoteException) { - return Mono.error(new AppsmithException( - AppsmithError.INVALID_PARAMETER, "remote url")); - } else if (error instanceof TimeoutException) { - return Mono.error( - new AppsmithException(AppsmithError.GIT_EXECUTION_TIMEOUT)); - } - return Mono.error(new AppsmithException( - AppsmithError.GIT_ACTION_FAILED, "clone", error)); - }); - }); - - return defaultBranchMono.zipWith(isPrivateRepoMono).flatMap(tuple2 -> { - String defaultBranch = tuple2.getT1(); - boolean isRepoPrivate = tuple2.getT2(); - GitArtifactMetadata gitArtifactMetadata = new GitArtifactMetadata(); - gitArtifactMetadata.setGitAuth(gitAuth); - gitArtifactMetadata.setDefaultApplicationId(application.getId()); - gitArtifactMetadata.setBranchName(defaultBranch); - gitArtifactMetadata.setDefaultBranchName(defaultBranch); - gitArtifactMetadata.setRemoteUrl(gitConnectDTO.getRemoteUrl()); - gitArtifactMetadata.setRepoName(repoName); - gitArtifactMetadata.setBrowserSupportedRemoteUrl( - GitUtils.convertSshUrlToBrowserSupportedUrl(gitConnectDTO.getRemoteUrl())); - gitArtifactMetadata.setIsRepoPrivate(isRepoPrivate); - gitArtifactMetadata.setLastCommittedAt(Instant.now()); - - application.setGitApplicationMetadata(gitArtifactMetadata); - return Mono.just(application).zipWith(profileMono); - }); - }) - .flatMap(objects -> { - Application application = objects.getT1(); - GitArtifactMetadata gitArtifactMetadata = application.getGitApplicationMetadata(); - String defaultBranch = gitArtifactMetadata.getDefaultBranchName(); - - Mono> datasourceMono = datasourceService - .getAllByWorkspaceIdWithStorages(workspaceId, datasourcePermission.getEditPermission()) - .collectList(); - Mono> pluginMono = - pluginService.getDefaultPlugins().collectList(); - Mono applicationJsonMono = fileUtils - .reconstructApplicationJsonFromGitRepoWithAnalytics( - workspaceId, application.getId(), gitArtifactMetadata.getRepoName(), defaultBranch) - .onErrorResume(error -> { - log.error("Error while constructing application from git repo", error); - return deleteApplicationCreatedFromGitImport( - application.getId(), - application.getWorkspaceId(), - gitArtifactMetadata.getRepoName()) - .flatMap(application1 -> Mono.error(new AppsmithException( - AppsmithError.GIT_FILE_SYSTEM_ERROR, error.getMessage()))); - }); - - return Mono.zip(applicationJsonMono, datasourceMono, pluginMono) - .flatMap(data -> { - ApplicationJson applicationJson = data.getT1(); - List datasourceList = data.getT2(); - List pluginList = data.getT3(); - - if (Optional.ofNullable(applicationJson.getExportedApplication()) - .isEmpty() - || applicationJson.getPageList().isEmpty()) { - return deleteApplicationCreatedFromGitImport( - application.getId(), - application.getWorkspaceId(), - gitArtifactMetadata.getRepoName()) - .then(Mono.error(new AppsmithException( - AppsmithError.GIT_ACTION_FAILED, - "import", - "Cannot import app from an empty repo"))); - } - - // If there is an existing datasource with the same name but a different type from that - // in the repo, the import api should fail - if (checkIsDatasourceNameConflict( - datasourceList, applicationJson.getDatasourceList(), pluginList)) { - return deleteApplicationCreatedFromGitImport( - application.getId(), - application.getWorkspaceId(), - gitArtifactMetadata.getRepoName()) - .then(Mono.error(new AppsmithException( - AppsmithError.GIT_ACTION_FAILED, - "import", - "Datasource already exists with the same name"))); - } - - applicationJson.getExportedApplication().setGitApplicationMetadata(gitArtifactMetadata); - return importService - .importArtifactInWorkspaceFromGit( - workspaceId, application.getId(), applicationJson, defaultBranch) - .map(importableArtifact -> (Application) importableArtifact) - .onErrorResume(throwable -> deleteApplicationCreatedFromGitImport( - application.getId(), - application.getWorkspaceId(), - gitArtifactMetadata.getRepoName()) - .flatMap(application1 -> Mono.error(new AppsmithException( - AppsmithError.GIT_FILE_SYSTEM_ERROR, throwable.getMessage())))); - }); - }) - .flatMap(application -> applicationPageService.publish(application.getId(), false)) - // Add un-configured datasource to the list to response - .flatMap(application -> importService.getArtifactImportDTO( - application.getWorkspaceId(), application.getId(), application, APPLICATION)) - .map(importableArtifactDTO -> (ApplicationImportDTO) importableArtifactDTO) - // Add analytics event - .flatMap(applicationImportDTO -> { - Application application = applicationImportDTO.getApplication(); - return addAnalyticsForGitOperation( - AnalyticsEvents.GIT_IMPORT, - application, - application.getGitApplicationMetadata().getIsRepoPrivate()) - .thenReturn(applicationImportDTO); - }); - - return Mono.create( - sink -> importedApplicationMono.subscribe(sink::success, sink::error, null, sink.currentContext())); - } - - @Override - public Mono generateSSHKey(String keyType) { - GitAuth gitAuth = GitDeployKeyGenerator.generateSSHKey(keyType); - - GitDeployKeys gitDeployKeys = new GitDeployKeys(); - gitDeployKeys.setGitAuth(gitAuth); - - return sessionUserService - .getCurrentUser() - .flatMap(user -> { - gitDeployKeys.setEmail(user.getEmail()); - return gitDeployKeysRepository - .findByEmail(user.getEmail()) - .switchIfEmpty(gitDeployKeysRepository.save(gitDeployKeys)) - .flatMap(gitDeployKeys1 -> { - if (gitDeployKeys.equals(gitDeployKeys1)) { - return Mono.just(gitDeployKeys1); - } - // Overwrite the existing keys - gitDeployKeys1.setGitAuth(gitDeployKeys.getGitAuth()); - return gitDeployKeysRepository.save(gitDeployKeys1); - }); - }) - .thenReturn(gitAuth); - } - - @Override - public Mono testConnection(String defaultApplicationId) { - return getApplicationById(defaultApplicationId, applicationPermission.getEditPermission()) - .flatMap(application -> { - GitArtifactMetadata gitArtifactMetadata = application.getGitApplicationMetadata(); - if (isInvalidDefaultApplicationGitMetadata(gitArtifactMetadata)) { - return Mono.error(new AppsmithException(AppsmithError.INVALID_GIT_SSH_CONFIGURATION)); - } - return gitExecutor - .testConnection( - gitArtifactMetadata.getGitAuth().getPublicKey(), - gitArtifactMetadata.getGitAuth().getPrivateKey(), - gitArtifactMetadata.getRemoteUrl()) - .zipWith(Mono.just(application)) - .onErrorResume(error -> { - log.error( - "Error while testing the connection to th remote repo " - + gitArtifactMetadata.getRemoteUrl() + " ", - error); - return addAnalyticsForGitOperation( - AnalyticsEvents.GIT_TEST_CONNECTION, - application, - error.getClass().getName(), - error.getMessage(), - application - .getGitApplicationMetadata() - .getIsRepoPrivate()) - .flatMap(application1 -> { - if (error instanceof TransportException) { - return Mono.error(new AppsmithException( - AppsmithError.INVALID_GIT_SSH_CONFIGURATION)); - } - if (error instanceof InvalidRemoteException) { - return Mono.error(new AppsmithException( - AppsmithError.INVALID_GIT_CONFIGURATION, error.getMessage())); - } - if (error instanceof TimeoutException) { - return Mono.error( - new AppsmithException(AppsmithError.GIT_EXECUTION_TIMEOUT)); - } - return Mono.error(new AppsmithException( - AppsmithError.GIT_GENERIC_ERROR, error.getMessage())); - }); - }); - }) - .flatMap(objects -> { - Application application = objects.getT2(); - return addAnalyticsForGitOperation( - AnalyticsEvents.GIT_TEST_CONNECTION, - application, - application.getGitApplicationMetadata().getIsRepoPrivate()) - .thenReturn(objects.getT1()); - }); - } - - @Override - public Mono deleteBranch(String defaultApplicationId, String branchName) { - Mono deleteBranchMono = getApplicationById( - defaultApplicationId, applicationPermission.getEditPermission()) - .zipWhen(application -> - gitPrivateRepoHelper.isBranchProtected(application.getGitApplicationMetadata(), branchName)) - .map(objects -> { - if (objects.getT2()) { - throw new AppsmithException( - AppsmithError.GIT_ACTION_FAILED, - "delete", - "Cannot delete protected branch " + branchName); - } - return objects.getT1(); - }) - .flatMap(application -> addFileLock(defaultApplicationId, GitCommandConstants.DELETE) - .map(status -> application)) - .flatMap(application -> { - GitArtifactMetadata gitArtifactMetadata = application.getGitApplicationMetadata(); - Path repoPath = Paths.get( - application.getWorkspaceId(), defaultApplicationId, gitArtifactMetadata.getRepoName()); - if (branchName.equals(gitArtifactMetadata.getDefaultBranchName())) { - return Mono.error(new AppsmithException( - AppsmithError.GIT_ACTION_FAILED, "delete branch", " Cannot delete default branch")); - } - return gitExecutor - .deleteBranch(repoPath, branchName) - .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())); - }) - .flatMap(isBranchDeleted -> - releaseFileLock(defaultApplicationId).map(status -> isBranchDeleted)) - .flatMap(isBranchDeleted -> { - if (FALSE.equals(isBranchDeleted)) { - return Mono.error(new AppsmithException( - AppsmithError.GIT_ACTION_FAILED, - " delete branch. Branch does not exists in the repo")); - } - return applicationService - .findByBranchNameAndDefaultApplicationId( - branchName, - defaultApplicationId, - applicationPermission.getEditPermission()) - .flatMap(application1 -> { - if (application1 - .getId() - .equals(application1 - .getGitApplicationMetadata() - .getDefaultApplicationId())) { - return Mono.just(application1); - } - return applicationPageService.deleteApplicationByResource(application1); - }) - .onErrorResume(throwable -> { - log.warn("Unable to find branch with name ", throwable); - return addAnalyticsForGitOperation( - AnalyticsEvents.GIT_DELETE_BRANCH, - application, - throwable.getClass().getName(), - throwable.getMessage(), - gitArtifactMetadata.getIsRepoPrivate()) - .flatMap(application1 -> Mono.just(application1)); - }); - }); - }) - .flatMap(application -> addAnalyticsForGitOperation( - AnalyticsEvents.GIT_DELETE_BRANCH, - application, - application.getGitApplicationMetadata().getIsRepoPrivate())) - .map(responseUtils::updateApplicationWithDefaultResources) - .name(GitSpan.OPS_DELETE_BRANCH) - .tap(Micrometer.observation(observationRegistry)); - - return Mono.create(sink -> deleteBranchMono.subscribe(sink::success, sink::error, null, sink.currentContext())); - } - - @Override - public Mono discardChanges(String defaultApplicationId, String branchName) { - - if (StringUtils.isEmptyOrNull(defaultApplicationId)) { - return Mono.error(new AppsmithException(AppsmithError.INVALID_PARAMETER, FieldName.APPLICATION_ID)); - } - Mono branchedApplicationMono = applicationService - .findByBranchNameAndDefaultApplicationId( - branchName, defaultApplicationId, applicationPermission.getEditPermission()) - .cache(); - - Mono discardChangeMono; - - // Rehydrate the application from local file system - discardChangeMono = branchedApplicationMono - // Add file lock before proceeding with the git operation - .flatMap(application -> addFileLock(defaultApplicationId, GitCommandConstants.DISCARD) - .thenReturn(application)) - .flatMap(branchedApplication -> { - GitArtifactMetadata gitData = branchedApplication.getGitApplicationMetadata(); - if (gitData == null || StringUtils.isEmptyOrNull(gitData.getDefaultApplicationId())) { - return Mono.error( - new AppsmithException(AppsmithError.INVALID_GIT_CONFIGURATION, GIT_CONFIG_ERROR)); - } - Path repoSuffix = Paths.get( - branchedApplication.getWorkspaceId(), - gitData.getDefaultApplicationId(), - gitData.getRepoName()); - return gitExecutor - .rebaseBranch(repoSuffix, branchName) - .flatMap(rebaseStatus -> { - return fileUtils.reconstructApplicationJsonFromGitRepoWithAnalytics( - branchedApplication.getWorkspaceId(), - branchedApplication - .getGitApplicationMetadata() - .getDefaultApplicationId(), - branchedApplication - .getGitApplicationMetadata() - .getRepoName(), - branchName); - }) - .onErrorResume(throwable -> { - log.error("Git Discard & Rebase failed {}", throwable.getMessage()); - return Mono.error( - new AppsmithException( - AppsmithError.GIT_ACTION_FAILED, - "discard changes", - "Please create a new branch and resolve the conflicts on remote repository before proceeding ahead.")); - }) - .flatMap(applicationJson -> importService.importArtifactInWorkspaceFromGit( - branchedApplication.getWorkspaceId(), - branchedApplication.getId(), - applicationJson, - branchName)) - // Update the last deployed status after the rebase - .flatMap(application -> publishAndOrGetApplication(application.getId(), true)); - }) - .flatMap(application -> releaseFileLock(defaultApplicationId) - .then(this.addAnalyticsForGitOperation(AnalyticsEvents.GIT_DISCARD_CHANGES, application, null))) - .map(responseUtils::updateApplicationWithDefaultResources) - .name(GitSpan.OPS_DISCARD_CHANGES) - .tap(Micrometer.observation(observationRegistry)); - - return Mono.create( - sink -> discardChangeMono.subscribe(sink::success, sink::error, null, sink.currentContext())); - } - - /** - * In some scenarios: - * connect: after loading the modal, keyTypes is not available, so a network call has to be made to ssh-keypair. - * import: cannot make a ssh-keypair call because application Id doesn’t exist yet, so API fails. - * - * @return Git docs urls for all the scenarios, client will cache this data and use it - */ - @Override - public Mono> getGitDocUrls() { - ErrorReferenceDocUrl[] docSet = ErrorReferenceDocUrl.values(); - List gitDocsDTOList = new ArrayList<>(); - for (ErrorReferenceDocUrl docUrl : docSet) { - GitDocsDTO gitDocsDTO = new GitDocsDTO(); - gitDocsDTO.setDocKey(docUrl); - gitDocsDTO.setDocUrl(docUrl.getDocUrl()); - gitDocsDTOList.add(gitDocsDTO); - } - return Mono.just(gitDocsDTOList); - } - - private Mono deleteApplicationCreatedFromGitImport( - String applicationId, String workspaceId, String repoName) { - Path repoSuffix = Paths.get(workspaceId, applicationId, repoName); - return fileUtils.deleteLocalRepo(repoSuffix).then(applicationPageService.deleteApplication(applicationId)); - } - - private Mono getSSHKeyForCurrentUser() { - return sessionUserService - .getCurrentUser() - .flatMap(user -> gitDeployKeysRepository.findByEmail(user.getEmail())) - .map(GitDeployKeys::getGitAuth); - } - - private boolean checkIsDatasourceNameConflict( - List existingDatasources, - List importedDatasources, - List pluginList) { - // If we have an existing datasource with the same name but a different type from that in the repo, the import - // api should fail - for (DatasourceStorage datasourceStorage : importedDatasources) { - // Collect the datasource(existing in workspace) which has same as of imported datasource - // As names are unique we will need filter first element to check if the plugin id is matched - Datasource filteredDatasource = existingDatasources.stream() - .filter(datasource1 -> datasource1.getName().equals(datasourceStorage.getName())) - .findFirst() - .orElse(null); - - // Check if both of the datasource's are of the same plugin type - if (filteredDatasource != null) { - long matchCount = pluginList.stream() - .filter(plugin -> { - final String pluginReference = - plugin.getPluginName() == null ? plugin.getPackageName() : plugin.getPluginName(); - - return plugin.getId().equals(filteredDatasource.getPluginId()) - && !datasourceStorage.getPluginId().equals(pluginReference); - }) - .count(); - if (matchCount > 0) { - return true; - } - } - } - return false; - } - - private boolean isInvalidDefaultApplicationGitMetadata(GitArtifactMetadata gitArtifactMetadata) { - return Optional.ofNullable(gitArtifactMetadata).isEmpty() - || Optional.ofNullable(gitArtifactMetadata.getGitAuth()).isEmpty() - || StringUtils.isEmptyOrNull(gitArtifactMetadata.getGitAuth().getPrivateKey()) - || StringUtils.isEmptyOrNull(gitArtifactMetadata.getGitAuth().getPublicKey()); - } - - private Mono commitAndPushWithDefaultCommit( - Path repoSuffix, GitAuth auth, GitArtifactMetadata gitArtifactMetadata, GitDefaultCommitMessage reason) { - return gitExecutor - .commitArtifact( - repoSuffix, - DEFAULT_COMMIT_MESSAGE + reason.getReason(), - APPSMITH_BOT_USERNAME, - emailConfig.getSupportEmailAddress(), - true, - false) - .onErrorResume(error -> { - if (error instanceof EmptyCommitException) { - return Mono.just(EMPTY_COMMIT_ERROR_MESSAGE); - } - return Mono.error( - new AppsmithException(AppsmithError.GIT_ACTION_FAILED, "commit", error.getMessage())); - }) - .flatMap(commitMessage -> gitExecutor - .pushApplication( - repoSuffix, - gitArtifactMetadata.getRemoteUrl(), - auth.getPublicKey(), - auth.getPrivateKey(), - gitArtifactMetadata.getBranchName()) - .flatMap(pushResult -> { - if (pushResult.contains("REJECTED")) { - return Mono.error(new AppsmithException(AppsmithError.GIT_UPSTREAM_CHANGES)); - } - return Mono.just(pushResult); - })); - } - - /** - * Method to pull the files from remote repo and rehydrate the application - * - * @param defaultApplication application which acts as the root for the concerned branch - * @param branchName branch for which the pull is required - * @return pull DTO with updated application - */ - private Mono pullAndRehydrateApplication(Application defaultApplication, String branchName) { - - /* - 1. Checkout to the concerned branch - 2. Do git pull after - On Merge conflict - throw exception and ask user to resolve these conflicts on remote - TODO create new branch and push the changes to remote and ask the user to resolve it on github/gitlab UI - 3. Rehydrate the application from filesystem so that the latest changes from remote are rendered to the application - */ - GitArtifactMetadata gitData = defaultApplication.getGitApplicationMetadata(); - if (isInvalidDefaultApplicationGitMetadata(gitData)) { - return Mono.error(new AppsmithException(AppsmithError.INVALID_GIT_CONFIGURATION, GIT_CONFIG_ERROR)); - } - Path repoSuffix = Paths.get( - defaultApplication.getWorkspaceId(), gitData.getDefaultApplicationId(), gitData.getRepoName()); - - Mono branchedApplicationMono = applicationService.findByBranchNameAndDefaultApplicationId( - branchName, defaultApplication.getId(), applicationPermission.getEditPermission()); - - return branchedApplicationMono - .flatMap(branchedApplication -> { - // git checkout and pull origin branchName - try { - Mono pullStatusMono = gitExecutor - .checkoutToBranch(repoSuffix, branchName) - .then(gitExecutor.pullApplication( - repoSuffix, - gitData.getRemoteUrl(), - branchName, - gitData.getGitAuth().getPrivateKey(), - gitData.getGitAuth().getPublicKey())) - .onErrorResume(error -> { - if (error.getMessage().contains("conflict")) { - return Mono.error(new AppsmithException( - AppsmithError.GIT_PULL_CONFLICTS, error.getMessage())); - } else if (error.getMessage().contains("Nothing to fetch")) { - MergeStatusDTO mergeStatus = new MergeStatusDTO(); - mergeStatus.setStatus( - "Nothing to fetch from remote. All changes are up to date."); - mergeStatus.setMergeAble(true); - return Mono.just(mergeStatus); - } - return Mono.error(new AppsmithException( - AppsmithError.GIT_ACTION_FAILED, "pull", error.getMessage())); - }) - .cache(); - // Rehydrate the application from file system - Mono applicationJsonMono = pullStatusMono.flatMap( - status -> fileUtils.reconstructApplicationJsonFromGitRepoWithAnalytics( - branchedApplication.getWorkspaceId(), - branchedApplication - .getGitApplicationMetadata() - .getDefaultApplicationId(), - branchedApplication - .getGitApplicationMetadata() - .getRepoName(), - branchName)); - - return Mono.zip(pullStatusMono, Mono.just(branchedApplication), applicationJsonMono); - } catch (IOException e) { - return Mono.error(new AppsmithException(AppsmithError.GIT_FILE_SYSTEM_ERROR, e.getMessage())); - } - }) - .flatMap(tuple -> { - MergeStatusDTO status = tuple.getT1(); - Application branchedApplication = tuple.getT2(); - ApplicationJson applicationJson = tuple.getT3(); - - // Get the latest application with all the changes - // Commit and push changes to sync with remote - return importService - .importArtifactInWorkspaceFromGit( - branchedApplication.getWorkspaceId(), - branchedApplication.getId(), - applicationJson, - branchName) - .map(importableArtifact -> (Application) importableArtifact) - .flatMap(application -> addAnalyticsForGitOperation( - AnalyticsEvents.GIT_PULL, - application, - application - .getGitApplicationMetadata() - .getIsRepoPrivate()) - .thenReturn(application)) - .flatMap(application -> { - GitCommitDTO commitDTO = new GitCommitDTO(); - commitDTO.setCommitMessage(DEFAULT_COMMIT_MESSAGE - + GitDefaultCommitMessage.SYNC_WITH_REMOTE_AFTER_PULL.getReason()); - commitDTO.setDoPush(true); - - GitPullDTO gitPullDTO = new GitPullDTO(); - gitPullDTO.setMergeStatus(status); - gitPullDTO.setArtifact( - responseUtils.updateApplicationWithDefaultResources(application)); - - // Make commit and push after pull is successful to have a clean repo - return this.commitApplication( - commitDTO, - application - .getGitApplicationMetadata() - .getDefaultApplicationId(), - branchName) - .thenReturn(gitPullDTO); - }); - }); - } - - private Mono addAnalyticsForGitOperation( - AnalyticsEvents eventName, Application application, Boolean isRepoPrivate) { - return addAnalyticsForGitOperation(eventName, application, "", "", isRepoPrivate, false); - } - - private Mono addAnalyticsForGitOperation( - AnalyticsEvents eventName, String branchName, Application application) { - return addAnalyticsForGitOperation(eventName, application, null, null, null, false, null, branchName); - } - - private Mono addAnalyticsForGitOperation( - AnalyticsEvents eventName, - Application application, - String errorType, - String errorMessage, - Boolean isRepoPrivate) { - return addAnalyticsForGitOperation(eventName, application, errorType, errorMessage, isRepoPrivate, false); - } - - private Mono addAnalyticsForGitOperation( - AnalyticsEvents event, - Application application, - String errorType, - String errorMessage, - Boolean isRepoPrivate, - Boolean isSystemGenerated) { - return addAnalyticsForGitOperation( - event, application, errorType, errorMessage, isRepoPrivate, isSystemGenerated, null); - } - - private Mono addAnalyticsForGitOperation( - AnalyticsEvents event, - Application application, - String errorType, - String errorMessage, - Boolean isRepoPrivate, - Boolean isSystemGenerated, - Boolean isMergeable) { - - String branchName = application.getGitApplicationMetadata() != null - ? application.getGitApplicationMetadata().getBranchName() - : null; - return addAnalyticsForGitOperation( - event, application, errorType, errorMessage, isRepoPrivate, isSystemGenerated, isMergeable, branchName); - } - - private Mono addAnalyticsForGitOperation( - AnalyticsEvents event, - Application application, - String errorType, - String errorMessage, - Boolean isRepoPrivate, - Boolean isSystemGenerated, - Boolean isMergeable, - String branchName) { - GitArtifactMetadata gitData = application.getGitApplicationMetadata(); - Map analyticsProps = new HashMap<>(); - if (gitData != null) { - analyticsProps.put(FieldName.APPLICATION_ID, gitData.getDefaultApplicationId()); - analyticsProps.put("appId", gitData.getDefaultApplicationId()); - analyticsProps.put(FieldName.BRANCH_NAME, branchName); - analyticsProps.put(FieldName.GIT_HOSTING_PROVIDER, GitUtils.getGitProviderName(gitData.getRemoteUrl())); - analyticsProps.put(FieldName.REPO_URL, gitData.getRemoteUrl()); - if (event == AnalyticsEvents.GIT_COMMIT) { - analyticsProps.put("isAutoCommit", false); - } - } - // Do not include the error data points in the map for success states - if (!StringUtils.isEmptyOrNull(errorMessage) || !StringUtils.isEmptyOrNull(errorType)) { - analyticsProps.put("errorMessage", errorMessage); - analyticsProps.put("errorType", errorType); - } - // Do not include the isMergeable for all the events - if (isMergeable != null) { - analyticsProps.put(FieldName.IS_MERGEABLE, isMergeable); - } - analyticsProps.putAll(Map.of( - FieldName.ORGANIZATION_ID, - defaultIfNull(application.getWorkspaceId(), ""), - "orgId", - defaultIfNull(application.getWorkspaceId(), ""), - "branchApplicationId", - defaultIfNull(application.getId(), ""), - "isRepoPrivate", - defaultIfNull(isRepoPrivate, ""), - "isSystemGenerated", - defaultIfNull(isSystemGenerated, ""))); - final Map eventData = - Map.of(FieldName.APP_MODE, ApplicationMode.EDIT.toString(), FieldName.APPLICATION, application); - analyticsProps.put(FieldName.EVENT_DATA, eventData); - return sessionUserService.getCurrentUser().flatMap(user -> analyticsService - .sendEvent(event.getEventName(), user.getUsername(), analyticsProps) - .thenReturn(application)); - } - - private Mono addFileLock(String defaultApplicationId, String commandName) { - return gitRedisUtils.addFileLock(defaultApplicationId, commandName); - } - - private Mono releaseFileLock(String defaultApplicationId) { - return gitRedisUtils.releaseFileLock(defaultApplicationId); - } - - @Override - public Mono> updateProtectedBranches(String defaultApplicationId, List branchNames) { - return getApplicationById(defaultApplicationId, applicationPermission.getManageProtectedBranchPermission()) - .flatMap(rootApplication -> { - GitArtifactMetadata metadata = rootApplication.getGitApplicationMetadata(); - String defaultBranchName = metadata.getDefaultBranchName(); - - if (branchNames.isEmpty() - || (branchNames.size() == 1 && branchNames.get(0).equals(defaultBranchName))) { - // keep a copy of old protected branches as it's required to send analytics event later - List oldProtectedBranches = metadata.getBranchProtectionRules() != null - ? metadata.getBranchProtectionRules() - : List.of(); - - // user wants to unprotect all branches or user wants to protect only default branch - metadata.setBranchProtectionRules(branchNames); - return applicationService - .save(rootApplication) - .then(applicationService.updateProtectedBranches(defaultApplicationId, branchNames)) - .then(sendBranchProtectionAnalytics(rootApplication, oldProtectedBranches, branchNames)) - .thenReturn(branchNames); - } else { - // user want to protect multiple branches, not allowed - return Mono.error(new AppsmithException(AppsmithError.UNSUPPORTED_OPERATION)); - } - }) - .as(transactionalOperator::transactional); - } - - @Override - public Mono> getProtectedBranches(String defaultApplicationId) { - return getApplicationById(defaultApplicationId, applicationPermission.getEditPermission()) - .map(application -> { - GitArtifactMetadata gitArtifactMetadata = application.getGitApplicationMetadata(); - /* - user may have multiple branches as protected, but we only return the default branch - as protected branch if it's present in the list of protected branches - */ - List protectedBranches = gitArtifactMetadata.getBranchProtectionRules(); - String defaultBranchName = gitArtifactMetadata.getDefaultBranchName(); - - if (!CollectionUtils.isNullOrEmpty(protectedBranches) - && protectedBranches.contains(defaultBranchName)) { - return List.of(defaultBranchName); - } else { - return List.of(); - } - }); - } - - @Override - public Mono autoCommitApplication(String defaultApplicationId, String branchName) { - return gitAutoCommitHelper.autoCommitClientMigration(defaultApplicationId, branchName); - } - - @Override - public Mono getAutoCommitProgress(String applicationId) { - return gitAutoCommitHelper.getAutoCommitProgress(applicationId); - } - - @Override - public Mono toggleAutoCommitEnabled(String defaultApplicationId) { - return getApplicationById(defaultApplicationId, applicationPermission.getManageAutoCommitPermission()) - .map(application -> { - GitArtifactMetadata gitArtifactMetadata = application.getGitApplicationMetadata(); - if (!application.getId().equals(gitArtifactMetadata.getDefaultApplicationId())) { - log.error( - "failed tp toggle auto commit. reason: {} is not the root application id", - defaultApplicationId); - throw new AppsmithException(AppsmithError.INVALID_PARAMETER, "default application id"); - } - - AutoCommitConfig autoCommitConfig = gitArtifactMetadata.getAutoCommitConfig(); - if (autoCommitConfig.getEnabled()) { - autoCommitConfig.setEnabled(FALSE); - } else { - autoCommitConfig.setEnabled(TRUE); - } - // need to call the setter because getter returns a default config if attribute is null - application.getGitApplicationMetadata().setAutoCommitConfig(autoCommitConfig); - return application; - }) - .flatMap(application -> applicationService - .save(application) - .thenReturn(application - .getGitApplicationMetadata() - .getAutoCommitConfig() - .getEnabled())); - } - - /** - * Sends one or more analytics events when there's a change in protected branches. - * If n number of branches are un-protected and m number of branches are protected, it'll send m+n number of - * events. It receives the list of branches before and after the action. - * For example, if user has "main" and "develop" branches as protected and wants to include "staging" branch as - * protected as well, then oldProtectedBranches will be ["main", "develop"] and newProtectedBranches will be - * ["main", "develop", "staging"] - * - * @param application Application object of the root application - * @param oldProtectedBranches List of branches that were protected before this action. - * @param newProtectedBranches List of branches that are going to be protected. - * @return An empty Mono - */ - protected Mono sendBranchProtectionAnalytics( - Application application, List oldProtectedBranches, List newProtectedBranches) { - List itemsAdded = new ArrayList<>(newProtectedBranches); // add all new items - itemsAdded.removeAll(oldProtectedBranches); // remove the items that were present earlier - - List itemsRemoved = new ArrayList<>(oldProtectedBranches); // add all old items - itemsRemoved.removeAll(newProtectedBranches); // remove the items that are also present in new list - - List> eventSenderMonos = new ArrayList<>(); - - // send an analytics event for each removed branch - for (String branchName : itemsRemoved) { - eventSenderMonos.add(addAnalyticsForGitOperation(GIT_REMOVE_PROTECTED_BRANCH, branchName, application)); - } - - // send an analytics event for each newly protected branch - for (String branchName : itemsAdded) { - eventSenderMonos.add(addAnalyticsForGitOperation(GIT_ADD_PROTECTED_BRANCH, branchName, application)); - } - - return Flux.merge(eventSenderMonos).then(); - } -} diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce_compatible/CommonGitServiceCECompatible.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce_compatible/CommonGitServiceCECompatible.java deleted file mode 100644 index 19eddc4e86..0000000000 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce_compatible/CommonGitServiceCECompatible.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.appsmith.server.services.ce_compatible; - -import com.appsmith.server.services.ce.CommonGitServiceCE; - -public interface CommonGitServiceCECompatible extends CommonGitServiceCE {} diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce_compatible/GitServiceCECompatible.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce_compatible/GitServiceCECompatible.java deleted file mode 100644 index 8de0d1082e..0000000000 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce_compatible/GitServiceCECompatible.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.appsmith.server.services.ce_compatible; - -import com.appsmith.server.services.ce.GitServiceCE; - -public interface GitServiceCECompatible extends GitServiceCE {} diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce_compatible/GitServiceCECompatibleImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce_compatible/GitServiceCECompatibleImpl.java deleted file mode 100644 index 2cbd0d53ef..0000000000 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce_compatible/GitServiceCECompatibleImpl.java +++ /dev/null @@ -1,93 +0,0 @@ -package com.appsmith.server.services.ce_compatible; - -import com.appsmith.external.git.GitExecutor; -import com.appsmith.server.actioncollections.base.ActionCollectionService; -import com.appsmith.server.applications.base.ApplicationService; -import com.appsmith.server.configurations.EmailConfig; -import com.appsmith.server.datasources.base.DatasourceService; -import com.appsmith.server.exports.internal.ExportService; -import com.appsmith.server.git.GitRedisUtils; -import com.appsmith.server.git.autocommit.helpers.GitAutoCommitHelper; -import com.appsmith.server.helpers.GitFileUtils; -import com.appsmith.server.helpers.GitPrivateRepoHelper; -import com.appsmith.server.helpers.ResponseUtils; -import com.appsmith.server.imports.internal.ImportService; -import com.appsmith.server.newactions.base.NewActionService; -import com.appsmith.server.newpages.base.NewPageService; -import com.appsmith.server.plugins.base.PluginService; -import com.appsmith.server.repositories.GitDeployKeysRepository; -import com.appsmith.server.services.AnalyticsService; -import com.appsmith.server.services.ApplicationPageService; -import com.appsmith.server.services.SessionUserService; -import com.appsmith.server.services.UserDataService; -import com.appsmith.server.services.UserService; -import com.appsmith.server.services.WorkspaceService; -import com.appsmith.server.services.ce.GitServiceCEImpl; -import com.appsmith.server.solutions.ApplicationPermission; -import com.appsmith.server.solutions.DatasourcePermission; -import com.appsmith.server.solutions.WorkspacePermission; -import io.micrometer.observation.ObservationRegistry; -import org.springframework.stereotype.Service; -import org.springframework.transaction.reactive.TransactionalOperator; - -@Service -public class GitServiceCECompatibleImpl extends GitServiceCEImpl implements GitServiceCECompatible { - - public GitServiceCECompatibleImpl( - UserService userService, - UserDataService userDataService, - SessionUserService sessionUserService, - ApplicationService applicationService, - ApplicationPageService applicationPageService, - NewPageService newPageService, - NewActionService newActionService, - ActionCollectionService actionCollectionService, - GitFileUtils fileUtils, - ImportService importService, - ExportService exportService, - GitExecutor gitExecutor, - ResponseUtils responseUtils, - EmailConfig emailConfig, - AnalyticsService analyticsService, - GitDeployKeysRepository gitDeployKeysRepository, - DatasourceService datasourceService, - PluginService pluginService, - DatasourcePermission datasourcePermission, - ApplicationPermission applicationPermission, - WorkspacePermission workspacePermission, - WorkspaceService workspaceService, - GitRedisUtils gitRedisUtils, - ObservationRegistry observationRegistry, - GitPrivateRepoHelper gitPrivateRepoHelper, - TransactionalOperator transactionalOperator, - GitAutoCommitHelper gitAutoCommitHelper) { - super( - userService, - userDataService, - sessionUserService, - applicationService, - applicationPageService, - newPageService, - newActionService, - actionCollectionService, - fileUtils, - importService, - exportService, - gitExecutor, - responseUtils, - emailConfig, - analyticsService, - gitDeployKeysRepository, - datasourceService, - pluginService, - datasourcePermission, - applicationPermission, - workspacePermission, - workspaceService, - gitRedisUtils, - observationRegistry, - gitPrivateRepoHelper, - transactionalOperator, - gitAutoCommitHelper); - } -} diff --git a/app/server/appsmith-server/src/test/java/com/appsmith/server/controllers/ApplicationControllerTest.java b/app/server/appsmith-server/src/test/java/com/appsmith/server/controllers/ApplicationControllerTest.java index c15ed044de..c26bad25bd 100644 --- a/app/server/appsmith-server/src/test/java/com/appsmith/server/controllers/ApplicationControllerTest.java +++ b/app/server/appsmith-server/src/test/java/com/appsmith/server/controllers/ApplicationControllerTest.java @@ -11,7 +11,7 @@ import com.appsmith.server.exceptions.AppsmithErrorCode; import com.appsmith.server.exports.internal.ExportService; import com.appsmith.server.exports.internal.partial.PartialExportService; import com.appsmith.server.fork.internal.ApplicationForkingService; -import com.appsmith.server.helpers.GitFileUtils; +import com.appsmith.server.helpers.CommonGitFileUtils; import com.appsmith.server.helpers.RedisUtils; import com.appsmith.server.imports.internal.ImportService; import com.appsmith.server.imports.internal.partial.PartialImportService; @@ -83,7 +83,7 @@ public class ApplicationControllerTest { AnalyticsService analyticsService; @MockBean - GitFileUtils gitFileUtils; + CommonGitFileUtils commonGitFileUtils; @MockBean SessionUserService sessionUserService; diff --git a/app/server/appsmith-server/src/test/java/com/appsmith/server/git/GitServiceCETest.java b/app/server/appsmith-server/src/test/java/com/appsmith/server/git/CommonGitServiceCETest.java similarity index 81% rename from app/server/appsmith-server/src/test/java/com/appsmith/server/git/GitServiceCETest.java rename to app/server/appsmith-server/src/test/java/com/appsmith/server/git/CommonGitServiceCETest.java index 0c221fe69f..05be8d1c0b 100644 --- a/app/server/appsmith-server/src/test/java/com/appsmith/server/git/GitServiceCETest.java +++ b/app/server/appsmith-server/src/test/java/com/appsmith/server/git/CommonGitServiceCETest.java @@ -16,6 +16,7 @@ import com.appsmith.external.models.Policy; import com.appsmith.server.acl.AclPermission; import com.appsmith.server.actioncollections.base.ActionCollectionService; import com.appsmith.server.applications.base.ApplicationService; +import com.appsmith.server.constants.ArtifactType; import com.appsmith.server.constants.FieldName; import com.appsmith.server.datasources.base.DatasourceService; import com.appsmith.server.domains.ActionCollection; @@ -23,6 +24,7 @@ import com.appsmith.server.domains.Application; import com.appsmith.server.domains.ApplicationDetail; import com.appsmith.server.domains.ApplicationMode; import com.appsmith.server.domains.ApplicationPage; +import com.appsmith.server.domains.Artifact; import com.appsmith.server.domains.AutoCommitConfig; import com.appsmith.server.domains.GitArtifactMetadata; import com.appsmith.server.domains.GitAuth; @@ -43,9 +45,10 @@ import com.appsmith.server.dtos.GitPullDTO; import com.appsmith.server.dtos.PageDTO; import com.appsmith.server.exceptions.AppsmithError; import com.appsmith.server.exceptions.AppsmithException; +import com.appsmith.server.git.common.CommonGitServiceCE; import com.appsmith.server.helpers.CollectionUtils; +import com.appsmith.server.helpers.CommonGitFileUtils; import com.appsmith.server.helpers.GitCloudServicesUtils; -import com.appsmith.server.helpers.GitFileUtils; import com.appsmith.server.helpers.MockPluginExecutor; import com.appsmith.server.helpers.PluginExecutorHelper; import com.appsmith.server.layouts.UpdateLayoutService; @@ -63,7 +66,6 @@ import com.appsmith.server.services.LayoutCollectionService; import com.appsmith.server.services.SessionUserService; import com.appsmith.server.services.UserService; import com.appsmith.server.services.WorkspaceService; -import com.appsmith.server.services.ce.GitServiceCE; import com.appsmith.server.solutions.ApplicationPermission; import com.appsmith.server.solutions.EnvironmentPermission; import com.appsmith.server.themes.base.ThemeService; @@ -141,7 +143,7 @@ import static org.mockito.Mockito.verify; @SpringBootTest @Slf4j @DirtiesContext -public class GitServiceCETest { +public class CommonGitServiceCETest { private static final String DEFAULT_BRANCH = "defaultBranchName"; private static final String EMPTY_COMMIT_ERROR_MESSAGE = "On current branch nothing to commit, working tree clean"; @@ -156,8 +158,8 @@ public class GitServiceCETest { private static final String filePath = "test_assets/ImportExportServiceTest/valid-application-without-action-collection.json"; - @Qualifier("gitServiceCEImpl") @Autowired - GitServiceCE gitService; + @Qualifier("commonGitServiceCEImpl") @Autowired + CommonGitServiceCE commonGitServiceCE; @Autowired Gson gson; @@ -205,7 +207,7 @@ public class GitServiceCETest { GitExecutor gitExecutor; @MockBean - GitFileUtils gitFileUtils; + CommonGitFileUtils commonGitFileUtils; @MockBean GitCloudServicesUtils gitCloudServicesUtils; @@ -253,24 +255,15 @@ public class GitServiceCETest { Mockito.when(pluginExecutorHelper.getPluginExecutor(any())).thenReturn(Mono.just(new MockPluginExecutor())); - gitConnectedApplication = createApplicationConnectedToGit("gitConnectedApplication", DEFAULT_BRANCH); - // applicationPermission = new ApplicationPermissionImpl(); testUserProfile.setAuthorEmail("test@email.com"); testUserProfile.setAuthorName("testUser"); - Set afterCreatingWorkspace = - cacheableRepositoryHelper.getPermissionGroupsOfUser(currentUser).block(); - log.info("Permission Groups for User after creating workspace: {}", afterCreatingWorkspace); - - log.info("Workspace ID: {}", workspaceId); - log.info("Workspace Role Ids: {}", workspace.getDefaultPermissionGroups()); - log.info("Policy for created Workspace: {}", workspace.getPolicies()); - log.info("Current User ID: {}", currentUser.getId()); + gitConnectedApplication = createApplicationConnectedToGit("gitConnectedApplication", DEFAULT_BRANCH); } @AfterEach public void cleanup() { - Mockito.when(gitFileUtils.deleteLocalRepo(any(Path.class))).thenReturn(Mono.just(true)); + Mockito.when(commonGitFileUtils.deleteLocalRepo(any(Path.class))).thenReturn(Mono.just(true)); List deletedApplications = applicationService .findByWorkspaceId(workspaceId, applicationPermission.getDeletePermission()) .flatMap(remainingApplication -> applicationPageService.deleteApplication(remainingApplication.getId())) @@ -334,11 +327,12 @@ public class GitServiceCETest { .thenReturn(Mono.just(true)); Mockito.when(gitExecutor.pushApplication(any(Path.class), any(), any(), any(), any())) .thenReturn(Mono.just("success")); - Mockito.when(gitFileUtils.checkIfDirectoryIsEmpty(any(Path.class))).thenReturn(Mono.just(true)); - Mockito.when(gitFileUtils.initializeReadme(any(Path.class), Mockito.anyString(), Mockito.anyString())) + Mockito.when(commonGitFileUtils.checkIfDirectoryIsEmpty(any(Path.class))) + .thenReturn(Mono.just(true)); + Mockito.when(commonGitFileUtils.initializeReadme(any(Path.class), Mockito.anyString(), Mockito.anyString())) .thenReturn(Mono.just(Paths.get("textPath"))); - Mockito.when(gitFileUtils.saveApplicationToLocalRepoWithAnalytics( - any(Path.class), any(ApplicationJson.class), Mockito.anyString())) + Mockito.when(commonGitFileUtils.saveArtifactToLocalRepoWithAnalytics( + any(Path.class), any(), Mockito.anyString())) .thenReturn(Mono.just(Paths.get("path"))); Application testApplication = new Application(); @@ -364,17 +358,20 @@ public class GitServiceCETest { String repoUrl = String.format("git@github.com:test/%s.git", name); GitConnectDTO gitConnectDTO = getConnectRequest(repoUrl, testUserProfile); - return gitService - .connectApplicationToGit(application1.getId(), gitConnectDTO, "baseUrl") + return commonGitServiceCE + .connectArtifactToGit(application1.getId(), gitConnectDTO, "baseUrl", ArtifactType.APPLICATION) + .map(artifact -> (Application) artifact) .block(); } @Test @WithUserDetails(value = "api_user") - public void connectApplicationToGit_EmptyRemoteUrl_ThrowInvalidParameterException() { + public void connectArtifactToGit_EmptyRemoteUrl_ThrowInvalidParameterException() { GitConnectDTO gitConnectDTO = getConnectRequest(null, testUserProfile); - Mono applicationMono = gitService.connectApplicationToGit("testID", gitConnectDTO, "baseUrl"); + Mono applicationMono = commonGitServiceCE + .connectArtifactToGit("testID", gitConnectDTO, "baseUrl", ArtifactType.APPLICATION) + .map(artifact -> (Application) artifact); StepVerifier.create(applicationMono) .expectErrorMatches(throwable -> throwable instanceof AppsmithException @@ -384,10 +381,12 @@ public class GitServiceCETest { @Test @WithUserDetails(value = "api_user") - public void connectApplicationToGit_EmptyOriginHeader_ThrowInvalidParameterException() { + public void connectArtifactToGit_EmptyOriginHeader_ThrowInvalidParameterException() { GitConnectDTO gitConnectDTO = getConnectRequest("git@github.com:test/testRepo.git", testUserProfile); - Mono applicationMono = gitService.connectApplicationToGit("testID", gitConnectDTO, null); + Mono applicationMono = commonGitServiceCE + .connectArtifactToGit("testID", gitConnectDTO, null, ArtifactType.APPLICATION) + .map(artifact -> (Application) artifact); StepVerifier.create(applicationMono) .expectErrorMatches(throwable -> throwable instanceof AppsmithException @@ -397,7 +396,7 @@ public class GitServiceCETest { @Test @WithUserDetails(value = "api_user") - public void connectApplicationToGit_InvalidGitApplicationMetadata_ThrowInvalidGitConfigurationException() { + public void connectArtifactToGit_InvalidGitApplicationMetadata_ThrowInvalidGitConfigurationException() { Application testApplication = new Application(); testApplication.setGitApplicationMetadata(new GitArtifactMetadata()); @@ -407,8 +406,9 @@ public class GitServiceCETest { applicationPageService.createApplication(testApplication).block(); GitConnectDTO gitConnectDTO = getConnectRequest("git@github.com:test/testRepo.git", testUserProfile); - Mono applicationMono = - gitService.connectApplicationToGit(application1.getId(), gitConnectDTO, "baseUrl"); + Mono applicationMono = commonGitServiceCE + .connectArtifactToGit(application1.getId(), gitConnectDTO, "baseUrl", ArtifactType.APPLICATION) + .map(artifact -> (Application) artifact); StepVerifier.create(applicationMono) .expectErrorMatches(throwable -> { @@ -424,7 +424,7 @@ public class GitServiceCETest { @Test @WithUserDetails(value = "api_user") - public void connectApplicationToGit_EmptyPrivateKey_ThrowInvalidGitConfigurationException() { + public void connectArtifactToGit_EmptyPrivateKey_ThrowInvalidGitConfigurationException() { Application testApplication = new Application(); GitArtifactMetadata gitArtifactMetadata = new GitArtifactMetadata(); @@ -438,8 +438,9 @@ public class GitServiceCETest { applicationPageService.createApplication(testApplication).block(); GitConnectDTO gitConnectDTO = getConnectRequest("git@github.com:test/testRepo.git", testUserProfile); - Mono applicationMono = - gitService.connectApplicationToGit(application1.getId(), gitConnectDTO, "baseUrl"); + Mono applicationMono = commonGitServiceCE + .connectArtifactToGit(application1.getId(), gitConnectDTO, "baseUrl", ArtifactType.APPLICATION) + .map(artifact -> (Application) artifact); StepVerifier.create(applicationMono) .expectErrorMatches(throwable -> throwable instanceof AppsmithException @@ -451,7 +452,7 @@ public class GitServiceCETest { @Test @WithUserDetails(value = "api_user") - public void connectApplicationToGit_EmptyPublicKey_ThrowInvalidGitConfigurationException() { + public void connectArtifactToGit_EmptyPublicKey_ThrowInvalidGitConfigurationException() { Application testApplication = new Application(); GitArtifactMetadata gitArtifactMetadata = new GitArtifactMetadata(); @@ -465,8 +466,9 @@ public class GitServiceCETest { applicationPageService.createApplication(testApplication).block(); GitConnectDTO gitConnectDTO = getConnectRequest("git@github.com:test/testRepo.git", testUserProfile); - Mono applicationMono = - gitService.connectApplicationToGit(application1.getId(), gitConnectDTO, "baseUrl"); + Mono applicationMono = commonGitServiceCE + .connectArtifactToGit(application1.getId(), gitConnectDTO, "baseUrl", ArtifactType.APPLICATION) + .map(artifact -> (Application) artifact); StepVerifier.create(applicationMono) .expectErrorMatches(throwable -> throwable instanceof AppsmithException @@ -478,7 +480,7 @@ public class GitServiceCETest { @Test @WithUserDetails(value = "api_user") - public void connectApplicationToGit_InvalidRemoteUrl_ThrowInvalidRemoteUrl() throws IOException { + public void connectArtifactToGit_InvalidRemoteUrl_ThrowInvalidRemoteUrl() throws IOException { Application testApplication = new Application(); GitArtifactMetadata gitArtifactMetadata = new GitArtifactMetadata(); @@ -497,10 +499,12 @@ public class GitServiceCETest { Mockito.when(gitExecutor.cloneRemoteIntoArtifactRepo( any(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString())) .thenReturn(Mono.just("defaultBranchName")); - Mockito.when(gitFileUtils.checkIfDirectoryIsEmpty(any(Path.class))).thenReturn(Mono.just(false)); + Mockito.when(commonGitFileUtils.checkIfDirectoryIsEmpty(any(Path.class))) + .thenReturn(Mono.just(false)); - Mono applicationMono = - gitService.connectApplicationToGit(application1.getId(), gitConnectDTO, "baseUrl"); + Mono applicationMono = commonGitServiceCE + .connectArtifactToGit(application1.getId(), gitConnectDTO, "baseUrl", ArtifactType.APPLICATION) + .map(artifact -> (Application) artifact); StepVerifier.create(applicationMono) .expectErrorMatches(throwable -> throwable instanceof AppsmithException) @@ -509,7 +513,7 @@ public class GitServiceCETest { @Test @WithUserDetails(value = "api_user") - public void connectApplicationToGit_InvalidRemoteUrlHttp_ThrowInvalidRemoteUrl() throws ClassCastException { + public void connectArtifactToGit_InvalidRemoteUrlHttp_ThrowInvalidRemoteUrl() throws ClassCastException { Application testApplication = new Application(); GitArtifactMetadata gitArtifactMetadata = new GitArtifactMetadata(); @@ -528,24 +532,29 @@ public class GitServiceCETest { Mockito.when(gitExecutor.cloneRemoteIntoArtifactRepo( any(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString())) .thenReturn(Mono.error(new ClassCastException("TransportHttp"))); - Mockito.when(gitFileUtils.deleteLocalRepo(any(Path.class))).thenReturn(Mono.just(true)); + Mockito.when(commonGitFileUtils.deleteLocalRepo(any(Path.class))).thenReturn(Mono.just(true)); - Mono applicationMono = - gitService.connectApplicationToGit(application1.getId(), gitConnectDTO, "baseUrl"); + Mono applicationMono = commonGitServiceCE.connectArtifactToGit( + application1.getId(), gitConnectDTO, "baseUrl", ArtifactType.APPLICATION); StepVerifier.create(applicationMono) - .verifyErrorMessage(AppsmithError.INVALID_GIT_CONFIGURATION.getMessage("Remote URL is incorrect. " - + "Please add a URL in standard format. Example: git@example.com:username/reponame.git")); + .expectErrorMatches( + e -> e.getMessage() + .equals( + AppsmithError.INVALID_GIT_CONFIGURATION.getMessage( + "Remote URL is incorrect. " + + "Please add a URL in standard format. Example: git@example.com:username/reponame.git"))) + .verify(); } @Test @WithUserDetails(value = "api_user") - public void connectApplicationToGit_InvalidFilePath_ThrowIOException() throws IOException { + public void connectArtifactToGit_InvalidFilePath_ThrowIOException() throws IOException { Mockito.when(gitExecutor.cloneRemoteIntoArtifactRepo( any(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString())) .thenReturn(Mono.just("defaultBranchName")); - Mockito.when(gitFileUtils.checkIfDirectoryIsEmpty(any(Path.class))) + Mockito.when(commonGitFileUtils.checkIfDirectoryIsEmpty(any(Path.class))) .thenThrow(new IOException("Error while accessing the file system")); Application testApplication = new Application(); @@ -561,8 +570,9 @@ public class GitServiceCETest { applicationPageService.createApplication(testApplication).block(); GitConnectDTO gitConnectDTO = getConnectRequest("git@github.com:test/testy.git", testUserProfile); - Mono applicationMono = - gitService.connectApplicationToGit(application1.getId(), gitConnectDTO, "baseUrl"); + Mono applicationMono = commonGitServiceCE + .connectArtifactToGit(application1.getId(), gitConnectDTO, "baseUrl", ArtifactType.APPLICATION) + .map(artifact -> (Application) artifact); StepVerifier.create(applicationMono) .expectErrorMatches(throwable -> throwable instanceof AppsmithException @@ -572,12 +582,13 @@ public class GitServiceCETest { @Test @WithUserDetails(value = "api_user") - public void connectApplicationToGit_ClonedRepoNotEmpty_Failure() throws IOException { + public void connectArtifactToGit_ClonedRepoNotEmpty_Failure() throws IOException { Mockito.when(gitExecutor.cloneRemoteIntoArtifactRepo( any(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString())) .thenReturn(Mono.just("defaultBranchName")); - Mockito.when(gitFileUtils.checkIfDirectoryIsEmpty(any(Path.class))).thenReturn(Mono.just(false)); + Mockito.when(commonGitFileUtils.checkIfDirectoryIsEmpty(any(Path.class))) + .thenReturn(Mono.just(false)); Application testApplication = new Application(); GitArtifactMetadata gitArtifactMetadata = new GitArtifactMetadata(); @@ -592,8 +603,9 @@ public class GitServiceCETest { applicationPageService.createApplication(testApplication).block(); GitConnectDTO gitConnectDTO = getConnectRequest("git@github.com:test/testy.git", testUserProfile); - Mono applicationMono = - gitService.connectApplicationToGit(application1.getId(), gitConnectDTO, "baseUrl"); + Mono applicationMono = commonGitServiceCE + .connectArtifactToGit(application1.getId(), gitConnectDTO, "baseUrl", ArtifactType.APPLICATION) + .map(artifact -> (Application) artifact); StepVerifier.create(applicationMono) .expectErrorMatches(throwable -> throwable instanceof AppsmithException @@ -603,16 +615,18 @@ public class GitServiceCETest { @Test @WithUserDetails(value = "api_user") - public void connectApplicationToGit_cloneException_throwGitException() throws IOException { + public void connectArtifactToGit_cloneException_throwGitException() throws IOException { Mockito.when(gitExecutor.cloneRemoteIntoArtifactRepo( any(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString())) .thenReturn(Mono.error(new Exception("error message"))); - Mockito.when(gitFileUtils.deleteLocalRepo(any(Path.class))).thenReturn(Mono.just(true)); + Mockito.when(commonGitFileUtils.deleteLocalRepo(any(Path.class))).thenReturn(Mono.just(true)); GitConnectDTO gitConnectDTO = getConnectRequest("git@github.com:test/testRepo.git", testUserProfile); - Mono applicationMono = - gitService.connectApplicationToGit(gitConnectedApplication.getId(), gitConnectDTO, "baseUrl"); + Mono applicationMono = commonGitServiceCE + .connectArtifactToGit( + gitConnectedApplication.getId(), gitConnectDTO, "baseUrl", ArtifactType.APPLICATION) + .map(artifact -> (Application) artifact); StepVerifier.create(applicationMono) .expectErrorMatches(throwable -> throwable instanceof AppsmithException @@ -622,7 +636,7 @@ public class GitServiceCETest { @Test @WithUserDetails(value = "api_user") - public void connectApplicationToGit_WithEmptyPublishedPages_CloneSuccess() throws IOException, GitAPIException { + public void connectArtifactToGit_WithEmptyPublishedPages_CloneSuccess() throws IOException, GitAPIException { Mockito.when(gitExecutor.cloneRemoteIntoArtifactRepo( any(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString())) @@ -644,19 +658,12 @@ public class GitServiceCETest { Mockito.anyString(), Mockito.anyString())) .thenReturn(Mono.just("success")); - Mockito.when(gitExecutor.commitArtifact( - any(Path.class), - Mockito.anyString(), - Mockito.anyString(), - Mockito.anyString(), - Mockito.anyBoolean(), - Mockito.anyBoolean())) - .thenReturn(Mono.just("commit")); - Mockito.when(gitFileUtils.saveApplicationToLocalRepoWithAnalytics( - any(Path.class), any(ApplicationJson.class), Mockito.anyString())) + Mockito.when(commonGitFileUtils.saveArtifactToLocalRepoWithAnalytics( + any(Path.class), any(), Mockito.anyString())) .thenReturn(Mono.just(Paths.get(""))); - Mockito.when(gitFileUtils.checkIfDirectoryIsEmpty(any(Path.class))).thenReturn(Mono.just(true)); - Mockito.when(gitFileUtils.initializeReadme(any(Path.class), Mockito.anyString(), Mockito.anyString())) + Mockito.when(commonGitFileUtils.checkIfDirectoryIsEmpty(any(Path.class))) + .thenReturn(Mono.just(true)); + Mockito.when(commonGitFileUtils.initializeReadme(any(Path.class), Mockito.anyString(), Mockito.anyString())) .thenReturn(Mono.just(Paths.get("textPath"))); Application testApplication = new Application(); @@ -673,8 +680,9 @@ public class GitServiceCETest { applicationPageService.createApplication(testApplication).block(); GitConnectDTO gitConnectDTO = getConnectRequest("git@github.com:test/testRepo.git", testUserProfile); - Mono applicationMono = - gitService.connectApplicationToGit(application1.getId(), gitConnectDTO, "baseUrl"); + Mono applicationMono = commonGitServiceCE + .connectArtifactToGit(application1.getId(), gitConnectDTO, "baseUrl", ArtifactType.APPLICATION) + .map(artifact -> (Application) artifact); StepVerifier.create(applicationMono) .assertNext(application -> { @@ -687,14 +695,14 @@ public class GitServiceCETest { assertThat(gitArtifactMetadata1.getGitAuth().getGeneratedAt()) .isNotNull(); assertThat(gitArtifactMetadata1.getRepoName()).isEqualTo("testRepo"); - assertThat(gitArtifactMetadata1.getDefaultApplicationId()).isEqualTo(application.getId()); + assertThat(gitArtifactMetadata1.getDefaultArtifactId()).isEqualTo(application.getId()); }) .verifyComplete(); } @Test @WithUserDetails(value = "api_user") - public void connectApplicationToGit_WithoutGitProfileUsingDefaultProfile_CloneSuccess() + public void connectArtifactToGit_WithoutGitProfileUsingDefaultProfile_CloneSuccess() throws IOException, GitAPIException { Mockito.when(gitExecutor.cloneRemoteIntoArtifactRepo( @@ -725,13 +733,14 @@ public class GitServiceCETest { Mockito.anyBoolean(), Mockito.anyBoolean())) .thenReturn(Mono.just("commit")); - Mockito.when(gitFileUtils.saveApplicationToLocalRepoWithAnalytics( - any(Path.class), any(ApplicationJson.class), Mockito.anyString())) + Mockito.when(commonGitFileUtils.saveArtifactToLocalRepoWithAnalytics( + any(Path.class), any(), Mockito.anyString())) .thenReturn(Mono.just(Paths.get(""))); - Mockito.when(gitFileUtils.checkIfDirectoryIsEmpty(any(Path.class))).thenReturn(Mono.just(true)); - Mockito.when(gitFileUtils.initializeReadme(any(Path.class), Mockito.anyString(), Mockito.anyString())) + Mockito.when(commonGitFileUtils.checkIfDirectoryIsEmpty(any(Path.class))) + .thenReturn(Mono.just(true)); + Mockito.when(commonGitFileUtils.initializeReadme(any(Path.class), Mockito.anyString(), Mockito.anyString())) .thenReturn(Mono.just(Paths.get("textPath"))); - Mockito.when(gitFileUtils.deleteLocalRepo(any(Path.class))).thenReturn(Mono.just(true)); + Mockito.when(commonGitFileUtils.deleteLocalRepo(any(Path.class))).thenReturn(Mono.just(true)); GitProfile gitProfile = new GitProfile(); gitProfile.setAuthorName(null); @@ -750,8 +759,9 @@ public class GitServiceCETest { applicationPageService.createApplication(testApplication).block(); GitConnectDTO gitConnectDTO = getConnectRequest("git@github.com:test/testRepo.git", gitProfile); - Mono applicationMono = - gitService.connectApplicationToGit(application1.getId(), gitConnectDTO, "baseUrl"); + Mono applicationMono = commonGitServiceCE + .connectArtifactToGit(application1.getId(), gitConnectDTO, "baseUrl", ArtifactType.APPLICATION) + .map(artifact -> (Application) artifact); StepVerifier.create(applicationMono) .assertNext(application -> { @@ -764,7 +774,7 @@ public class GitServiceCETest { @Test @WithUserDetails(value = "api_user") - public void connectApplicationToGit_WithoutGitProfileUsingLocalProfile_ThrowAuthorNameUnavailableError() { + public void connectArtifactToGit_WithoutGitProfileUsingLocalProfile_ThrowAuthorNameUnavailableError() { GitProfile gitProfile = new GitProfile(); gitProfile.setAuthorName(null); @@ -789,8 +799,9 @@ public class GitServiceCETest { applicationPageService.createApplication(testApplication).block(); GitConnectDTO gitConnectDTO = getConnectRequest("git@github.com:test/testRepo.git", gitProfile); - Mono applicationMono = - gitService.connectApplicationToGit(application1.getId(), gitConnectDTO, "baseUrl"); + Mono applicationMono = commonGitServiceCE + .connectArtifactToGit(application1.getId(), gitConnectDTO, "baseUrl", ArtifactType.APPLICATION) + .map(artifact -> (Application) artifact); StepVerifier.create(applicationMono) .expectErrorMatches(throwable -> throwable instanceof AppsmithException @@ -800,7 +811,7 @@ public class GitServiceCETest { @Test @WithUserDetails(value = "api_user") - public void connectApplicationToGit_WithNonEmptyPublishedPages_CloneSuccess() throws IOException, GitAPIException { + public void connectArtifactToGit_WithNonEmptyPublishedPages_CloneSuccess() throws IOException, GitAPIException { Mockito.when(gitExecutor.cloneRemoteIntoArtifactRepo( any(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString())) @@ -830,13 +841,14 @@ public class GitServiceCETest { Mockito.anyBoolean(), Mockito.anyBoolean())) .thenReturn(Mono.just("commit")); - Mockito.when(gitFileUtils.saveApplicationToLocalRepoWithAnalytics( - any(Path.class), any(ApplicationJson.class), Mockito.anyString())) + Mockito.when(commonGitFileUtils.saveArtifactToLocalRepoWithAnalytics( + any(Path.class), any(), Mockito.anyString())) .thenReturn(Mono.just(Paths.get(""))); - Mockito.when(gitFileUtils.checkIfDirectoryIsEmpty(any(Path.class))).thenReturn(Mono.just(true)); - Mockito.when(gitFileUtils.initializeReadme(any(Path.class), Mockito.anyString(), Mockito.anyString())) + Mockito.when(commonGitFileUtils.checkIfDirectoryIsEmpty(any(Path.class))) + .thenReturn(Mono.just(true)); + Mockito.when(commonGitFileUtils.initializeReadme(any(Path.class), Mockito.anyString(), Mockito.anyString())) .thenReturn(Mono.just(Paths.get("textPath"))); - Mockito.when(gitFileUtils.deleteLocalRepo(any(Path.class))).thenReturn(Mono.just(true)); + Mockito.when(commonGitFileUtils.deleteLocalRepo(any(Path.class))).thenReturn(Mono.just(true)); Application testApplication = new Application(); GitArtifactMetadata gitArtifactMetadata = new GitArtifactMetadata(); @@ -847,7 +859,7 @@ public class GitServiceCETest { gitAuth.setDocUrl("docUrl"); gitArtifactMetadata.setGitAuth(gitAuth); testApplication.setGitApplicationMetadata(gitArtifactMetadata); - testApplication.setName("connectApplicationToGit_WithNonEmptyPublishedPages"); + testApplication.setName("connectArtifactToGit_WithNonEmptyPublishedPages"); testApplication.setWorkspaceId(workspaceId); Application application1 = applicationPageService.createApplication(testApplication).block(); @@ -858,8 +870,9 @@ public class GitServiceCETest { applicationPageService.createPage(page).block(); GitConnectDTO gitConnectDTO = getConnectRequest("git@github.com:test/testRepo.git", testUserProfile); - Mono applicationMono = - gitService.connectApplicationToGit(application1.getId(), gitConnectDTO, "baseUrl"); + Mono applicationMono = commonGitServiceCE + .connectArtifactToGit(application1.getId(), gitConnectDTO, "baseUrl", ArtifactType.APPLICATION) + .map(artifact -> (Application) artifact); StepVerifier.create(applicationMono) .assertNext(application -> { @@ -878,7 +891,7 @@ public class GitServiceCETest { @Test @WithUserDetails(value = "api_user") - public void connectApplicationToGit_moreThanSupportedPrivateRepos_throwException() + public void connectArtifactToGit_moreThanSupportedPrivateRepos_throwException() throws IOException, GitAPIException { Workspace workspace = new Workspace(); workspace.setName("Limit Private Repo Test Workspace"); @@ -908,8 +921,9 @@ public class GitServiceCETest { Mockito.anyString(), Mockito.anyString())) .thenReturn(Mono.just("success")); - Mockito.when(gitFileUtils.checkIfDirectoryIsEmpty(any(Path.class))).thenReturn(Mono.just(true)); - Mockito.when(gitFileUtils.initializeReadme(any(Path.class), Mockito.anyString(), Mockito.anyString())) + Mockito.when(commonGitFileUtils.checkIfDirectoryIsEmpty(any(Path.class))) + .thenReturn(Mono.just(true)); + Mockito.when(commonGitFileUtils.initializeReadme(any(Path.class), Mockito.anyString(), Mockito.anyString())) .thenReturn(Mono.just(Paths.get("textPath"))); Application testApplication = new Application(); @@ -921,14 +935,15 @@ public class GitServiceCETest { gitAuth.setDocUrl("docUrl"); gitArtifactMetadata.setGitAuth(gitAuth); testApplication.setGitApplicationMetadata(gitArtifactMetadata); - testApplication.setName("connectApplicationToGit_WithNonEmptyPublishedPages"); + testApplication.setName("connectArtifactToGit_WithNonEmptyPublishedPages"); testApplication.setWorkspaceId(limitPrivateRepoTestWorkspaceId); Application application = applicationPageService.createApplication(testApplication).block(); GitConnectDTO gitConnectDTO = getConnectRequest("git@github.com:test/testRepo.git", testUserProfile); - Mono applicationMono = - gitService.connectApplicationToGit(application.getId(), gitConnectDTO, "baseUrl"); + Mono applicationMono = commonGitServiceCE + .connectArtifactToGit(application.getId(), gitConnectDTO, "baseUrl", ArtifactType.APPLICATION) + .map(artifact -> (Application) artifact); StepVerifier.create(applicationMono) .expectErrorMatches(error -> error instanceof AppsmithException @@ -938,7 +953,7 @@ public class GitServiceCETest { @Test @WithUserDetails(value = "api_user") - public void connectApplicationToGit_toggleAccessibilityToPublicForConnectedApp_connectSuccessful() + public void connectArtifactToGit_toggleAccessibilityToPublicForConnectedApp_connectSuccessful() throws IOException, GitAPIException { Workspace workspace = new Workspace(); workspace.setName("Toggle Accessibility To Public From Private Repo Test Workspace"); @@ -968,8 +983,9 @@ public class GitServiceCETest { Mockito.anyString(), Mockito.anyString())) .thenReturn(Mono.just("success")); - Mockito.when(gitFileUtils.checkIfDirectoryIsEmpty(any(Path.class))).thenReturn(Mono.just(true)); - Mockito.when(gitFileUtils.initializeReadme(any(Path.class), Mockito.anyString(), Mockito.anyString())) + Mockito.when(commonGitFileUtils.checkIfDirectoryIsEmpty(any(Path.class))) + .thenReturn(Mono.just(true)); + Mockito.when(commonGitFileUtils.initializeReadme(any(Path.class), Mockito.anyString(), Mockito.anyString())) .thenReturn(Mono.just(Paths.get("textPath"))); Application application1 = @@ -985,14 +1001,15 @@ public class GitServiceCETest { gitAuth.setDocUrl("docUrl"); gitArtifactMetadata.setGitAuth(gitAuth); testApplication.setGitApplicationMetadata(gitArtifactMetadata); - testApplication.setName("connectApplicationToGit_WithNonEmptyPublishedPages"); + testApplication.setName("connectArtifactToGit_WithNonEmptyPublishedPages"); testApplication.setWorkspaceId(limitPrivateRepoTestWorkspaceId); Application application = applicationPageService.createApplication(testApplication).block(); GitConnectDTO gitConnectDTO = getConnectRequest("git@github.com:test/testRepo.git", testUserProfile); - Mono applicationMono = - gitService.connectApplicationToGit(application.getId(), gitConnectDTO, "baseUrl"); + Mono applicationMono = commonGitServiceCE + .connectArtifactToGit(application.getId(), gitConnectDTO, "baseUrl", ArtifactType.APPLICATION) + .map(artifact -> (Application) artifact); // Use any dummy url so as to get 2xx response application1.getGitApplicationMetadata().setBrowserSupportedRemoteUrl("https://www.google.com/"); @@ -1007,7 +1024,7 @@ public class GitServiceCETest { @Test @WithUserDetails(value = "api_user") - public void connectApplicationToGit_WithValidCustomGitDomain_CloneSuccess() throws IOException, GitAPIException { + public void connectArtifactToGit_WithValidCustomGitDomain_CloneSuccess() throws IOException, GitAPIException { Mockito.when(gitExecutor.cloneRemoteIntoArtifactRepo( any(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString())) @@ -1037,11 +1054,12 @@ public class GitServiceCETest { Mockito.anyBoolean(), Mockito.anyBoolean())) .thenReturn(Mono.just("commit")); - Mockito.when(gitFileUtils.saveApplicationToLocalRepoWithAnalytics( - any(Path.class), any(ApplicationJson.class), Mockito.anyString())) + Mockito.when(commonGitFileUtils.saveArtifactToLocalRepoWithAnalytics( + any(Path.class), any(), Mockito.anyString())) .thenReturn(Mono.just(Paths.get(""))); - Mockito.when(gitFileUtils.checkIfDirectoryIsEmpty(any(Path.class))).thenReturn(Mono.just(true)); - Mockito.when(gitFileUtils.initializeReadme(any(Path.class), Mockito.anyString(), Mockito.anyString())) + Mockito.when(commonGitFileUtils.checkIfDirectoryIsEmpty(any(Path.class))) + .thenReturn(Mono.just(true)); + Mockito.when(commonGitFileUtils.initializeReadme(any(Path.class), Mockito.anyString(), Mockito.anyString())) .thenReturn(Mono.just(Paths.get("textPath"))); Application testApplication = new Application(); @@ -1052,15 +1070,16 @@ public class GitServiceCETest { gitAuth.setGeneratedAt(Instant.now()); gitArtifactMetadata.setGitAuth(gitAuth); testApplication.setGitApplicationMetadata(gitArtifactMetadata); - testApplication.setName("connectApplicationToGit_WithValidCustomGitDomain_CloneSuccess"); + testApplication.setName("connectArtifactToGit_WithValidCustomGitDomain_CloneSuccess"); testApplication.setWorkspaceId(workspaceId); Application application1 = applicationPageService.createApplication(testApplication).block(); GitConnectDTO gitConnectDTO = getConnectRequest("git@github.test.net:user/test/tests/testRepo.git", testUserProfile); - Mono applicationMono = - gitService.connectApplicationToGit(application1.getId(), gitConnectDTO, "baseUrl"); + Mono applicationMono = commonGitServiceCE + .connectArtifactToGit(application1.getId(), gitConnectDTO, "baseUrl", ArtifactType.APPLICATION) + .map(artifact -> (Application) artifact); StepVerifier.create(applicationMono) .assertNext(application -> { @@ -1073,7 +1092,7 @@ public class GitServiceCETest { assertThat(gitArtifactMetadata1.getGitAuth().getGeneratedAt()) .isNotNull(); assertThat(gitArtifactMetadata1.getRepoName()).isEqualTo("testRepo"); - assertThat(gitArtifactMetadata1.getDefaultApplicationId()).isEqualTo(application.getId()); + assertThat(gitArtifactMetadata1.getDefaultArtifactId()).isEqualTo(application.getId()); }) .verifyComplete(); } @@ -1096,7 +1115,9 @@ public class GitServiceCETest { applicationPageService.createApplication(testApplication).block(); GitArtifactMetadata gitArtifactMetadata1 = null; - Mono applicationMono = gitService.updateGitMetadata(application1.getId(), gitArtifactMetadata1); + Mono applicationMono = commonGitServiceCE + .updateGitMetadata(application1.getId(), gitArtifactMetadata1, ArtifactType.APPLICATION) + .map(artifact -> (Application) artifact); StepVerifier.create(applicationMono) .expectErrorMatches(throwable -> throwable instanceof AppsmithException @@ -1123,7 +1144,9 @@ public class GitServiceCETest { GitArtifactMetadata gitArtifactMetadata1 = application1.getGitApplicationMetadata(); gitArtifactMetadata1.setRemoteUrl("https://test/.git"); - Mono applicationMono = gitService.updateGitMetadata(application1.getId(), gitArtifactMetadata1); + Mono applicationMono = commonGitServiceCE + .updateGitMetadata(application1.getId(), gitArtifactMetadata1, ArtifactType.APPLICATION) + .map(artifact -> (Application) artifact); StepVerifier.create(applicationMono) .assertNext(application -> { @@ -1147,7 +1170,7 @@ public class GitServiceCETest { branchList.add(remoteGitBranchDTO); Mockito.when(gitExecutor.listBranches(any(Path.class))).thenReturn(Mono.just(branchList)); - Mockito.when(gitFileUtils.deleteLocalRepo(any(Path.class))).thenReturn(Mono.just(true)); + Mockito.when(commonGitFileUtils.deleteLocalRepo(any(Path.class))).thenReturn(Mono.just(true)); Mockito.when(pluginExecutorHelper.getPluginExecutor(any())).thenReturn(Mono.just(new MockPluginExecutor())); Application testApplication = new Application(); @@ -1256,8 +1279,9 @@ public class GitServiceCETest { .map(tuple2 -> application); }); - Mono resultMono = - applicationMono.flatMap(application -> gitService.detachRemote(application.getId())); + Mono resultMono = applicationMono.flatMap(application -> commonGitServiceCE + .detachRemote(application.getId(), ArtifactType.APPLICATION) + .map(artifact -> (Application) artifact)); StepVerifier.create(resultMono.zipWhen(application -> Mono.zip( newActionService @@ -1348,7 +1372,9 @@ public class GitServiceCETest { Application application1 = applicationPageService.createApplication(testApplication).block(); - Mono applicationMono = gitService.detachRemote(application1.getId()); + Mono applicationMono = commonGitServiceCE + .detachRemote(application1.getId(), ArtifactType.APPLICATION) + .map(artifact -> (Application) artifact); StepVerifier.create(applicationMono) .expectErrorMatches(throwable -> throwable instanceof AppsmithException @@ -1358,7 +1384,7 @@ public class GitServiceCETest { @Test @WithUserDetails(value = "api_user") - public void listBranchForApplication_emptyGitMetadata_throwError() { + public void listBranchForArtifact_emptyGitMetadata_throwError() { Application testApplication = new Application(); testApplication.setGitApplicationMetadata(null); @@ -1367,8 +1393,8 @@ public class GitServiceCETest { Application application1 = applicationPageService.createApplication(testApplication).block(); - Mono> listMono = - gitService.listBranchForApplication(application1.getId(), false, "defaultBranch"); + Mono> listMono = commonGitServiceCE.listBranchForArtifact( + application1.getId(), false, "defaultBranch", ArtifactType.APPLICATION); StepVerifier.create(listMono) .expectErrorMatches(throwable -> throwable instanceof AppsmithException @@ -1380,22 +1406,23 @@ public class GitServiceCETest { @Test @WithUserDetails(value = "api_user") - public void listBranchForApplication_applicationWithInvalidGitConfig_throwError() throws IOException { + public void listBranchForArtifact_applicationWithInvalidGitConfig_throwError() throws IOException { - Mockito.when(gitFileUtils.checkIfDirectoryIsEmpty(any(Path.class))).thenReturn(Mono.just(true)); - Mockito.when(gitFileUtils.initializeReadme(any(Path.class), Mockito.anyString(), Mockito.anyString())) + Mockito.when(commonGitFileUtils.checkIfDirectoryIsEmpty(any(Path.class))) + .thenReturn(Mono.just(true)); + Mockito.when(commonGitFileUtils.initializeReadme(any(Path.class), Mockito.anyString(), Mockito.anyString())) .thenReturn(Mono.just(Paths.get("textPath"))); Application testApplication = new Application(); testApplication.setGitApplicationMetadata(null); - testApplication.setName("listBranchForApplication_GitFailure_ThrowError"); + testApplication.setName("listBranchForArtifact_GitFailure_ThrowError"); testApplication.setWorkspaceId(workspaceId); Application application1 = applicationPageService.createApplication(testApplication).block(); - Mono> listMono = - gitService.listBranchForApplication(application1.getId(), false, "defaultBranch"); + Mono> listMono = commonGitServiceCE.listBranchForArtifact( + application1.getId(), false, "defaultBranch", ArtifactType.APPLICATION); StepVerifier.create(listMono) .expectErrorMatches(throwable -> throwable instanceof AppsmithException @@ -1407,7 +1434,7 @@ public class GitServiceCETest { @Test @WithUserDetails(value = "api_user") - public void listBranchForApplication_defaultBranchNotChangesInRemote_Success() throws IOException, GitAPIException { + public void listBranchForArtifact_defaultBranchNotChangesInRemote_Success() throws IOException, GitAPIException { List branchList = List.of( createGitBranchDTO("defaultBranch", false), createGitBranchDTO("feature1", false), @@ -1423,8 +1450,9 @@ public class GitServiceCETest { Mockito.when(gitExecutor.cloneRemoteIntoArtifactRepo( any(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString())) .thenReturn(Mono.just("defaultBranch")); - Mockito.when(gitFileUtils.checkIfDirectoryIsEmpty(any(Path.class))).thenReturn(Mono.just(true)); - Mockito.when(gitFileUtils.initializeReadme(any(Path.class), Mockito.anyString(), Mockito.anyString())) + Mockito.when(commonGitFileUtils.checkIfDirectoryIsEmpty(any(Path.class))) + .thenReturn(Mono.just(true)); + Mockito.when(commonGitFileUtils.initializeReadme(any(Path.class), Mockito.anyString(), Mockito.anyString())) .thenReturn(Mono.just(Paths.get("textPath"))); Mockito.when(gitExecutor.fetchRemote( any(Path.class), @@ -1436,10 +1464,10 @@ public class GitServiceCETest { .thenReturn(Mono.just("status")); Application application1 = createApplicationConnectedToGit( - "listBranchForApplication_pruneBranchNoChangesInRemote_Success", "defaultBranch"); + "listBranchForArtifact_pruneBranchNoChangesInRemote_Success", "defaultBranch"); - Mono> listMono = - gitService.listBranchForApplication(application1.getId(), true, "defaultBranch"); + Mono> listMono = commonGitServiceCE.listBranchForArtifact( + application1.getId(), true, "defaultBranch", ArtifactType.APPLICATION); StepVerifier.create(listMono) .assertNext(listBranch -> { @@ -1450,7 +1478,7 @@ public class GitServiceCETest { @Test @WithUserDetails(value = "api_user") - public void listBranchForApplication_defaultBranchChangesInRemoteExistsInDB_Success() + public void listBranchForArtifact_defaultBranchChangesInRemoteExistsInDB_Success() throws IOException, GitAPIException { List branchList = List.of( createGitBranchDTO("defaultBranch", false), @@ -1467,8 +1495,9 @@ public class GitServiceCETest { Mockito.when(gitExecutor.cloneRemoteIntoArtifactRepo( any(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString())) .thenReturn(Mono.just("defaultBranch")); - Mockito.when(gitFileUtils.checkIfDirectoryIsEmpty(any(Path.class))).thenReturn(Mono.just(true)); - Mockito.when(gitFileUtils.initializeReadme(any(Path.class), Mockito.anyString(), Mockito.anyString())) + Mockito.when(commonGitFileUtils.checkIfDirectoryIsEmpty(any(Path.class))) + .thenReturn(Mono.just(true)); + Mockito.when(commonGitFileUtils.initializeReadme(any(Path.class), Mockito.anyString(), Mockito.anyString())) .thenReturn(Mono.just(Paths.get("textPath"))); Mockito.when(gitExecutor.fetchRemote( any(Path.class), @@ -1482,15 +1511,15 @@ public class GitServiceCETest { .thenReturn(Mono.just(true)); Application application1 = createApplicationConnectedToGit( - "listBranchForApplication_pruneBranchWithBranchNotExistsInDB_Success", "defaultBranch"); + "listBranchForArtifact_pruneBranchWithBranchNotExistsInDB_Success", "defaultBranch"); // Create branch Application application2 = createApplicationConnectedToGit( - "listBranchForApplication_defaultBranchChangesInRemoteExistsInDB_Success", "feature1"); + "listBranchForArtifact_defaultBranchChangesInRemoteExistsInDB_Success", "feature1"); application2.getGitApplicationMetadata().setDefaultApplicationId(application1.getId()); applicationService.save(application2).block(); - Mono applicationUpdatedMono = gitService - .listBranchForApplication(application1.getId(), true, "defaultBranch") + Mono applicationUpdatedMono = commonGitServiceCE + .listBranchForArtifact(application1.getId(), true, "defaultBranch", ArtifactType.APPLICATION) .then(applicationService.findById(application1.getId())); StepVerifier.create(applicationUpdatedMono) @@ -1512,7 +1541,7 @@ public class GitServiceCETest { @Test @WithUserDetails(value = "api_user") - public void listBranchForApplication_defaultBranchChangesInRemoteDoesNotExistsInDB_Success() + public void listBranchForArtifact_defaultBranchChangesInRemoteDoesNotExistsInDB_Success() throws IOException, GitAPIException { List branchList = List.of( createGitBranchDTO("defaultBranch", false), @@ -1530,8 +1559,9 @@ public class GitServiceCETest { Mockito.when(gitExecutor.cloneRemoteIntoArtifactRepo( any(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString())) .thenReturn(Mono.just("defaultBranch")); - Mockito.when(gitFileUtils.checkIfDirectoryIsEmpty(any(Path.class))).thenReturn(Mono.just(true)); - Mockito.when(gitFileUtils.initializeReadme(any(Path.class), Mockito.anyString(), Mockito.anyString())) + Mockito.when(commonGitFileUtils.checkIfDirectoryIsEmpty(any(Path.class))) + .thenReturn(Mono.just(true)); + Mockito.when(commonGitFileUtils.initializeReadme(any(Path.class), Mockito.anyString(), Mockito.anyString())) .thenReturn(Mono.just(Paths.get("textPath"))); Mockito.when(gitExecutor.fetchRemote( any(Path.class), @@ -1543,16 +1573,20 @@ public class GitServiceCETest { .thenReturn(Mono.just("status")); Mockito.when(gitExecutor.checkoutRemoteBranch(any(Path.class), Mockito.anyString())) .thenReturn(Mono.just("feature1")); - Mockito.when(gitFileUtils.reconstructApplicationJsonFromGitRepoWithAnalytics( - Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString())) + Mockito.when(commonGitFileUtils.reconstructArtifactExchangeJsonFromGitRepoWithAnalytics( + Mockito.anyString(), + Mockito.anyString(), + Mockito.anyString(), + Mockito.anyString(), + Mockito.any())) .thenReturn(Mono.just(applicationJson)); Application application1 = createApplicationConnectedToGit( - "listBranchForApplication_defaultBranchChangesInRemoteDoesNotExistsInDB_Success", "defaultBranch"); + "listBranchForArtifact_defaultBranchChangesInRemoteDoesNotExistsInDB_Success", "defaultBranch"); - Mono applicationUpdatedMono = gitService - .listBranchForApplication(application1.getId(), true, "defaultBranch") - .then(applicationService.findById(application1.getId())); + Mono applicationUpdatedMono = commonGitServiceCE + .listBranchForArtifact(application1.getId(), true, "defaultBranch", ArtifactType.APPLICATION) + .then(Mono.defer(() -> applicationService.findById(application1.getId()))); StepVerifier.create(applicationUpdatedMono) .assertNext(application -> { @@ -1582,11 +1616,14 @@ public class GitServiceCETest { gitStatusDTO.setBehindCount(0); gitStatusDTO.setIsClean(true); - Mockito.when(gitFileUtils.saveApplicationToLocalRepoWithAnalytics( - any(Path.class), any(ApplicationJson.class), Mockito.anyString())) + Mockito.when(commonGitFileUtils.saveArtifactToLocalRepo(any(Path.class), any(), Mockito.anyString())) .thenReturn(Mono.just(Paths.get("path"))); - Mockito.when(gitFileUtils.reconstructApplicationJsonFromGitRepoWithAnalytics( - Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString())) + Mockito.when(commonGitFileUtils.reconstructArtifactExchangeJsonFromGitRepoWithAnalytics( + Mockito.anyString(), + Mockito.anyString(), + Mockito.anyString(), + Mockito.anyString(), + Mockito.any())) .thenReturn(Mono.just(applicationJson)); Mockito.when(gitExecutor.pullApplication( any(Path.class), @@ -1608,8 +1645,8 @@ public class GitServiceCETest { Mockito.when(gitExecutor.resetToLastCommit(any(Path.class), Mockito.anyString())) .thenReturn(Mono.just(true)); - Mono applicationMono = gitService.pullApplication( - application.getId(), application.getGitApplicationMetadata().getBranchName()); + Mono applicationMono = commonGitServiceCE.pullArtifact( + application.getId(), application.getGitApplicationMetadata().getBranchName(), ArtifactType.APPLICATION); StepVerifier.create(applicationMono) .assertNext(gitPullDTO -> { @@ -1628,11 +1665,14 @@ public class GitServiceCETest { mergeStatusDTO.setStatus("2 commits pulled"); mergeStatusDTO.setMergeAble(true); - Mockito.when(gitFileUtils.saveApplicationToLocalRepoWithAnalytics( - any(Path.class), any(ApplicationJson.class), Mockito.anyString())) - .thenThrow(new IOException("Error accessing the file System")); - Mockito.when(gitFileUtils.reconstructApplicationJsonFromGitRepoWithAnalytics( - Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString())) + Mockito.when(commonGitFileUtils.saveArtifactToLocalRepo(any(Path.class), any(), Mockito.anyString())) + .thenReturn(Mono.error(new IOException("Error accessing the file System"))); + Mockito.when(commonGitFileUtils.reconstructArtifactExchangeJsonFromGitRepoWithAnalytics( + Mockito.anyString(), + Mockito.anyString(), + Mockito.anyString(), + Mockito.anyString(), + Mockito.any())) .thenReturn(Mono.just(new ApplicationJson())); Mockito.when(gitExecutor.fetchRemote( any(Path.class), @@ -1650,8 +1690,8 @@ public class GitServiceCETest { Mockito.anyString())) .thenReturn(Mono.just(mergeStatusDTO)); - Mono applicationMono = gitService.pullApplication( - application.getId(), application.getGitApplicationMetadata().getBranchName()); + Mono applicationMono = commonGitServiceCE.pullArtifact( + application.getId(), application.getGitApplicationMetadata().getBranchName(), ArtifactType.APPLICATION); StepVerifier.create(applicationMono) .expectErrorMatches(throwable -> throwable instanceof AppsmithException @@ -1673,11 +1713,14 @@ public class GitServiceCETest { GitStatusDTO statusDTO = new GitStatusDTO(); statusDTO.setIsClean(true); - Mockito.when(gitFileUtils.saveApplicationToLocalRepoWithAnalytics( - any(Path.class), any(ApplicationJson.class), Mockito.anyString())) + Mockito.when(commonGitFileUtils.saveArtifactToLocalRepo(any(Path.class), any(), Mockito.anyString())) .thenReturn(Mono.just(Paths.get(""))); - Mockito.when(gitFileUtils.reconstructApplicationJsonFromGitRepoWithAnalytics( - Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString())) + Mockito.when(commonGitFileUtils.reconstructArtifactExchangeJsonFromGitRepoWithAnalytics( + Mockito.anyString(), + Mockito.anyString(), + Mockito.anyString(), + Mockito.anyString(), + Mockito.any())) .thenReturn(Mono.justOrEmpty(applicationJson)); Mockito.when(gitExecutor.getStatus(any(), any())).thenReturn(Mono.just(statusDTO)); Mockito.when(gitExecutor.fetchRemote( @@ -1698,8 +1741,8 @@ public class GitServiceCETest { Mockito.when(gitExecutor.resetToLastCommit(any(Path.class), Mockito.anyString())) .thenReturn(Mono.just(true)); - Mono applicationMono = gitService.pullApplication( - application.getId(), application.getGitApplicationMetadata().getBranchName()); + Mono applicationMono = commonGitServiceCE.pullArtifact( + application.getId(), application.getGitApplicationMetadata().getBranchName(), ArtifactType.APPLICATION); StepVerifier.create(applicationMono) .assertNext(gitPullDTO -> { @@ -1732,8 +1775,7 @@ public class GitServiceCETest { gitStatusDTO.setBehindCount(0); gitStatusDTO.setIsClean(true); - Mockito.when(gitFileUtils.saveApplicationToLocalRepoWithAnalytics( - any(Path.class), any(ApplicationJson.class), Mockito.anyString())) + Mockito.when(commonGitFileUtils.saveArtifactToLocalRepo(any(Path.class), any(), Mockito.anyString())) .thenReturn(Mono.just(Paths.get(""))); Mockito.when(gitExecutor.isMergeBranch(any(Path.class), Mockito.anyString(), Mockito.anyString())) .thenReturn(Mono.just(mergeStatus)); @@ -1748,11 +1790,12 @@ public class GitServiceCETest { .thenReturn(Mono.just("fetchResult")); Mockito.when(gitExecutor.resetToLastCommit(any(Path.class), Mockito.anyString())) .thenReturn(Mono.just(TRUE)); - Mockito.when(gitFileUtils.saveApplicationToLocalRepoWithAnalytics( - any(Path.class), any(ApplicationJson.class), Mockito.anyString())) + Mockito.when(commonGitFileUtils.saveArtifactToLocalRepoWithAnalytics( + any(Path.class), any(), Mockito.anyString())) .thenReturn(Mono.just(Paths.get(""))); - Mono applicationMono = gitService.isBranchMergeable(application.getId(), gitMergeDTO); + Mono applicationMono = + commonGitServiceCE.isBranchMergeable(application.getId(), gitMergeDTO, ArtifactType.APPLICATION); StepVerifier.create(applicationMono) .assertNext(s -> assertThat(s.isMergeAble()).isTrue()) @@ -1775,8 +1818,8 @@ public class GitServiceCETest { MergeStatusDTO mergeStatus = new MergeStatusDTO(); mergeStatus.setMergeAble(false); - Mockito.when(gitFileUtils.saveApplicationToLocalRepoWithAnalytics( - any(Path.class), any(ApplicationJson.class), Mockito.anyString())) + Mockito.when(commonGitFileUtils.saveArtifactToLocalRepoWithAnalytics( + any(Path.class), any(), Mockito.anyString())) .thenReturn(Mono.just(Paths.get(""))); Mockito.when(gitExecutor.isMergeBranch(any(Path.class), Mockito.anyString(), Mockito.anyString())) .thenReturn(Mono.just(mergeStatus)); @@ -1791,11 +1834,12 @@ public class GitServiceCETest { .thenReturn(Mono.just("fetchResult")); Mockito.when(gitExecutor.resetToLastCommit(any(Path.class), Mockito.anyString())) .thenReturn(Mono.just(Boolean.FALSE)); - Mockito.when(gitFileUtils.saveApplicationToLocalRepoWithAnalytics( - any(Path.class), any(ApplicationJson.class), Mockito.anyString())) + Mockito.when(commonGitFileUtils.saveArtifactToLocalRepoWithAnalytics( + any(Path.class), any(), Mockito.anyString())) .thenReturn(Mono.just(Paths.get(""))); - Mono applicationMono = gitService.isBranchMergeable(application.getId(), gitMergeDTO); + Mono applicationMono = + commonGitServiceCE.isBranchMergeable(application.getId(), gitMergeDTO, ArtifactType.APPLICATION); StepVerifier.create(applicationMono) .assertNext(s -> { @@ -1825,8 +1869,7 @@ public class GitServiceCETest { MergeStatusDTO mergeStatus = new MergeStatusDTO(); mergeStatus.setMergeAble(false); - Mockito.when(gitFileUtils.saveApplicationToLocalRepoWithAnalytics( - any(Path.class), any(ApplicationJson.class), Mockito.anyString())) + Mockito.when(commonGitFileUtils.saveArtifactToLocalRepo(any(Path.class), any(), Mockito.anyString())) .thenReturn(Mono.just(Paths.get("path"))); Mockito.when(gitExecutor.isMergeBranch(any(Path.class), Mockito.anyString(), Mockito.anyString())) .thenReturn(Mono.just(mergeStatus)); @@ -1841,12 +1884,12 @@ public class GitServiceCETest { .thenReturn(Mono.just("fetchResult")); Mockito.when(gitExecutor.resetToLastCommit(any(Path.class), Mockito.anyString())) .thenReturn(Mono.just(Boolean.FALSE)); - Mockito.when(gitFileUtils.saveApplicationToLocalRepoWithAnalytics( - any(Path.class), any(ApplicationJson.class), Mockito.anyString())) + Mockito.when(commonGitFileUtils.saveArtifactToLocalRepoWithAnalytics( + any(Path.class), any(), Mockito.anyString())) .thenReturn(Mono.just(Paths.get(""))); - Mono applicationMono = - gitService.isBranchMergeable(gitConnectedApplication.getId(), gitMergeDTO); + Mono applicationMono = commonGitServiceCE.isBranchMergeable( + gitConnectedApplication.getId(), gitMergeDTO, ArtifactType.APPLICATION); StepVerifier.create(applicationMono) .assertNext(s -> { @@ -1864,8 +1907,8 @@ public class GitServiceCETest { gitMergeDTO.setSourceBranch("origin/branch2"); gitMergeDTO.setDestinationBranch("defaultBranch"); - Mono applicationMono = - gitService.isBranchMergeable(gitConnectedApplication.getId(), gitMergeDTO); + Mono applicationMono = commonGitServiceCE.isBranchMergeable( + gitConnectedApplication.getId(), gitMergeDTO, ArtifactType.APPLICATION); StepVerifier.create(applicationMono) .expectErrorMatches(throwable -> throwable instanceof AppsmithException @@ -1897,13 +1940,18 @@ public class GitServiceCETest { .thenReturn(Mono.just("fetchResult")); Mockito.when(gitExecutor.checkoutRemoteBranch(any(Path.class), Mockito.anyString())) .thenReturn(Mono.just("testBranch")); - Mockito.when(gitFileUtils.reconstructApplicationJsonFromGitRepoWithAnalytics( - Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString())) + Mockito.when(commonGitFileUtils.reconstructArtifactExchangeJsonFromGitRepoWithAnalytics( + Mockito.anyString(), + Mockito.anyString(), + Mockito.anyString(), + Mockito.anyString(), + Mockito.any())) .thenReturn(Mono.just(applicationJson)); Mockito.when(gitExecutor.listBranches(any())).thenReturn(Mono.just(branchList)); - Mono applicationMono = gitService - .checkoutBranch(gitConnectedApplication.getId(), "origin/branchNotInLocal", true) + Mono applicationMono = commonGitServiceCE + .checkoutBranch( + gitConnectedApplication.getId(), "origin/branchNotInLocal", true, ArtifactType.APPLICATION) .flatMap(application1 -> applicationService.findByBranchNameAndDefaultApplicationId( "branchNotInLocal", gitConnectedApplication.getId(), READ_APPLICATIONS)); @@ -1911,7 +1959,7 @@ public class GitServiceCETest { .assertNext(application1 -> { assertThat(application1.getGitApplicationMetadata().getBranchName()) .isEqualTo("branchNotInLocal"); - assertThat(application1.getGitApplicationMetadata().getDefaultApplicationId()) + assertThat(application1.getGitApplicationMetadata().getDefaultArtifactId()) .isEqualTo(gitConnectedApplication.getId()); }) .verifyComplete(); @@ -1954,13 +2002,17 @@ public class GitServiceCETest { .thenReturn(Mono.just("fetchResult")); Mockito.when(gitExecutor.checkoutRemoteBranch(any(Path.class), Mockito.anyString())) .thenReturn(Mono.just("testBranch")); - Mockito.when(gitFileUtils.reconstructApplicationJsonFromGitRepoWithAnalytics( - Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString())) + Mockito.when(commonGitFileUtils.reconstructArtifactExchangeJsonFromGitRepoWithAnalytics( + Mockito.anyString(), + Mockito.anyString(), + Mockito.anyString(), + Mockito.anyString(), + Mockito.any())) .thenReturn(Mono.just(applicationJson)); Mockito.when(gitExecutor.listBranches(any())).thenReturn(Mono.just(branchList)); // set custom theme to the git connected application - Mono setCustomThemeToGitConnectedAppMono = themeService + Theme customTheme = themeService .getSystemTheme(Theme.DEFAULT_THEME_NAME) .flatMap(systemTheme -> { // we'll create a custom theme by copying properties from the system theme @@ -1974,25 +2026,22 @@ public class GitServiceCETest { gitConnectedApplication.setEditModeThemeId(savedCustomTheme.getId()); gitConnectedApplication.setPublishedModeThemeId(savedCustomTheme.getId()); return applicationService.save(gitConnectedApplication).thenReturn(savedCustomTheme); - }); + }) + .block(); - Mono> resultMono = - setCustomThemeToGitConnectedAppMono.flatMap(theme -> { - return gitService - .checkoutBranch(gitConnectedApplication.getId(), "origin/branchNotInLocal2", true) - .then(Mono.defer(() -> applicationService.findByBranchNameAndDefaultApplicationId( - "branchNotInLocal2", gitConnectedApplication.getId(), READ_APPLICATIONS))) - .flatMap(application -> { - Mono defaultAppTheme = Mono.just(theme); - Mono branchedAppMono = Mono.just(application); - Mono editThemeMono = themeService.getApplicationTheme( - gitConnectedApplication.getId(), ApplicationMode.EDIT, "branchNotInLocal2"); - Mono publishedThemeMono = themeService.getApplicationTheme( - gitConnectedApplication.getId(), - ApplicationMode.PUBLISHED, - "branchNotInLocal2"); - return Mono.zip(defaultAppTheme, branchedAppMono, editThemeMono, publishedThemeMono); - }); + Mono> resultMono = commonGitServiceCE + .checkoutBranch( + gitConnectedApplication.getId(), "origin/branchNotInLocal2", true, ArtifactType.APPLICATION) + .flatMap(checkedOutArtifact -> applicationService.findByBranchNameAndDefaultApplicationId( + "branchNotInLocal2", gitConnectedApplication.getId(), READ_APPLICATIONS)) + .flatMap(application -> { + Mono defaultAppTheme = Mono.just(customTheme); + Mono branchedAppMono = Mono.just(application); + Mono editThemeMono = themeService.getApplicationTheme( + gitConnectedApplication.getId(), ApplicationMode.EDIT, "branchNotInLocal2"); + Mono publishedThemeMono = themeService.getApplicationTheme( + gitConnectedApplication.getId(), ApplicationMode.PUBLISHED, "branchNotInLocal2"); + return Mono.zip(defaultAppTheme, branchedAppMono, editThemeMono, publishedThemeMono); }); StepVerifier.create(resultMono) @@ -2041,8 +2090,9 @@ public class GitServiceCETest { Mockito.anyBoolean())) .thenReturn(Mono.just("fetchResult")); - Mono applicationMono = - gitService.checkoutBranch(gitConnectedApplication.getId(), "origin/branchInLocal", true); + Mono applicationMono = commonGitServiceCE + .checkoutBranch(gitConnectedApplication.getId(), "origin/branchInLocal", true, ArtifactType.APPLICATION) + .map(artifact -> (Application) artifact); StepVerifier.create(applicationMono) .expectErrorMatches(throwable -> throwable instanceof AppsmithException @@ -2056,7 +2106,9 @@ public class GitServiceCETest { @Test @WithUserDetails(value = "api_user") public void checkoutBranch_branchNotProvided_throwInvalidParameterError() { - Mono applicationMono = gitService.checkoutBranch(gitConnectedApplication.getId(), null, true); + Mono applicationMono = commonGitServiceCE + .checkoutBranch(gitConnectedApplication.getId(), null, true, ArtifactType.APPLICATION) + .map(artifact -> (Application) artifact); StepVerifier.create(applicationMono) .expectErrorMatches(throwable -> throwable instanceof AppsmithException @@ -2068,14 +2120,14 @@ public class GitServiceCETest { @Test @WithUserDetails(value = "api_user") - public void commitApplication_noChangesInLocal_emptyCommitMessage() throws GitAPIException, IOException { + public void commitArtifact_noChangesInLocal_emptyCommitMessage() throws GitAPIException, IOException { GitCommitDTO commitDTO = new GitCommitDTO(); commitDTO.setDoPush(false); commitDTO.setCommitMessage("empty commit"); - Mockito.when(gitFileUtils.saveApplicationToLocalRepoWithAnalytics( - any(Path.class), any(ApplicationJson.class), Mockito.anyString())) + Mockito.when(commonGitFileUtils.saveArtifactToLocalRepoWithAnalytics( + any(Path.class), any(), Mockito.anyString())) .thenReturn(Mono.just(Paths.get(""))); Mockito.when(gitExecutor.commitArtifact( any(Path.class), @@ -2086,8 +2138,8 @@ public class GitServiceCETest { Mockito.anyBoolean())) .thenReturn(Mono.error(new EmptyCommitException("nothing to commit"))); - Mono commitMono = - gitService.commitApplication(commitDTO, gitConnectedApplication.getId(), DEFAULT_BRANCH); + Mono commitMono = commonGitServiceCE.commitArtifact( + commitDTO, gitConnectedApplication.getId(), DEFAULT_BRANCH, ArtifactType.APPLICATION); StepVerifier.create(commitMono) .assertNext(commitMsg -> { @@ -2098,7 +2150,7 @@ public class GitServiceCETest { @Test @WithUserDetails(value = "api_user") - public void commitApplication_applicationNotConnectedToGit_throwInvalidGitConfigException() { + public void commitArtifact_applicationNotConnectedToGit_throwInvalidGitConfigException() { Application application = new Application(); application.setName("sampleAppNotConnectedToGit"); @@ -2110,7 +2162,8 @@ public class GitServiceCETest { commitDTO.setDoPush(false); commitDTO.setCommitMessage("empty commit"); - Mono commitMono = gitService.commitApplication(commitDTO, application.getId(), DEFAULT_BRANCH); + Mono commitMono = commonGitServiceCE.commitArtifact( + commitDTO, application.getId(), DEFAULT_BRANCH, ArtifactType.APPLICATION); StepVerifier.create(commitMono) .expectErrorMatches(throwable -> throwable instanceof AppsmithException @@ -2122,18 +2175,17 @@ public class GitServiceCETest { @Test @WithUserDetails(value = "api_user") - public void commitApplication_localRepoNotAvailable_throwRepoNotFoundException() - throws GitAPIException, IOException { + public void commitArtifact_localRepoNotAvailable_throwRepoNotFoundException() throws GitAPIException, IOException { GitCommitDTO commitDTO = new GitCommitDTO(); commitDTO.setDoPush(false); commitDTO.setCommitMessage("empty commit"); - Mono commitMono = - gitService.commitApplication(commitDTO, gitConnectedApplication.getId(), DEFAULT_BRANCH); + Mono commitMono = commonGitServiceCE.commitArtifact( + commitDTO, gitConnectedApplication.getId(), DEFAULT_BRANCH, ArtifactType.APPLICATION); - Mockito.when(gitFileUtils.saveApplicationToLocalRepoWithAnalytics( - any(Path.class), any(ApplicationJson.class), Mockito.anyString())) + Mockito.when(commonGitFileUtils.saveArtifactToLocalRepoWithAnalytics( + any(Path.class), any(), Mockito.anyString())) .thenReturn( Mono.error(new RepositoryNotFoundException(AppsmithError.REPOSITORY_NOT_FOUND.getMessage()))); @@ -2148,14 +2200,14 @@ public class GitServiceCETest { @Test @WithUserDetails(value = "api_user") - public void commitApplication_commitChanges_success() throws GitAPIException, IOException { + public void commitArtifact_commitChanges_success() throws GitAPIException, IOException { GitCommitDTO commitDTO = new GitCommitDTO(); commitDTO.setDoPush(false); commitDTO.setCommitMessage("commit message"); - Mockito.when(gitFileUtils.saveApplicationToLocalRepoWithAnalytics( - any(Path.class), any(ApplicationJson.class), Mockito.anyString())) + Mockito.when(commonGitFileUtils.saveArtifactToLocalRepoWithAnalytics( + any(Path.class), any(), Mockito.anyString())) .thenReturn(Mono.just(Paths.get(""))); Mockito.when(gitExecutor.commitArtifact( any(Path.class), @@ -2166,8 +2218,8 @@ public class GitServiceCETest { Mockito.anyBoolean())) .thenReturn(Mono.just("sample response for commit")); - Mono commitMono = - gitService.commitApplication(commitDTO, gitConnectedApplication.getId(), DEFAULT_BRANCH); + Mono commitMono = commonGitServiceCE.commitArtifact( + commitDTO, gitConnectedApplication.getId(), DEFAULT_BRANCH, ArtifactType.APPLICATION); StepVerifier.create(commitMono) .assertNext(commitMsg -> { @@ -2178,13 +2230,13 @@ public class GitServiceCETest { @Test @WithUserDetails(value = "api_user") - public void commitApplication_BranchIsProtected_Failure() throws GitAPIException, IOException { + public void commitArtifact_BranchIsProtected_Failure() throws GitAPIException, IOException { GitCommitDTO commitDTO = new GitCommitDTO(); commitDTO.setDoPush(false); commitDTO.setCommitMessage("commit message"); - Mockito.when(gitFileUtils.saveApplicationToLocalRepoWithAnalytics( - any(Path.class), any(ApplicationJson.class), Mockito.anyString())) + Mockito.when(commonGitFileUtils.saveArtifactToLocalRepoWithAnalytics( + any(Path.class), any(), Mockito.anyString())) .thenReturn(Mono.just(Paths.get(""))); Mockito.when(gitExecutor.commitArtifact( any(Path.class), @@ -2195,9 +2247,11 @@ public class GitServiceCETest { Mockito.anyBoolean())) .thenReturn(Mono.just("sample response for commit")); - Mono commitMono = gitService - .updateProtectedBranches(gitConnectedApplication.getId(), List.of(DEFAULT_BRANCH)) - .then(gitService.commitApplication(commitDTO, gitConnectedApplication.getId(), DEFAULT_BRANCH)); + Mono commitMono = commonGitServiceCE + .updateProtectedBranches( + gitConnectedApplication.getId(), List.of(DEFAULT_BRANCH), ArtifactType.APPLICATION) + .then(commonGitServiceCE.commitArtifact( + commitDTO, gitConnectedApplication.getId(), DEFAULT_BRANCH, ArtifactType.APPLICATION)); StepVerifier.create(commitMono).verifyError(); } @@ -2210,8 +2264,8 @@ public class GitServiceCETest { commitDTO.setDoPush(true); commitDTO.setCommitMessage("commit message"); - Mockito.when(gitFileUtils.saveApplicationToLocalRepoWithAnalytics( - any(Path.class), any(ApplicationJson.class), Mockito.anyString())) + Mockito.when(commonGitFileUtils.saveArtifactToLocalRepoWithAnalytics( + any(Path.class), any(), Mockito.anyString())) .thenReturn(Mono.just(Paths.get(""))); Mockito.when(gitExecutor.commitArtifact( any(Path.class), @@ -2231,8 +2285,8 @@ public class GitServiceCETest { Mockito.anyString())) .thenReturn(Mono.just("pushed successfully")); - Mono commitAndPushMono = - gitService.commitApplication(commitDTO, gitConnectedApplication.getId(), DEFAULT_BRANCH); + Mono commitAndPushMono = commonGitServiceCE.commitArtifact( + commitDTO, gitConnectedApplication.getId(), DEFAULT_BRANCH, ArtifactType.APPLICATION); StepVerifier.create(commitAndPushMono.zipWhen(status -> applicationService.findByIdAndBranchName(gitConnectedApplication.getId(), DEFAULT_BRANCH))) @@ -2257,8 +2311,8 @@ public class GitServiceCETest { commitDTO.setDoPush(true); commitDTO.setCommitMessage("empty commit"); - Mockito.when(gitFileUtils.saveApplicationToLocalRepoWithAnalytics( - any(Path.class), any(ApplicationJson.class), Mockito.anyString())) + Mockito.when(commonGitFileUtils.saveArtifactToLocalRepoWithAnalytics( + any(Path.class), any(), Mockito.anyString())) .thenReturn(Mono.just(Paths.get(""))); Mockito.when(gitExecutor.commitArtifact( any(Path.class), @@ -2278,8 +2332,8 @@ public class GitServiceCETest { Mockito.anyString())) .thenReturn(Mono.just("pushed successfully")); - Mono commitAndPushMono = - gitService.commitApplication(commitDTO, gitConnectedApplication.getId(), DEFAULT_BRANCH); + Mono commitAndPushMono = commonGitServiceCE.commitArtifact( + commitDTO, gitConnectedApplication.getId(), DEFAULT_BRANCH, ArtifactType.APPLICATION); StepVerifier.create(commitAndPushMono) .assertNext(commitAndPushMsg -> { @@ -2294,29 +2348,30 @@ public class GitServiceCETest { */ @Test @WithUserDetails(value = "api_user") - public void commitApplication_pushFails_verifyAppNotPublished_throwUpstreamChangesFoundException() + public void commitArtifact_pushFails_verifyAppNotPublished_throwUpstreamChangesFoundException() throws GitAPIException, IOException { // Create and fetch the application state before adding new page Application testApplication = createApplicationConnectedToGit("gitConnectedPushFailApplication", DEFAULT_BRANCH); - Application preCommitApplication = applicationService + Application precommitArtifact = applicationService .getApplicationByDefaultApplicationIdAndDefaultBranch(testApplication.getId()) .block(); // Creating a new page to commit to git PageDTO testPage = new PageDTO(); testPage.setName("GitServiceTestPageGitPushFail"); - testPage.setApplicationId(preCommitApplication.getId()); + testPage.setApplicationId(precommitArtifact.getId()); PageDTO createdPage = applicationPageService.createPage(testPage).block(); GitCommitDTO commitDTO = new GitCommitDTO(); commitDTO.setDoPush(true); commitDTO.setCommitMessage("New page added"); - Mono commitMono = gitService.commitApplication(commitDTO, preCommitApplication.getId(), DEFAULT_BRANCH); + Mono commitMono = commonGitServiceCE.commitArtifact( + commitDTO, precommitArtifact.getId(), DEFAULT_BRANCH, ArtifactType.APPLICATION); Mono committedApplicationMono = - applicationService.getApplicationByDefaultApplicationIdAndDefaultBranch(preCommitApplication.getId()); + applicationService.getApplicationByDefaultApplicationIdAndDefaultBranch(precommitArtifact.getId()); // Mocking a git push failure Mockito.when(gitExecutor.pushApplication(any(), any(), any(), any(), any())) @@ -2337,7 +2392,7 @@ public class GitServiceCETest { .assertNext(application -> { List publishedPages = application.getPublishedPages(); assertThat(application.getPublishedPages()) - .hasSize(preCommitApplication.getPublishedPages().size()); + .hasSize(precommitArtifact.getPublishedPages().size()); publishedPages.forEach(publishedPage -> { assertThat(publishedPage.getId().equals(createdPage.getId())) .isFalse(); @@ -2348,24 +2403,25 @@ public class GitServiceCETest { @Test @WithUserDetails(value = "api_user") - public void commitApplication_protectedBranch_pushFails() throws GitAPIException, IOException { + public void commitArtifact_protectedBranch_pushFails() throws GitAPIException, IOException { // Create and fetch the application state before adding new page Application testApplication = - createApplicationConnectedToGit("commitApplication_protectedBranch_pushFails", DEFAULT_BRANCH); - Application preCommitApplication = applicationService + createApplicationConnectedToGit("commitArtifact_protectedBranch_pushFails", DEFAULT_BRANCH); + Application precommitArtifact = applicationService .getApplicationByDefaultApplicationIdAndDefaultBranch(testApplication.getId()) .block(); // Creating a new page to commit to git PageDTO testPage = new PageDTO(); testPage.setName("GitServiceTestPageGitPushFail"); - testPage.setApplicationId(preCommitApplication.getId()); + testPage.setApplicationId(precommitArtifact.getId()); PageDTO createdPage = applicationPageService.createPage(testPage).block(); GitCommitDTO commitDTO = new GitCommitDTO(); commitDTO.setDoPush(true); commitDTO.setCommitMessage("New page added"); - Mono commitMono = gitService.commitApplication(commitDTO, preCommitApplication.getId(), DEFAULT_BRANCH); + Mono commitMono = commonGitServiceCE.commitArtifact( + commitDTO, precommitArtifact.getId(), DEFAULT_BRANCH, ArtifactType.APPLICATION); // Mocking a git push failure Mockito.when(gitExecutor.pushApplication(any(), any(), any(), any(), any())) @@ -2392,10 +2448,13 @@ public class GitServiceCETest { GitBranchDTO createGitBranchDTO = new GitBranchDTO(); createGitBranchDTO.setBranchName("origin/createNewBranch"); - Mono createBranchMono = gitService.createBranch( - gitConnectedApplication.getId(), - createGitBranchDTO, - gitConnectedApplication.getGitApplicationMetadata().getBranchName()); + Mono createBranchMono = commonGitServiceCE + .createBranch( + gitConnectedApplication.getId(), + createGitBranchDTO, + gitConnectedApplication.getGitApplicationMetadata().getBranchName(), + ArtifactType.APPLICATION) + .map(artifact -> (Application) artifact); StepVerifier.create(createBranchMono) .expectErrorMatches(throwable -> throwable instanceof AppsmithException @@ -2428,10 +2487,13 @@ public class GitServiceCETest { Mockito.anyBoolean())) .thenReturn(Mono.just("fetchResult")); Mockito.when(gitExecutor.listBranches(any())).thenReturn(Mono.just(branchList)); - Mono createBranchMono = gitService.createBranch( - gitConnectedApplication.getId(), - createGitBranchDTO, - gitConnectedApplication.getGitApplicationMetadata().getBranchName()); + Mono createBranchMono = commonGitServiceCE + .createBranch( + gitConnectedApplication.getId(), + createGitBranchDTO, + gitConnectedApplication.getGitApplicationMetadata().getBranchName(), + ArtifactType.APPLICATION) + .map(artifact -> (Application) artifact); StepVerifier.create(createBranchMono) .expectErrorMatches(throwable -> throwable instanceof AppsmithException @@ -2464,8 +2526,8 @@ public class GitServiceCETest { Mockito.when(gitExecutor.listBranches(any())).thenReturn(Mono.just(new ArrayList<>())); Mockito.when(gitExecutor.createAndCheckoutToBranch(any(), any())) .thenReturn(Mono.just(createGitBranchDTO.getBranchName())); - Mockito.when(gitFileUtils.saveApplicationToLocalRepoWithAnalytics( - any(Path.class), any(ApplicationJson.class), Mockito.anyString())) + Mockito.when(commonGitFileUtils.saveArtifactToLocalRepoWithAnalytics( + any(Path.class), any(), Mockito.anyString())) .thenReturn(Mono.just(Paths.get(""))); Mockito.when(gitExecutor.commitArtifact( any(Path.class), @@ -2488,8 +2550,9 @@ public class GitServiceCETest { Mockito.when(gitExecutor.cloneRemoteIntoArtifactRepo( any(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString())) .thenReturn(Mono.just(DEFAULT_BRANCH)); - Mockito.when(gitFileUtils.checkIfDirectoryIsEmpty(any(Path.class))).thenReturn(Mono.just(true)); - Mockito.when(gitFileUtils.initializeReadme(any(Path.class), Mockito.anyString(), Mockito.anyString())) + Mockito.when(commonGitFileUtils.checkIfDirectoryIsEmpty(any(Path.class))) + .thenReturn(Mono.just(true)); + Mockito.when(commonGitFileUtils.initializeReadme(any(Path.class), Mockito.anyString(), Mockito.anyString())) .thenReturn(Mono.just(Paths.get("textPath"))); Application testApplication = new Application(); @@ -2575,13 +2638,16 @@ public class GitServiceCETest { layout.getId(), layout)), layoutCollectionService.createCollection(actionCollectionDTO, null)) - .then(gitService.connectApplicationToGit(application.getId(), gitConnectDTO, "origin")); + .then(commonGitServiceCE.connectArtifactToGit( + application.getId(), gitConnectDTO, "origin", ArtifactType.APPLICATION)) + .map(artifact -> (Application) artifact); }) - .flatMap(application -> gitService + .flatMap(application -> commonGitServiceCE .createBranch( application.getId(), createGitBranchDTO, - application.getGitApplicationMetadata().getBranchName()) + application.getGitApplicationMetadata().getBranchName(), + ArtifactType.APPLICATION) .then(applicationService.findByBranchNameAndDefaultApplicationId( createGitBranchDTO.getBranchName(), application.getId(), READ_APPLICATIONS))); @@ -2596,7 +2662,7 @@ public class GitServiceCETest { .findNewPagesByApplicationId(application.getId(), READ_PAGES) .collectList(), applicationService.findById( - application.getGitApplicationMetadata().getDefaultApplicationId())))) + application.getGitApplicationMetadata().getDefaultArtifactId())))) .assertNext(tuple -> { Application application = tuple.getT1(); List actionList = tuple.getT2().getT1(); @@ -2606,8 +2672,8 @@ public class GitServiceCETest { GitArtifactMetadata gitData = application.getGitApplicationMetadata(); assertThat(application).isNotNull(); - assertThat(application.getId()).isNotEqualTo(gitData.getDefaultApplicationId()); - assertThat(gitData.getDefaultApplicationId()).isEqualTo(parentApplication.getId()); + assertThat(application.getId()).isNotEqualTo(gitData.getDefaultArtifactId()); + assertThat(gitData.getDefaultArtifactId()).isEqualTo(parentApplication.getId()); assertThat(gitData.getBranchName()).isEqualTo(createGitBranchDTO.getBranchName()); assertThat(gitData.getDefaultBranchName()).isNotEmpty(); assertThat(gitData.getRemoteUrl()).isNotEmpty(); @@ -2703,8 +2769,8 @@ public class GitServiceCETest { Mockito.when(gitExecutor.listBranches(any())).thenReturn(Mono.just(new ArrayList<>())); Mockito.when(gitExecutor.createAndCheckoutToBranch(any(), any())) .thenReturn(Mono.just(createGitBranchDTO.getBranchName())); - Mockito.when(gitFileUtils.saveApplicationToLocalRepoWithAnalytics( - any(Path.class), any(ApplicationJson.class), Mockito.anyString())) + Mockito.when(commonGitFileUtils.saveArtifactToLocalRepoWithAnalytics( + any(Path.class), any(), Mockito.anyString())) .thenReturn(Mono.just(Paths.get(""))); Mockito.when(gitExecutor.commitArtifact( any(Path.class), @@ -2727,9 +2793,11 @@ public class GitServiceCETest { Mockito.when(gitExecutor.cloneRemoteIntoArtifactRepo( any(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString())) .thenReturn(Mono.just(DEFAULT_BRANCH)); - Mockito.when(gitFileUtils.checkIfDirectoryIsEmpty(any(Path.class))).thenReturn(Mono.just(true)); - Mockito.when(gitFileUtils.initializeReadme(any(Path.class), Mockito.anyString(), Mockito.anyString())) + Mockito.when(commonGitFileUtils.checkIfDirectoryIsEmpty(any(Path.class))) + .thenReturn(Mono.just(true)); + Mockito.when(commonGitFileUtils.initializeReadme(any(Path.class), Mockito.anyString(), Mockito.anyString())) .thenReturn(Mono.just(Paths.get("textPath"))); + Mockito.when(commonGitFileUtils.deleteLocalRepo(any(Path.class))).thenReturn(Mono.just(true)); Application testApplication = new Application(); GitArtifactMetadata gitArtifactMetadata = new GitArtifactMetadata(); @@ -2758,17 +2826,20 @@ public class GitServiceCETest { theme.setName("Custom theme"); return themeService.updateTheme(application.getId(), null, theme); }) - .then(gitService.connectApplicationToGit(application.getId(), gitConnectDTO, "origin")); + .then(commonGitServiceCE.connectArtifactToGit( + application.getId(), gitConnectDTO, "origin", ArtifactType.APPLICATION)) + .map(artifact -> (Application) artifact); }) - .flatMap(application -> gitService + .flatMap(application -> commonGitServiceCE .createBranch( application.getId(), createGitBranchDTO, - application.getGitApplicationMetadata().getBranchName()) + application.getGitApplicationMetadata().getBranchName(), + ArtifactType.APPLICATION) .then(applicationService.findByBranchNameAndDefaultApplicationId( createGitBranchDTO.getBranchName(), application.getId(), READ_APPLICATIONS))) .zipWhen(application -> applicationService.findById( - application.getGitApplicationMetadata().getDefaultApplicationId())); + application.getGitApplicationMetadata().getDefaultArtifactId())); StepVerifier.create(createBranchMono) .assertNext(tuple -> { @@ -2794,8 +2865,8 @@ public class GitServiceCETest { Mockito.when(gitExecutor.listBranches(any())).thenReturn(Mono.just(new ArrayList<>())); Mockito.when(gitExecutor.createAndCheckoutToBranch(any(), any())) .thenReturn(Mono.just(createGitBranchDTO.getBranchName())); - Mockito.when(gitFileUtils.saveApplicationToLocalRepoWithAnalytics( - any(Path.class), any(ApplicationJson.class), Mockito.anyString())) + Mockito.when(commonGitFileUtils.saveArtifactToLocalRepoWithAnalytics( + any(Path.class), any(), Mockito.anyString())) .thenReturn(Mono.just(Paths.get(""))); Mockito.when(gitExecutor.commitArtifact( any(Path.class), @@ -2818,8 +2889,9 @@ public class GitServiceCETest { Mockito.when(gitExecutor.cloneRemoteIntoArtifactRepo( any(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString())) .thenReturn(Mono.just(DEFAULT_BRANCH)); - Mockito.when(gitFileUtils.checkIfDirectoryIsEmpty(any(Path.class))).thenReturn(Mono.just(true)); - Mockito.when(gitFileUtils.initializeReadme(any(Path.class), Mockito.anyString(), Mockito.anyString())) + Mockito.when(commonGitFileUtils.checkIfDirectoryIsEmpty(any(Path.class))) + .thenReturn(Mono.just(true)); + Mockito.when(commonGitFileUtils.initializeReadme(any(Path.class), Mockito.anyString(), Mockito.anyString())) .thenReturn(Mono.just(Paths.get("textPath"))); } @@ -2845,13 +2917,15 @@ public class GitServiceCETest { Mono> createBranchMono = applicationPageService .createApplication(testApplication) - .flatMap( - application -> gitService.connectApplicationToGit(application.getId(), gitConnectDTO, "origin")) - .flatMap(application -> gitService + .flatMap(application -> commonGitServiceCE.connectArtifactToGit( + application.getId(), gitConnectDTO, "origin", ArtifactType.APPLICATION)) + .map(artifact -> (Application) artifact) + .flatMap(application -> commonGitServiceCE .createBranch( application.getId(), createGitBranchDTO, - application.getGitApplicationMetadata().getBranchName()) + application.getGitApplicationMetadata().getBranchName(), + ArtifactType.APPLICATION) .then(applicationService.findByBranchNameAndDefaultApplicationId( createGitBranchDTO.getBranchName(), application.getId(), READ_APPLICATIONS))) .flatMap(branchedApplication -> { @@ -2872,11 +2946,11 @@ public class GitServiceCETest { return Mono.just(branchedApplication); }) .flatMap(branchedApplication -> applicationService.update( - branchedApplication.getGitApplicationMetadata().getDefaultApplicationId(), + branchedApplication.getGitApplicationMetadata().getDefaultArtifactId(), branchedApplication, branchedApplication.getGitApplicationMetadata().getBranchName())) .zipWhen(application -> applicationService.findById( - application.getGitApplicationMetadata().getDefaultApplicationId())); + application.getGitApplicationMetadata().getDefaultArtifactId())); StepVerifier.create(createBranchMono) .assertNext(tuple -> { @@ -2934,13 +3008,15 @@ public class GitServiceCETest { Mono> createBranchMono = applicationPageService .createApplication(testApplication) - .flatMap( - application -> gitService.connectApplicationToGit(application.getId(), gitConnectDTO, "origin")) - .flatMap(application -> gitService + .flatMap(application -> commonGitServiceCE.connectArtifactToGit( + application.getId(), gitConnectDTO, "origin", ArtifactType.APPLICATION)) + .map(artifact -> (Application) artifact) + .flatMap(application -> commonGitServiceCE .createBranch( application.getId(), createGitBranchDTO, - application.getGitApplicationMetadata().getBranchName()) + application.getGitApplicationMetadata().getBranchName(), + ArtifactType.APPLICATION) .then(applicationService.findByBranchNameAndDefaultApplicationId( createGitBranchDTO.getBranchName(), application.getId(), READ_APPLICATIONS))) .flatMap(branchedApplication -> { @@ -2952,12 +3028,12 @@ public class GitServiceCETest { .getBranchName(), branchedApplication .getGitApplicationMetadata() - .getDefaultApplicationId(), + .getDefaultArtifactId(), filepart) .cache(); }) .zipWhen(application -> applicationService.findById( - application.getGitApplicationMetadata().getDefaultApplicationId())); + application.getGitApplicationMetadata().getDefaultArtifactId())); StepVerifier.create(createBranchMono) .assertNext(tuple -> { @@ -2996,14 +3072,16 @@ public class GitServiceCETest { Mono> createBranchMono = applicationPageService .createApplication(testApplication) - .flatMap( - application -> gitService.connectApplicationToGit(application.getId(), gitConnectDTO, "origin")) + .flatMap(application -> commonGitServiceCE.connectArtifactToGit( + application.getId(), gitConnectDTO, "origin", ArtifactType.APPLICATION)) + .map(artifact -> (Application) artifact) .flatMap(application -> Mono.zip( - gitService + commonGitServiceCE .createBranch( application.getId(), createGitBranchDTO, - application.getGitApplicationMetadata().getBranchName()) + application.getGitApplicationMetadata().getBranchName(), + ArtifactType.APPLICATION) .then(applicationService.findByBranchNameAndDefaultApplicationId( createGitBranchDTO.getBranchName(), application.getId(), READ_APPLICATIONS)), Mono.just(application))) @@ -3015,7 +3093,7 @@ public class GitServiceCETest { String otherBranchName = branchedApplication.getGitApplicationMetadata().getBranchName(); String defaultApplicationId = - branchedApplication.getGitApplicationMetadata().getDefaultApplicationId(); + branchedApplication.getGitApplicationMetadata().getDefaultArtifactId(); FilePart filepart = createMockFilePart(); return Mono.zip( @@ -3037,17 +3115,17 @@ public class GitServiceCETest { .getBranchName(), branchedApplication .getGitApplicationMetadata() - .getDefaultApplicationId()) + .getDefaultArtifactId()) .then(applicationService.findByIdAndBranchName( branchedApplication .getGitApplicationMetadata() - .getDefaultApplicationId(), + .getDefaultArtifactId(), branchedApplication .getGitApplicationMetadata() .getBranchName())); }) .zipWhen(application -> applicationService.findById( - application.getGitApplicationMetadata().getDefaultApplicationId())); + application.getGitApplicationMetadata().getDefaultArtifactId())); StepVerifier.create(createBranchMono) .assertNext(tuple -> { @@ -3091,14 +3169,16 @@ public class GitServiceCETest { Mono> createBranchMono = applicationPageService .createApplication(testApplication) - .flatMap( - application -> gitService.connectApplicationToGit(application.getId(), gitConnectDTO, "origin")) + .flatMap(application -> commonGitServiceCE.connectArtifactToGit( + application.getId(), gitConnectDTO, "origin", ArtifactType.APPLICATION)) + .map(artifact -> (Application) artifact) .flatMap(application -> Mono.zip( - gitService + commonGitServiceCE .createBranch( application.getId(), createGitBranchDTO, - application.getGitApplicationMetadata().getBranchName()) + application.getGitApplicationMetadata().getBranchName(), + ArtifactType.APPLICATION) .then(applicationService.findByBranchNameAndDefaultApplicationId( createGitBranchDTO.getBranchName(), application.getId(), READ_APPLICATIONS)), Mono.just(application))) @@ -3110,7 +3190,7 @@ public class GitServiceCETest { String otherBranchName = branchedApplication.getGitApplicationMetadata().getBranchName(); String defaultApplicationId = - branchedApplication.getGitApplicationMetadata().getDefaultApplicationId(); + branchedApplication.getGitApplicationMetadata().getDefaultArtifactId(); PageDTO newSrcPage = new PageDTO(); newSrcPage.setName("newSrcPage"); @@ -3140,7 +3220,7 @@ public class GitServiceCETest { @Test @WithUserDetails(value = "api_user") - public void connectApplicationToGit_cancelledMidway_cloneSuccess() throws IOException { + public void connectArtifactToGit_cancelledMidway_cloneSuccess() throws IOException { Mockito.when(gitExecutor.cloneRemoteIntoArtifactRepo( any(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString())) @@ -3162,8 +3242,9 @@ public class GitServiceCETest { Mockito.anyString(), Mockito.anyString())) .thenReturn(Mono.just("success")); - Mockito.when(gitFileUtils.checkIfDirectoryIsEmpty(any(Path.class))).thenReturn(Mono.just(true)); - Mockito.when(gitFileUtils.initializeReadme(any(Path.class), Mockito.anyString(), Mockito.anyString())) + Mockito.when(commonGitFileUtils.checkIfDirectoryIsEmpty(any(Path.class))) + .thenReturn(Mono.just(true)); + Mockito.when(commonGitFileUtils.initializeReadme(any(Path.class), Mockito.anyString(), Mockito.anyString())) .thenReturn(Mono.just(Paths.get("textPath"))); Application testApplication = new Application(); @@ -3182,8 +3263,9 @@ public class GitServiceCETest { GitConnectDTO gitConnectDTO = getConnectRequest("git@github.com:test/testRepo.git", testUserProfile); - gitService - .connectApplicationToGit(application1.getId(), gitConnectDTO, "baseUrl") + commonGitServiceCE + .connectArtifactToGit(application1.getId(), gitConnectDTO, "baseUrl", ArtifactType.APPLICATION) + .map(artifact -> (Application) artifact) .timeout(Duration.ofNanos(100)) .subscribe(); @@ -3201,7 +3283,7 @@ public class GitServiceCETest { StepVerifier.create(gitConnectedAppFromDbMono) .assertNext(application -> { - assertThat(application.getGitApplicationMetadata().getDefaultApplicationId()) + assertThat(application.getGitApplicationMetadata().getDefaultArtifactId()) .isEqualTo(application.getId()); }) .verifyComplete(); @@ -3220,8 +3302,8 @@ public class GitServiceCETest { page.setName("commit_sink_page"); applicationPageService.createPage(page).block(); - Mockito.when(gitFileUtils.saveApplicationToLocalRepoWithAnalytics( - any(Path.class), any(ApplicationJson.class), Mockito.anyString())) + Mockito.when(commonGitFileUtils.saveArtifactToLocalRepoWithAnalytics( + any(Path.class), any(), Mockito.anyString())) .thenReturn(Mono.just(Paths.get(""))); Mockito.when(gitExecutor.commitArtifact( any(Path.class), @@ -3241,8 +3323,8 @@ public class GitServiceCETest { Mockito.anyString())) .thenReturn(Mono.just("pushed successfully")); - gitService - .commitApplication(commitDTO, gitConnectedApplication.getId(), DEFAULT_BRANCH) + commonGitServiceCE + .commitArtifact(commitDTO, gitConnectedApplication.getId(), DEFAULT_BRANCH, ArtifactType.APPLICATION) .timeout(Duration.ofMillis(10)) .subscribe(); @@ -3285,8 +3367,8 @@ public class GitServiceCETest { Mockito.when(gitExecutor.listBranches(any())).thenReturn(Mono.just(new ArrayList<>())); Mockito.when(gitExecutor.createAndCheckoutToBranch(any(), any())) .thenReturn(Mono.just(createGitBranchDTO.getBranchName())); - Mockito.when(gitFileUtils.saveApplicationToLocalRepoWithAnalytics( - any(Path.class), any(ApplicationJson.class), Mockito.anyString())) + Mockito.when(commonGitFileUtils.saveArtifactToLocalRepoWithAnalytics( + any(Path.class), any(), Mockito.anyString())) .thenReturn(Mono.just(Paths.get(""))); Mockito.when(gitExecutor.commitArtifact( any(Path.class), @@ -3308,11 +3390,12 @@ public class GitServiceCETest { Application application1 = createApplicationConnectedToGit("createBranch_cancelledMidway_newApplicationCreated", "master"); - gitService + commonGitServiceCE .createBranch( application1.getId(), createGitBranchDTO, - application1.getGitApplicationMetadata().getBranchName()) + application1.getGitApplicationMetadata().getBranchName(), + ArtifactType.APPLICATION) .timeout(Duration.ofMillis(10)) .subscribe(); @@ -3332,8 +3415,8 @@ public class GitServiceCETest { .assertNext(application -> { GitArtifactMetadata gitData = application.getGitApplicationMetadata(); assertThat(application).isNotNull(); - assertThat(application.getId()).isNotEqualTo(gitData.getDefaultApplicationId()); - assertThat(gitData.getDefaultApplicationId()).isEqualTo(application1.getId()); + assertThat(application.getId()).isNotEqualTo(gitData.getDefaultArtifactId()); + assertThat(gitData.getDefaultArtifactId()).isEqualTo(application1.getId()); assertThat(gitData.getBranchName()).isEqualTo(createGitBranchDTO.getBranchName()); assertThat(gitData.getDefaultBranchName()).isNotEmpty(); assertThat(gitData.getRemoteUrl()).isNotEmpty(); @@ -3353,7 +3436,7 @@ public class GitServiceCETest { @Test @WithUserDetails(value = "api_user") public void generateSSHKeyDefaultType_DataNotExistsInCollection_Success() { - Mono publicKey = gitService.generateSSHKey(null); + Mono publicKey = commonGitServiceCE.generateSSHKey(null); StepVerifier.create(publicKey) .assertNext(s -> { @@ -3367,7 +3450,7 @@ public class GitServiceCETest { @Test @WithUserDetails(value = "api_user") public void generateSSHKeyRSAType_DataNotExistsInCollection_Success() { - Mono publicKey = gitService.generateSSHKey("RSA"); + Mono publicKey = commonGitServiceCE.generateSSHKey("RSA"); StepVerifier.create(publicKey) .assertNext(s -> { @@ -3381,9 +3464,9 @@ public class GitServiceCETest { @Test @WithUserDetails(value = "api_user") public void generateSSHKeyDefaultType_KeyExistsInCollection_Success() { - GitAuth publicKey = gitService.generateSSHKey(null).block(); + GitAuth publicKey = commonGitServiceCE.generateSSHKey(null).block(); - Mono newKey = gitService.generateSSHKey(null); + Mono newKey = commonGitServiceCE.generateSSHKey(null); StepVerifier.create(newKey) .assertNext(s -> { @@ -3399,9 +3482,9 @@ public class GitServiceCETest { @Test @WithUserDetails(value = "api_user") public void generateSSHKeyRSA_KeyExistsInCollection_Success() { - GitAuth publicKey = gitService.generateSSHKey(null).block(); + GitAuth publicKey = commonGitServiceCE.generateSSHKey(null).block(); - Mono newKey = gitService.generateSSHKey("RSA"); + Mono newKey = commonGitServiceCE.generateSSHKey("RSA"); StepVerifier.create(newKey) .assertNext(s -> { @@ -3416,9 +3499,11 @@ public class GitServiceCETest { @Test @WithUserDetails(value = "api_user") - public void importApplicationFromGit_InvalidRemoteUrl_ThrowError() { + public void importArtifactFromGit_InvalidRemoteUrl_ThrowError() { GitConnectDTO gitConnectDTO = getConnectRequest(null, testUserProfile); - Mono applicationMono = gitService.importApplicationFromGit("testID", gitConnectDTO); + Mono applicationMono = commonGitServiceCE + .importArtifactFromGit("testID", gitConnectDTO, ArtifactType.APPLICATION) + .map(artifactImportDTO -> (ApplicationImportDTO) artifactImportDTO); StepVerifier.create(applicationMono) .expectErrorMatches(throwable -> throwable instanceof AppsmithException @@ -3428,9 +3513,11 @@ public class GitServiceCETest { @Test @WithUserDetails(value = "api_user") - public void importApplicationFromGit_emptyWorkspaceId_ThrowError() { + public void importArtifactFromGit_emptyWorkspaceId_ThrowError() { GitConnectDTO gitConnectDTO = getConnectRequest("git@github.com:test/testRepo.git", testUserProfile); - Mono applicationMono = gitService.importApplicationFromGit(null, gitConnectDTO); + Mono applicationMono = commonGitServiceCE + .importArtifactFromGit(null, gitConnectDTO, ArtifactType.APPLICATION) + .map(artifactImportDTO -> (ApplicationImportDTO) artifactImportDTO); StepVerifier.create(applicationMono) .expectErrorMatches(throwable -> throwable instanceof AppsmithException @@ -3442,13 +3529,15 @@ public class GitServiceCETest { @Test @WithUserDetails(value = "api_user") - public void importApplicationFromGit_privateRepoLimitReached_ThrowApplicationLimitError() { + public void importArtifactFromGit_privateRepoLimitReached_ThrowApplicationLimitError() { GitConnectDTO gitConnectDTO = getConnectRequest("git@github.com:test/testRepo.git", testUserProfile); - gitService.generateSSHKey(null).block(); + commonGitServiceCE.generateSSHKey(null).block(); Mockito.when(gitCloudServicesUtils.getPrivateRepoLimitForOrg(Mockito.anyString(), Mockito.anyBoolean())) .thenReturn(Mono.just(0)); - Mono applicationMono = gitService.importApplicationFromGit(workspaceId, gitConnectDTO); + Mono applicationMono = commonGitServiceCE + .importArtifactFromGit(workspaceId, gitConnectDTO, ArtifactType.APPLICATION) + .map(artifactImportDTO -> (ApplicationImportDTO) artifactImportDTO); StepVerifier.create(applicationMono) .expectErrorMatches(throwable -> throwable instanceof AppsmithException @@ -3461,9 +3550,9 @@ public class GitServiceCETest { @Test @WithUserDetails(value = "api_user") - public void importApplicationFromGit_emptyRepo_ThrowError() { + public void importArtifactFromGit_emptyRepo_ThrowError() { GitConnectDTO gitConnectDTO = getConnectRequest("git@github.com:test/testRepo.git", testUserProfile); - GitAuth gitAuth = gitService.generateSSHKey(null).block(); + GitAuth gitAuth = commonGitServiceCE.generateSSHKey(null).block(); ApplicationJson applicationJson = createAppJson(filePath).block(); applicationJson.setExportedApplication(null); @@ -3471,12 +3560,18 @@ public class GitServiceCETest { Mockito.when(gitExecutor.cloneRemoteIntoArtifactRepo( any(Path.class), Mockito.anyString(), Mockito.anyString(), Mockito.anyString())) .thenReturn(Mono.just("defaultBranch")); - Mockito.when(gitFileUtils.reconstructApplicationJsonFromGitRepoWithAnalytics( - Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString())) + Mockito.when(commonGitFileUtils.reconstructArtifactExchangeJsonFromGitRepoWithAnalytics( + Mockito.anyString(), + Mockito.anyString(), + Mockito.anyString(), + Mockito.anyString(), + Mockito.any())) .thenReturn(Mono.just(applicationJson)); - Mockito.when(gitFileUtils.deleteLocalRepo(any(Path.class))).thenReturn(Mono.just(true)); + Mockito.when(commonGitFileUtils.deleteLocalRepo(any(Path.class))).thenReturn(Mono.just(true)); - Mono applicationMono = gitService.importApplicationFromGit(workspaceId, gitConnectDTO); + Mono applicationMono = commonGitServiceCE + .importArtifactFromGit(workspaceId, gitConnectDTO, ArtifactType.APPLICATION) + .map(artifactImportDTO -> (ApplicationImportDTO) artifactImportDTO); StepVerifier.create(applicationMono) .expectErrorMatches(throwable -> throwable instanceof AppsmithException @@ -3486,9 +3581,9 @@ public class GitServiceCETest { @Test @WithUserDetails(value = "api_user") - public void importApplicationFromGit_validRequest_Success() { + public void importArtifactFromGit_validRequest_Success() { GitConnectDTO gitConnectDTO = getConnectRequest("git@github.com:test/testRepo.git", testUserProfile); - GitAuth gitAuth = gitService.generateSSHKey(null).block(); + GitAuth gitAuth = commonGitServiceCE.generateSSHKey(null).block(); ApplicationJson applicationJson = createAppJson(filePath).block(); applicationJson.getExportedApplication().setName("testRepo"); @@ -3496,11 +3591,17 @@ public class GitServiceCETest { Mockito.when(gitExecutor.cloneRemoteIntoArtifactRepo( any(Path.class), Mockito.anyString(), Mockito.anyString(), Mockito.anyString())) .thenReturn(Mono.just("defaultBranch")); - Mockito.when(gitFileUtils.reconstructApplicationJsonFromGitRepoWithAnalytics( - Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString())) + Mockito.when(commonGitFileUtils.reconstructArtifactExchangeJsonFromGitRepoWithAnalytics( + Mockito.anyString(), + Mockito.anyString(), + Mockito.anyString(), + Mockito.anyString(), + Mockito.any())) .thenReturn(Mono.just(applicationJson)); - Mono applicationMono = gitService.importApplicationFromGit(workspaceId, gitConnectDTO); + Mono applicationMono = commonGitServiceCE + .importArtifactFromGit(workspaceId, gitConnectDTO, ArtifactType.APPLICATION) + .map(artifactImportDTO -> (ApplicationImportDTO) artifactImportDTO); StepVerifier.create(applicationMono) .assertNext(applicationImportDTO -> { @@ -3528,9 +3629,9 @@ public class GitServiceCETest { @Test @WithUserDetails(value = "api_user") - public void importApplicationFromGit_validRequestWithDuplicateApplicationName_Success() { + public void importArtifactFromGit_validRequestWithDuplicateApplicationName_Success() { GitConnectDTO gitConnectDTO = getConnectRequest("git@github.com:test/testGitRepo.git", testUserProfile); - GitAuth gitAuth = gitService.generateSSHKey(null).block(); + GitAuth gitAuth = commonGitServiceCE.generateSSHKey(null).block(); ApplicationJson applicationJson = createAppJson(filePath).block(); applicationJson.getExportedApplication().setName("testGitRepo (1)"); @@ -3543,11 +3644,17 @@ public class GitServiceCETest { Mockito.when(gitExecutor.cloneRemoteIntoArtifactRepo( any(Path.class), Mockito.anyString(), Mockito.anyString(), Mockito.anyString())) .thenReturn(Mono.just("defaultBranch")); - Mockito.when(gitFileUtils.reconstructApplicationJsonFromGitRepoWithAnalytics( - Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString())) + Mockito.when(commonGitFileUtils.reconstructArtifactExchangeJsonFromGitRepoWithAnalytics( + Mockito.anyString(), + Mockito.anyString(), + Mockito.anyString(), + Mockito.anyString(), + Mockito.any())) .thenReturn(Mono.just(applicationJson)); - Mono applicationMono = gitService.importApplicationFromGit(workspaceId, gitConnectDTO); + Mono applicationMono = commonGitServiceCE + .importArtifactFromGit(workspaceId, gitConnectDTO, ArtifactType.APPLICATION) + .map(artifactImportDTO -> (ApplicationImportDTO) artifactImportDTO); StepVerifier.create(applicationMono) .assertNext(applicationImportDTO -> { @@ -3573,7 +3680,7 @@ public class GitServiceCETest { @Test @WithUserDetails(value = "api_user") - public void importApplicationFromGit_validRequestWithDuplicateDatasourceOfSameType_Success() { + public void importArtifactFromGit_validRequestWithDuplicateDatasourceOfSameType_Success() { Workspace workspace = new Workspace(); workspace.setName("gitImportOrg"); final String testWorkspaceId = @@ -3583,7 +3690,7 @@ public class GitServiceCETest { .block(); GitConnectDTO gitConnectDTO = getConnectRequest("git@github.com:test/testGitImportRepo.git", testUserProfile); - GitAuth gitAuth = gitService.generateSSHKey(null).block(); + GitAuth gitAuth = commonGitServiceCE.generateSSHKey(null).block(); ApplicationJson applicationJson = createAppJson(filePath).block(); applicationJson.getExportedApplication().setName("testGitImportRepo"); @@ -3604,12 +3711,18 @@ public class GitServiceCETest { Mockito.when(gitExecutor.cloneRemoteIntoArtifactRepo( any(Path.class), Mockito.anyString(), Mockito.anyString(), Mockito.anyString())) .thenReturn(Mono.just("defaultBranch")); - Mockito.when(gitFileUtils.reconstructApplicationJsonFromGitRepoWithAnalytics( - Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString())) + Mockito.when(commonGitFileUtils.reconstructArtifactExchangeJsonFromGitRepoWithAnalytics( + Mockito.anyString(), + Mockito.anyString(), + Mockito.anyString(), + Mockito.anyString(), + Mockito.any())) .thenReturn(Mono.just(applicationJson)); - Mockito.when(gitFileUtils.deleteLocalRepo(any(Path.class))).thenReturn(Mono.just(true)); + Mockito.when(commonGitFileUtils.deleteLocalRepo(any(Path.class))).thenReturn(Mono.just(true)); - Mono applicationMono = gitService.importApplicationFromGit(workspaceId, gitConnectDTO); + Mono applicationMono = commonGitServiceCE + .importArtifactFromGit(workspaceId, gitConnectDTO, ArtifactType.APPLICATION) + .map(artifactImportDTO -> (ApplicationImportDTO) artifactImportDTO); StepVerifier.create(applicationMono) .assertNext(applicationImportDTO -> { @@ -3635,7 +3748,7 @@ public class GitServiceCETest { @Test @WithUserDetails(value = "api_user") - public void importApplicationFromGit_validRequestWithDuplicateDatasourceOfSameTypeCancelledMidway_Success() { + public void importArtifactFromGit_validRequestWithDuplicateDatasourceOfSameTypeCancelledMidway_Success() { Workspace workspace = new Workspace(); workspace.setName("gitImportOrgCancelledMidway"); final String testWorkspaceId = @@ -3646,7 +3759,7 @@ public class GitServiceCETest { GitConnectDTO gitConnectDTO = getConnectRequest("git@github.com:test/testGitImportRepoCancelledMidway.git", testUserProfile); - GitAuth gitAuth = gitService.generateSSHKey(null).block(); + GitAuth gitAuth = commonGitServiceCE.generateSSHKey(null).block(); ApplicationJson applicationJson = createAppJson(filePath).block(); applicationJson.getExportedApplication().setName(null); @@ -3669,13 +3782,17 @@ public class GitServiceCETest { Mockito.when(gitExecutor.cloneRemoteIntoArtifactRepo( any(Path.class), Mockito.anyString(), Mockito.anyString(), Mockito.anyString())) .thenReturn(Mono.just("defaultBranch")); - Mockito.when(gitFileUtils.reconstructApplicationJsonFromGitRepoWithAnalytics( - Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString())) + Mockito.when(commonGitFileUtils.reconstructArtifactExchangeJsonFromGitRepoWithAnalytics( + Mockito.anyString(), + Mockito.anyString(), + Mockito.anyString(), + Mockito.anyString(), + Mockito.any())) .thenReturn(Mono.just(applicationJson)); - Mockito.when(gitFileUtils.deleteLocalRepo(any(Path.class))).thenReturn(Mono.just(true)); + Mockito.when(commonGitFileUtils.deleteLocalRepo(any(Path.class))).thenReturn(Mono.just(true)); - gitService - .importApplicationFromGit(testWorkspaceId, gitConnectDTO) + commonGitServiceCE + .importArtifactFromGit(testWorkspaceId, gitConnectDTO, ArtifactType.APPLICATION) .timeout(Duration.ofMillis(10)) .subscribe(); @@ -3717,9 +3834,9 @@ public class GitServiceCETest { @Test @WithUserDetails(value = "api_user") - public void importApplicationFromGit_validRequestWithDuplicateDatasourceOfDifferentType_ThrowError() { + public void importArtifactFromGit_validRequestWithDuplicateDatasourceOfDifferentType_ThrowError() { GitConnectDTO gitConnectDTO = getConnectRequest("git@github.com:test/testGitImportRepo1.git", testUserProfile); - gitService.generateSSHKey(null).block(); + commonGitServiceCE.generateSSHKey(null).block(); ApplicationJson applicationJson = createAppJson(filePath).block(); applicationJson.getExportedApplication().setName("testGitImportRepo1"); applicationJson.getDatasourceList().get(0).setName("db-auth-1"); @@ -3739,12 +3856,18 @@ public class GitServiceCETest { Mockito.when(gitExecutor.cloneRemoteIntoArtifactRepo( any(Path.class), Mockito.anyString(), Mockito.anyString(), Mockito.anyString())) .thenReturn(Mono.just("defaultBranch")); - Mockito.when(gitFileUtils.reconstructApplicationJsonFromGitRepoWithAnalytics( - Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString())) + Mockito.when(commonGitFileUtils.reconstructArtifactExchangeJsonFromGitRepoWithAnalytics( + Mockito.anyString(), + Mockito.anyString(), + Mockito.anyString(), + Mockito.anyString(), + Mockito.any())) .thenReturn(Mono.just(applicationJson)); - Mockito.when(gitFileUtils.deleteLocalRepo(any(Path.class))).thenReturn(Mono.just(true)); + Mockito.when(commonGitFileUtils.deleteLocalRepo(any(Path.class))).thenReturn(Mono.just(true)); - Mono applicationMono = gitService.importApplicationFromGit(workspaceId, gitConnectDTO); + Mono applicationMono = commonGitServiceCE + .importArtifactFromGit(workspaceId, gitConnectDTO, ArtifactType.APPLICATION) + .map(artifactImportDTO -> (ApplicationImportDTO) artifactImportDTO); StepVerifier.create(applicationMono) .expectErrorMatches(throwable -> throwable instanceof AppsmithException @@ -3754,21 +3877,27 @@ public class GitServiceCETest { @Test @WithUserDetails(value = "api_user") - public void importApplicationFromGit_validRequestWithEmptyRepo_ThrowError() { + public void importArtifactFromGit_validRequestWithEmptyRepo_ThrowError() { GitConnectDTO gitConnectDTO = getConnectRequest("git@github.com:test/emptyRepo.git", testUserProfile); - GitAuth gitAuth = gitService.generateSSHKey(null).block(); + GitAuth gitAuth = commonGitServiceCE.generateSSHKey(null).block(); ApplicationJson applicationJson = new ApplicationJson(); Mockito.when(gitExecutor.cloneRemoteIntoArtifactRepo( any(Path.class), Mockito.anyString(), Mockito.anyString(), Mockito.anyString())) .thenReturn(Mono.just("defaultBranch")); - Mockito.when(gitFileUtils.reconstructApplicationJsonFromGitRepoWithAnalytics( - Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString())) + Mockito.when(commonGitFileUtils.reconstructArtifactExchangeJsonFromGitRepoWithAnalytics( + Mockito.anyString(), + Mockito.anyString(), + Mockito.anyString(), + Mockito.anyString(), + Mockito.any())) .thenReturn(Mono.just(applicationJson)); - Mockito.when(gitFileUtils.deleteLocalRepo(any(Path.class))).thenReturn(Mono.just(true)); + Mockito.when(commonGitFileUtils.deleteLocalRepo(any(Path.class))).thenReturn(Mono.just(true)); - Mono applicationMono = gitService.importApplicationFromGit(workspaceId, gitConnectDTO); + Mono applicationMono = commonGitServiceCE + .importArtifactFromGit(workspaceId, gitConnectDTO, ArtifactType.APPLICATION) + .map(artifactImportDTO -> (ApplicationImportDTO) artifactImportDTO); StepVerifier.create(applicationMono) .expectErrorMatches(throwable -> throwable instanceof AppsmithException @@ -3787,7 +3916,9 @@ public class GitServiceCETest { Mockito.when(gitExecutor.deleteBranch(any(Path.class), Mockito.anyString())) .thenReturn(Mono.just(true)); - Mono applicationMono = gitService.deleteBranch(application.getId(), "test"); + Mono applicationMono = commonGitServiceCE + .deleteBranch(application.getId(), "test", ArtifactType.APPLICATION) + .map(artifact -> (Application) artifact); StepVerifier.create(applicationMono) .assertNext(application1 -> { @@ -3805,7 +3936,9 @@ public class GitServiceCETest { Mockito.when(gitExecutor.deleteBranch(any(Path.class), Mockito.anyString())) .thenReturn(Mono.just(true)); - Mono applicationMono = gitService.deleteBranch(application.getId(), "master"); + Mono applicationMono = commonGitServiceCE + .deleteBranch(application.getId(), "master", ArtifactType.APPLICATION) + .map(artifact -> (Application) artifact); StepVerifier.create(applicationMono) .assertNext(application1 -> { @@ -3825,9 +3958,11 @@ public class GitServiceCETest { Mockito.when(gitExecutor.deleteBranch(any(Path.class), Mockito.anyString())) .thenReturn(Mono.just(true)); - Mono applicationMono = gitService - .updateProtectedBranches(application.getId(), List.of("master")) - .then(gitService.deleteBranch(application.getId(), branchName)); + Mono applicationMono = commonGitServiceCE + .updateProtectedBranches(application.getId(), List.of("master"), ArtifactType.APPLICATION) + .then(commonGitServiceCE + .deleteBranch(application.getId(), branchName, ArtifactType.APPLICATION) + .map(artifact -> (Application) artifact)); StepVerifier.create(applicationMono).verifyError(); } @@ -3842,7 +3977,9 @@ public class GitServiceCETest { Mockito.when(gitExecutor.deleteBranch(any(Path.class), Mockito.anyString())) .thenReturn(Mono.just(false)); - Mono applicationMono = gitService.deleteBranch(application.getId(), "master"); + Mono applicationMono = commonGitServiceCE + .deleteBranch(application.getId(), "master", ArtifactType.APPLICATION) + .map(artifact -> (Application) artifact); StepVerifier.create(applicationMono) .expectErrorMatches(throwable -> throwable instanceof AppsmithException @@ -3859,7 +3996,9 @@ public class GitServiceCETest { Mockito.when(gitExecutor.deleteBranch(any(Path.class), Mockito.anyString())) .thenReturn(Mono.just(false)); - Mono applicationMono = gitService.deleteBranch(application.getId(), "master"); + Mono applicationMono = commonGitServiceCE + .deleteBranch(application.getId(), "master", ArtifactType.APPLICATION) + .map(artifact -> (Application) artifact); StepVerifier.create(applicationMono) .expectErrorMatches(throwable -> throwable instanceof AppsmithException @@ -3883,7 +4022,9 @@ public class GitServiceCETest { Mockito.when(gitExecutor.deleteBranch(any(Path.class), Mockito.anyString())) .thenReturn(Mono.just(true)); - Mono applicationMono = gitService.deleteBranch(application.getId(), "master"); + Mono applicationMono = commonGitServiceCE + .deleteBranch(application.getId(), "master", ArtifactType.APPLICATION) + .map(artifact -> (Application) artifact); StepVerifier.create(applicationMono) .assertNext(application1 -> { @@ -3913,17 +4054,25 @@ public class GitServiceCETest { gitStatusDTO.setBehindCount(0); gitStatusDTO.setIsClean(true); - Mockito.when(gitFileUtils.saveApplicationToLocalRepoWithAnalytics( - any(Path.class), any(ApplicationJson.class), Mockito.anyString())) + Mockito.when(commonGitFileUtils.saveArtifactToLocalRepoWithAnalytics( + any(Path.class), any(), Mockito.anyString())) .thenReturn(Mono.just(Paths.get("path"))); - Mockito.when(gitFileUtils.reconstructApplicationJsonFromGitRepoWithAnalytics( - Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString())) + Mockito.when(commonGitFileUtils.reconstructArtifactExchangeJsonFromGitRepoWithAnalytics( + Mockito.anyString(), + Mockito.anyString(), + Mockito.anyString(), + Mockito.anyString(), + Mockito.any())) .thenReturn(Mono.just(applicationJson)); Mockito.when(gitExecutor.rebaseBranch(any(Path.class), Mockito.anyString())) .thenReturn(Mono.just(true)); - Mono applicationMono = gitService.discardChanges( - application.getId(), application.getGitApplicationMetadata().getBranchName()); + Mono applicationMono = commonGitServiceCE + .discardChanges( + application.getId(), + application.getGitApplicationMetadata().getBranchName(), + ArtifactType.APPLICATION) + .map(artifact -> (Application) artifact); StepVerifier.create(applicationMono) .assertNext(application1 -> { @@ -3949,11 +4098,15 @@ public class GitServiceCETest { gitStatusDTO.setBehindCount(0); gitStatusDTO.setIsClean(true); - Mockito.when(gitFileUtils.saveApplicationToLocalRepoWithAnalytics( - any(Path.class), any(ApplicationJson.class), Mockito.anyString())) + Mockito.when(commonGitFileUtils.saveArtifactToLocalRepoWithAnalytics( + any(Path.class), any(), Mockito.anyString())) .thenReturn(Mono.just(Paths.get("path"))); - Mockito.when(gitFileUtils.reconstructApplicationJsonFromGitRepoWithAnalytics( - Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString())) + Mockito.when(commonGitFileUtils.reconstructArtifactExchangeJsonFromGitRepoWithAnalytics( + Mockito.anyString(), + Mockito.anyString(), + Mockito.anyString(), + Mockito.anyString(), + Mockito.any())) .thenReturn(Mono.just(applicationJson)); Mockito.when(gitExecutor.pullApplication( any(Path.class), @@ -3973,10 +4126,12 @@ public class GitServiceCETest { Mockito.anyBoolean())) .thenReturn(Mono.just("fetched")); - gitService + commonGitServiceCE .discardChanges( application.getId(), - application.getGitApplicationMetadata().getBranchName()) + application.getGitApplicationMetadata().getBranchName(), + ArtifactType.APPLICATION) + .map(artifact -> (Application) artifact) .timeout(Duration.ofNanos(100)) .subscribe(); @@ -4018,8 +4173,9 @@ public class GitServiceCETest { Mockito.when(gitExecutor.deleteBranch(any(Path.class), Mockito.anyString())) .thenReturn(Mono.just(true)); - gitService - .deleteBranch(application.getId(), TO_BE_DELETED_BRANCH) + commonGitServiceCE + .deleteBranch(application.getId(), TO_BE_DELETED_BRANCH, ArtifactType.APPLICATION) + .map(artifact -> (Application) artifact) .timeout(Duration.ofMillis(5)) .subscribe(); @@ -4060,8 +4216,8 @@ public class GitServiceCETest { page.setName("commit_WithMultipleUsers_page"); applicationPageService.createPage(page).block(); - Mockito.when(gitFileUtils.saveApplicationToLocalRepoWithAnalytics( - any(Path.class), any(ApplicationJson.class), Mockito.anyString())) + Mockito.when(commonGitFileUtils.saveArtifactToLocalRepoWithAnalytics( + any(Path.class), any(), Mockito.anyString())) .thenReturn(Mono.just(Paths.get(""))); Mockito.when(gitExecutor.commitArtifact( any(Path.class), @@ -4082,11 +4238,11 @@ public class GitServiceCETest { .thenReturn(Mono.just("pushed successfully")); // First request for commit operation - Mono commitMonoReq1 = - gitService.commitApplication(commitDTO, gitConnectedApplication.getId(), DEFAULT_BRANCH); + Mono commitMonoReq1 = commonGitServiceCE.commitArtifact( + commitDTO, gitConnectedApplication.getId(), DEFAULT_BRANCH, ArtifactType.APPLICATION); // Second request for commit operation - Mono commitMonoReq2 = - gitService.commitApplication(commitDTO, gitConnectedApplication.getId(), DEFAULT_BRANCH); + Mono commitMonoReq2 = commonGitServiceCE.commitArtifact( + commitDTO, gitConnectedApplication.getId(), DEFAULT_BRANCH, ArtifactType.APPLICATION); // Both the request to execute completely without the file lock error from jgit. StepVerifier.create(Mono.zip(commitMonoReq1, commitMonoReq2)) @@ -4128,7 +4284,7 @@ public class GitServiceCETest { GitArtifactMetadata gitData = application.getGitApplicationMetadata(); GitAuth gitAuth = gitData.getGitAuth(); Path repoSuffix = - Paths.get(application.getWorkspaceId(), gitData.getDefaultApplicationId(), gitData.getRepoName()); + Paths.get(application.getWorkspaceId(), gitData.getDefaultArtifactId(), gitData.getRepoName()); Path repoPath = Paths.get("test", "git", "root", repoSuffix.toString()); BranchTrackingStatus branchTrackingStatus = Mockito.mock(BranchTrackingStatus.class); @@ -4142,7 +4298,8 @@ public class GitServiceCETest { .thenReturn(Mono.just("success")); Mockito.when(gitExecutor.getBranchTrackingStatus(repoPath, branch)).thenReturn(Mono.just(branchTrackingStatus)); - StepVerifier.create(gitService.fetchRemoteChanges(gitData.getDefaultApplicationId(), branch, false)) + StepVerifier.create(commonGitServiceCE.fetchRemoteChanges( + gitData.getDefaultArtifactId(), branch, false, ArtifactType.APPLICATION)) .assertNext(response -> { assertThat(response.getAheadCount()).isEqualTo(1); assertThat(response.getBehindCount()).isEqualTo(2); @@ -4165,7 +4322,7 @@ public class GitServiceCETest { assertThat(application.getGitApplicationMetadata().getBranchName()).isEqualTo("develop"); assertThat(application.getId()) - .isEqualTo(application.getGitApplicationMetadata().getDefaultApplicationId()); + .isEqualTo(application.getGitApplicationMetadata().getDefaultArtifactId()); List branches = List.of(new GitBranchDTO("main", true, false)); ApplicationJson applicationJson = createAppJson(filePath).block(); @@ -4183,11 +4340,17 @@ public class GitServiceCETest { Mockito.when(gitExecutor.checkoutRemoteBranch(Mockito.any(Path.class), Mockito.anyString())) .thenReturn(Mono.just("success")); - Mockito.when(gitFileUtils.reconstructApplicationJsonFromGitRepoWithAnalytics( - Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString())) + Mockito.when(commonGitFileUtils.reconstructArtifactExchangeJsonFromGitRepoWithAnalytics( + Mockito.anyString(), + Mockito.anyString(), + Mockito.anyString(), + Mockito.anyString(), + Mockito.any())) .thenReturn(Mono.just(applicationJson)); - Mono checkoutBranchMono = gitService.checkoutBranch(application.getId(), "origin/develop", true); + Mono checkoutBranchMono = commonGitServiceCE + .checkoutBranch(application.getId(), "origin/develop", true, ArtifactType.APPLICATION) + .map(artifact -> (Application) artifact); StepVerifier.create(checkoutBranchMono) .assertNext(app -> { assertThat(app.getId()).isEqualTo(application.getId()); @@ -4226,13 +4389,14 @@ public class GitServiceCETest { @WithUserDetails("api_user") @Test - public void ConnectApplicationToGit_WhenUserDoesNotHaveRequiredPermission_OperationFails() { + public void connectArtifactToGit_WhenUserDoesNotHaveRequiredPermission_OperationFails() { Application application = createApplicationAndRemovePermissionFromApplication(applicationPermission.getGitConnectPermission()); GitConnectDTO gitConnectDTO = getConnectRequest("git@github.com:test/testRepo.git", testUserProfile); - Mono applicationMono = - gitService.connectApplicationToGit(application.getId(), gitConnectDTO, "baseUrl"); + Mono applicationMono = commonGitServiceCE + .connectArtifactToGit(application.getId(), gitConnectDTO, "baseUrl", ArtifactType.APPLICATION) + .map(artifact -> (Application) artifact); StepVerifier.create(applicationMono) .expectErrorMessage( @@ -4245,7 +4409,9 @@ public class GitServiceCETest { public void detachRemote_WhenUserDoesNotHaveRequiredPermission_OperationFails() { Application application = createApplicationAndRemovePermissionFromApplication(applicationPermission.getGitConnectPermission()); - Mono applicationMono = gitService.detachRemote(application.getId()); + Mono applicationMono = commonGitServiceCE + .detachRemote(application.getId(), ArtifactType.APPLICATION) + .map(artifact -> (Application) artifact); StepVerifier.create(applicationMono) .expectErrorMessage( @@ -4269,7 +4435,8 @@ public class GitServiceCETest { application.setGitApplicationMetadata(gitArtifactMetadata); return applicationRepository.save(application); }) - .flatMap(application -> gitService.getProtectedBranches(application.getId())); + .flatMap(application -> + commonGitServiceCE.getProtectedBranches(application.getId(), ArtifactType.APPLICATION)); StepVerifier.create(branchListMono) .assertNext(branches -> { @@ -4293,7 +4460,8 @@ public class GitServiceCETest { application.setGitApplicationMetadata(gitArtifactMetadata); return applicationRepository.save(application); }) - .flatMap(application -> gitService.getProtectedBranches(application.getId())); + .flatMap(application -> + commonGitServiceCE.getProtectedBranches(application.getId(), ArtifactType.APPLICATION)); StepVerifier.create(branchListMono) .assertNext(branches -> { @@ -4346,13 +4514,16 @@ public class GitServiceCETest { // create three app with master as the default branch String defaultAppId = createBranchedApplication(branchList); - StepVerifier.create(gitService.updateProtectedBranches(defaultAppId, List.of("develop", "feature"))) + StepVerifier.create(commonGitServiceCE.updateProtectedBranches( + defaultAppId, List.of("develop", "feature"), ArtifactType.APPLICATION)) .verifyErrorMessage(AppsmithError.UNSUPPORTED_OPERATION.getMessage()); - StepVerifier.create(gitService.updateProtectedBranches(defaultAppId, List.of("develop"))) + StepVerifier.create(commonGitServiceCE.updateProtectedBranches( + defaultAppId, List.of("develop"), ArtifactType.APPLICATION)) .verifyErrorMessage(AppsmithError.UNSUPPORTED_OPERATION.getMessage()); - StepVerifier.create(gitService.updateProtectedBranches(defaultAppId, List.of("master", "develop"))) + StepVerifier.create(commonGitServiceCE.updateProtectedBranches( + defaultAppId, List.of("master", "develop"), ArtifactType.APPLICATION)) .verifyErrorMessage(AppsmithError.UNSUPPORTED_OPERATION.getMessage()); } @@ -4362,8 +4533,8 @@ public class GitServiceCETest { List branchList = List.of("master", "develop", "feature"); // create three app with master as the default branch String defaultAppId = createBranchedApplication(branchList); - Flux applicationFlux = gitService - .updateProtectedBranches(defaultAppId, List.of("master")) + Flux applicationFlux = commonGitServiceCE + .updateProtectedBranches(defaultAppId, List.of("master"), ArtifactType.APPLICATION) .thenMany(applicationService.findAllApplicationsByDefaultApplicationId( defaultAppId, applicationPermission.getEditPermission())); @@ -4390,9 +4561,10 @@ public class GitServiceCETest { List branchList = List.of("master", "develop", "feature"); // create three app with master as the default branch String defaultAppId = createBranchedApplication(branchList); - Flux applicationFlux = gitService - .updateProtectedBranches(defaultAppId, List.of("master")) - .then(gitService.updateProtectedBranches(defaultAppId, List.of())) // unset the protected branch list + Flux applicationFlux = commonGitServiceCE + .updateProtectedBranches(defaultAppId, List.of("master"), ArtifactType.APPLICATION) + .then(commonGitServiceCE.updateProtectedBranches( + defaultAppId, List.of(), ArtifactType.APPLICATION)) // unset the protected branch list .thenMany(applicationService.findAllApplicationsByDefaultApplicationId( defaultAppId, applicationPermission.getEditPermission())); @@ -4415,7 +4587,7 @@ public class GitServiceCETest { Application application = createApplicationAndRemovePermissionFromApplication( applicationPermission.getManageProtectedBranchPermission()); Mono> updateProtectedBranchesMono = - gitService.updateProtectedBranches(application.getId(), List.of()); + commonGitServiceCE.updateProtectedBranches(application.getId(), List.of(), ArtifactType.APPLICATION); StepVerifier.create(updateProtectedBranchesMono) .expectErrorMessage( @@ -4432,9 +4604,10 @@ public class GitServiceCETest { doReturn(Mono.error(new AppsmithException(AppsmithError.GENERIC_BAD_REQUEST, "Test error"))) .when(applicationService) - .updateProtectedBranches(Mockito.any(), Mockito.any()); + .updateProtectedBranches(Mockito.anyString(), Mockito.any()); - Mono> updateProtectedBranchesMono = gitService.updateProtectedBranches(defaultAppId, List.of()); + Mono> updateProtectedBranchesMono = + commonGitServiceCE.updateProtectedBranches(defaultAppId, List.of(), ArtifactType.APPLICATION); StepVerifier.create(updateProtectedBranchesMono) .expectErrorMessage(AppsmithError.GENERIC_BAD_REQUEST.getMessage("Test error")) @@ -4469,8 +4642,8 @@ public class GitServiceCETest { }) .cache(); - Mono> branchListMonoWithDefaultBranch = - applicationMono.flatMap(application -> gitService.getProtectedBranches(application.getId())); + Mono> branchListMonoWithDefaultBranch = applicationMono.flatMap( + application -> commonGitServiceCE.getProtectedBranches(application.getId(), ArtifactType.APPLICATION)); StepVerifier.create(branchListMonoWithDefaultBranch) .assertNext(branchList -> { @@ -4485,7 +4658,8 @@ public class GitServiceCETest { gitArtifactMetadata.setBranchProtectionRules(List.of("develop", "feature")); return applicationRepository.save(application); }) - .flatMap(application -> gitService.getProtectedBranches(application.getId())); + .flatMap(application -> + commonGitServiceCE.getProtectedBranches(application.getId(), ArtifactType.APPLICATION)); StepVerifier.create(branchListMonoWithoutDefaultBranch) .assertNext(branchList -> { @@ -4511,8 +4685,8 @@ public class GitServiceCETest { }) .cache(); - Mono toggleAutoCommitWhenSettingsIsNullMono = - createdApplicationMono.flatMap(application -> gitService.toggleAutoCommitEnabled(application.getId())); + Mono toggleAutoCommitWhenSettingsIsNullMono = createdApplicationMono.flatMap(application -> + commonGitServiceCE.toggleAutoCommitEnabled(application.getId(), ArtifactType.APPLICATION)); StepVerifier.create(toggleAutoCommitWhenSettingsIsNullMono) .assertNext(aBoolean -> { @@ -4529,7 +4703,8 @@ public class GitServiceCETest { .setEnabled(TRUE); return applicationRepository.save(application); }) - .flatMap(application -> gitService.toggleAutoCommitEnabled(application.getId())); + .flatMap(application -> + commonGitServiceCE.toggleAutoCommitEnabled(application.getId(), ArtifactType.APPLICATION)); StepVerifier.create(toggleAutoCommitWhenSettingsHasTrue) .assertNext(aBoolean -> { @@ -4546,7 +4721,8 @@ public class GitServiceCETest { .setEnabled(FALSE); return applicationRepository.save(application); }) - .flatMap(application -> gitService.toggleAutoCommitEnabled(application.getId())); + .flatMap(application -> + commonGitServiceCE.toggleAutoCommitEnabled(application.getId(), ArtifactType.APPLICATION)); StepVerifier.create(toggleAutoCommitWhenSettingsHasFalse) .assertNext(aBoolean -> { @@ -4563,7 +4739,8 @@ public class GitServiceCETest { .setEnabled(FALSE); return applicationRepository.save(application); }) - .flatMap(application -> gitService.getGitApplicationMetadata(application.getId())); + .flatMap(application -> + commonGitServiceCE.getGitArtifactMetadata(application.getId(), ArtifactType.APPLICATION)); StepVerifier.create(metaDataAfterToggleMono) .assertNext(gitApplicationMetadata -> { @@ -4581,7 +4758,7 @@ public class GitServiceCETest { applicationPermission.getManageAutoCommitPermission()); Mono toggleAutoCommitWhenSettingsIsNullMono = - gitService.toggleAutoCommitEnabled(testApplication.getId()); + commonGitServiceCE.toggleAutoCommitEnabled(testApplication.getId(), ArtifactType.APPLICATION); StepVerifier.create(toggleAutoCommitWhenSettingsIsNullMono) .expectErrorMessage( @@ -4591,7 +4768,7 @@ public class GitServiceCETest { @Test @WithUserDetails(value = "api_user") - public void listBranchForApplication_WhenLocalRepoDoesNotExist_RepoIsClonedFromRemote() + public void listBranchForArtifact_WhenLocalRepoDoesNotExist_RepoIsClonedFromRemote() throws IOException, GitAPIException { List branchList = List.of( createGitBranchDTO("defaultBranch", false), @@ -4606,8 +4783,9 @@ public class GitServiceCETest { any(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString())) .thenReturn(Mono.just("defaultBranch")); - Mockito.when(gitFileUtils.checkIfDirectoryIsEmpty(any(Path.class))).thenReturn(Mono.just(true)); - Mockito.when(gitFileUtils.initializeReadme(any(Path.class), Mockito.anyString(), Mockito.anyString())) + Mockito.when(commonGitFileUtils.checkIfDirectoryIsEmpty(any(Path.class))) + .thenReturn(Mono.just(true)); + Mockito.when(commonGitFileUtils.initializeReadme(any(Path.class), Mockito.anyString(), Mockito.anyString())) .thenReturn(Mono.just(Paths.get("textPath"))); Mockito.when(gitExecutor.fetchRemote( @@ -4620,10 +4798,10 @@ public class GitServiceCETest { .thenReturn(Mono.just("status")); Application application1 = createApplicationConnectedToGit( - "listBranchForApplication_pruneBranchNoChangesInRemote_Success", "defaultBranch"); + "listBranchForArtifact_pruneBranchNoChangesInRemote_Success", "defaultBranch"); - Mono> listMono = - gitService.listBranchForApplication(application1.getId(), false, "defaultBranch"); + Mono> listMono = commonGitServiceCE.listBranchForArtifact( + application1.getId(), false, "defaultBranch", ArtifactType.APPLICATION); StepVerifier.create(listMono) .assertNext(listBranch -> { diff --git a/app/server/appsmith-server/src/test/java/com/appsmith/server/git/autocommit/ApplicationPageServiceAutoCommitTest.java b/app/server/appsmith-server/src/test/java/com/appsmith/server/git/autocommit/ApplicationPageServiceAutoCommitTest.java index 88652ece3d..a72bb0efce 100644 --- a/app/server/appsmith-server/src/test/java/com/appsmith/server/git/autocommit/ApplicationPageServiceAutoCommitTest.java +++ b/app/server/appsmith-server/src/test/java/com/appsmith/server/git/autocommit/ApplicationPageServiceAutoCommitTest.java @@ -18,13 +18,13 @@ import com.appsmith.server.dtos.ApplicationJson; import com.appsmith.server.dtos.AutoCommitTriggerDTO; import com.appsmith.server.dtos.PageDTO; import com.appsmith.server.git.autocommit.helpers.AutoCommitEligibilityHelper; +import com.appsmith.server.git.common.CommonGitService; import com.appsmith.server.helpers.DSLMigrationUtils; import com.appsmith.server.helpers.GitPrivateRepoHelper; import com.appsmith.server.migrations.JsonSchemaMigration; import com.appsmith.server.migrations.JsonSchemaVersions; import com.appsmith.server.newpages.base.NewPageService; import com.appsmith.server.services.ApplicationPageService; -import com.appsmith.server.services.CommonGitService; import com.appsmith.server.services.FeatureFlagService; import com.appsmith.server.services.UserDataService; import com.appsmith.server.testhelpers.git.GitFileSystemTestHelper; diff --git a/app/server/appsmith-server/src/test/java/com/appsmith/server/git/autocommit/AutoCommitEventHandlerImplTest.java b/app/server/appsmith-server/src/test/java/com/appsmith/server/git/autocommit/AutoCommitEventHandlerImplTest.java index 10fef4cd72..f046da6005 100644 --- a/app/server/appsmith-server/src/test/java/com/appsmith/server/git/autocommit/AutoCommitEventHandlerImplTest.java +++ b/app/server/appsmith-server/src/test/java/com/appsmith/server/git/autocommit/AutoCommitEventHandlerImplTest.java @@ -19,7 +19,6 @@ import com.appsmith.server.git.AutoCommitEventHandlerImpl; import com.appsmith.server.git.GitRedisUtils; import com.appsmith.server.helpers.CommonGitFileUtils; import com.appsmith.server.helpers.DSLMigrationUtils; -import com.appsmith.server.helpers.GitFileUtils; import com.appsmith.server.helpers.RedisUtils; import com.appsmith.server.migrations.JsonSchemaMigration; import com.appsmith.server.migrations.JsonSchemaVersions; @@ -83,9 +82,6 @@ public class AutoCommitEventHandlerImplTest { @MockBean DSLMigrationUtils dslMigrationUtils; - @SpyBean - GitFileUtils gitFileUtils; - @SpyBean FileInterface fileUtils; @@ -120,7 +116,6 @@ public class AutoCommitEventHandlerImplTest { gitRedisUtils, redisUtils, dslMigrationUtils, - gitFileUtils, commonGitFileUtils, gitExecutor, projectProperties, @@ -221,8 +216,8 @@ public class AutoCommitEventHandlerImplTest { Mockito.when(dslMigrationUtils.migratePageDsl(any(JSONObject.class))).thenReturn(Mono.just(dslAfterMigration)); doReturn(Mono.just(baseRepoSuffix)) - .when(gitFileUtils) - .saveApplicationToLocalRepo( + .when(commonGitFileUtils) + .saveArtifactToLocalRepo( autoCommitEvent.getWorkspaceId(), autoCommitEvent.getApplicationId(), autoCommitEvent.getRepoName(), @@ -319,8 +314,8 @@ public class AutoCommitEventHandlerImplTest { ArtifactType.APPLICATION); doReturn(Mono.just(baseRepoSuffix)) - .when(gitFileUtils) - .saveApplicationToLocalRepo( + .when(commonGitFileUtils) + .saveArtifactToLocalRepo( autoCommitEvent.getWorkspaceId(), autoCommitEvent.getApplicationId(), autoCommitEvent.getRepoName(), @@ -394,8 +389,8 @@ public class AutoCommitEventHandlerImplTest { ArtifactType.APPLICATION); doReturn(Mono.just(baseRepoSuffix)) - .when(gitFileUtils) - .saveApplicationToLocalRepo( + .when(commonGitFileUtils) + .saveArtifactToLocalRepo( autoCommitEvent.getWorkspaceId(), autoCommitEvent.getApplicationId(), autoCommitEvent.getRepoName(), @@ -440,8 +435,8 @@ public class AutoCommitEventHandlerImplTest { autoCommitEvent.getBranchName()); doReturn(Mono.just(baseRepoSuffix)) - .when(gitFileUtils) - .saveApplicationToLocalRepo( + .when(commonGitFileUtils) + .saveArtifactToLocalRepo( anyString(), anyString(), anyString(), any(ApplicationJson.class), anyString()); ApplicationJson applicationJson1 = new ApplicationJson(); diff --git a/app/server/appsmith-server/src/test/java/com/appsmith/server/git/autocommit/helpers/GitAutoCommitHelperImplTest.java b/app/server/appsmith-server/src/test/java/com/appsmith/server/git/autocommit/helpers/GitAutoCommitHelperImplTest.java index f8aa5b6595..8da6e36beb 100644 --- a/app/server/appsmith-server/src/test/java/com/appsmith/server/git/autocommit/helpers/GitAutoCommitHelperImplTest.java +++ b/app/server/appsmith-server/src/test/java/com/appsmith/server/git/autocommit/helpers/GitAutoCommitHelperImplTest.java @@ -11,9 +11,9 @@ import com.appsmith.server.domains.GitProfile; import com.appsmith.server.dtos.AutoCommitProgressDTO; import com.appsmith.server.events.AutoCommitEvent; import com.appsmith.server.git.AutoCommitEventHandler; +import com.appsmith.server.git.common.CommonGitService; import com.appsmith.server.helpers.GitPrivateRepoHelper; import com.appsmith.server.helpers.RedisUtils; -import com.appsmith.server.services.CommonGitService; import com.appsmith.server.services.FeatureFlagService; import com.appsmith.server.services.UserDataService; import com.appsmith.server.solutions.ApplicationPermission; diff --git a/app/server/appsmith-server/src/test/java/com/appsmith/server/helpers/GitFileUtilsTest.java b/app/server/appsmith-server/src/test/java/com/appsmith/server/helpers/GitFileUtilsTest.java index 480985352b..5f261a1ca6 100644 --- a/app/server/appsmith-server/src/test/java/com/appsmith/server/helpers/GitFileUtilsTest.java +++ b/app/server/appsmith-server/src/test/java/com/appsmith/server/helpers/GitFileUtilsTest.java @@ -2,6 +2,7 @@ package com.appsmith.server.helpers; import com.appsmith.external.git.FileInterface; import com.appsmith.external.models.ApplicationGitReference; +import com.appsmith.server.constants.ArtifactType; import com.appsmith.server.domains.ActionCollection; import com.appsmith.server.domains.NewAction; import com.appsmith.server.domains.NewPage; @@ -52,7 +53,7 @@ public class GitFileUtilsTest { FileInterface fileInterface; @Autowired - GitFileUtils gitFileUtils; + CommonGitFileUtils commonGitFileUtils; @Autowired AnalyticsService analyticsService; @@ -90,7 +91,8 @@ public class GitFileUtilsTest { @Test public void getSerializableResource_allEntitiesArePresentForApplication_keysIncludesSeparator() { ApplicationJson validAppJson = createAppJson(filePath).block(); - ApplicationGitReference applicationGitReference = gitFileUtils.createApplicationReference(validAppJson); + ApplicationGitReference applicationGitReference = + (ApplicationGitReference) commonGitFileUtils.createArtifactReference(validAppJson); List pageNames = validAppJson.getPageList().stream() .map(newPage -> newPage.getUnpublishedPage().getName()) @@ -149,7 +151,8 @@ public class GitFileUtilsTest { .get(validAppJson.getActionCollectionList().size() - 1); deletedCollection.getUnpublishedCollection().setDeletedAt(Instant.now()); - ApplicationGitReference applicationGitReference = gitFileUtils.createApplicationReference(validAppJson); + ApplicationGitReference applicationGitReference = + (ApplicationGitReference) commonGitFileUtils.createArtifactReference(validAppJson); Map actions = applicationGitReference.getActions(); for (Map.Entry entry : actions.entrySet()) { @@ -187,7 +190,7 @@ public class GitFileUtilsTest { Mockito.any(Path.class), Mockito.any(ApplicationGitReference.class), Mockito.anyString())) .thenReturn(Mono.just(Path.of("orgId", "appId", "repoName"))); - Mono resultMono = gitFileUtils.saveApplicationToLocalRepoWithAnalytics( + Mono resultMono = commonGitFileUtils.saveArtifactToLocalRepoWithAnalytics( Path.of("orgId/appId/repoName"), validAppJson, "gitFileTest"); StepVerifier.create(resultMono) @@ -240,8 +243,10 @@ public class GitFileUtilsTest { Mockito.anyString(), Mockito.anyString(), Mockito.anyString(), Mockito.anyString())) .thenReturn(Mono.just(applicationReference)); - Mono resultMono = gitFileUtils - .reconstructApplicationJsonFromGitRepoWithAnalytics("orgId", "appId", "repoName", "branch") + Mono resultMono = commonGitFileUtils + .reconstructArtifactExchangeJsonFromGitRepoWithAnalytics( + "orgId", "appId", "repoName", "branch", ArtifactType.APPLICATION) + .map(artifactExchangeJson -> (ApplicationJson) artifactExchangeJson) .cache(); StepVerifier.create(resultMono) diff --git a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/UserDataServiceTest.java b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/UserDataServiceTest.java index 27dc61ad78..2bf4f79ba6 100644 --- a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/UserDataServiceTest.java +++ b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/UserDataServiceTest.java @@ -8,7 +8,7 @@ import com.appsmith.server.domains.UserData; import com.appsmith.server.dtos.RecentlyUsedEntityDTO; import com.appsmith.server.exceptions.AppsmithError; import com.appsmith.server.exceptions.AppsmithException; -import com.appsmith.server.repositories.ApplicationRepository; +import com.appsmith.server.git.common.CommonGitService; import com.appsmith.server.repositories.AssetRepository; import com.appsmith.server.repositories.UserDataRepository; import org.assertj.core.api.AssertionsForClassTypes; @@ -61,13 +61,7 @@ public class UserDataServiceTest { private AssetRepository assetRepository; @Autowired - private AssetService assetService; - - @Autowired - private ApplicationRepository applicationRepository; - - @Autowired - private GitService gitService; + private CommonGitService commonGitService; private Mono userMono; @@ -448,7 +442,7 @@ public class UserDataServiceTest { GitProfile gitGlobalConfigDTO = createGitProfile(null, "Test 1"); Mono> userDataMono = - gitService.updateOrCreateGitProfileForCurrentUser(gitGlobalConfigDTO); + commonGitService.updateOrCreateGitProfileForCurrentUser(gitGlobalConfigDTO); StepVerifier.create(userDataMono) .expectErrorMatches(throwable -> throwable instanceof AppsmithException && throwable.getMessage().contains(AppsmithError.INVALID_PARAMETER.getMessage("Author Email"))) @@ -461,7 +455,7 @@ public class UserDataServiceTest { GitProfile gitProfileDTO = createGitProfile(null, null); Mono> userDataMono = - gitService.updateOrCreateGitProfileForCurrentUser(gitProfileDTO, "defaultAppId"); + commonGitService.updateOrCreateGitProfileForCurrentUser(gitProfileDTO, "defaultAppId"); StepVerifier.create(userDataMono) .assertNext(gitProfileMap -> { AssertionsForClassTypes.assertThat(gitProfileMap).isNotNull(); @@ -483,7 +477,7 @@ public class UserDataServiceTest { GitProfile gitGlobalConfigDTO = createGitProfile("test@appsmith.com", null); Mono> userDataMono = - gitService.updateOrCreateGitProfileForCurrentUser(gitGlobalConfigDTO); + commonGitService.updateOrCreateGitProfileForCurrentUser(gitGlobalConfigDTO); StepVerifier.create(userDataMono) .expectErrorMatches(throwable -> throwable instanceof AppsmithException && throwable.getMessage().contains(AppsmithError.INVALID_PARAMETER.getMessage("Author Name"))) @@ -494,7 +488,7 @@ public class UserDataServiceTest { @WithUserDetails(value = "api_user") public void getAndUpdateDefaultGitProfile_fallbackValueFromUserProfileIfEmpty_updateWithProfile() { - Mono gitConfigMono = gitService.getDefaultGitProfileOrCreateIfEmpty(); + Mono gitConfigMono = commonGitService.getDefaultGitProfileOrCreateIfEmpty(); Mono userData = userDataService .getForCurrentUser() @@ -511,7 +505,7 @@ public class UserDataServiceTest { GitProfile gitGlobalConfigDTO = createGitProfile("test@appsmith.com", "Test 1"); Mono> gitProfilesMono = - gitService.updateOrCreateGitProfileForCurrentUser(gitGlobalConfigDTO); + commonGitService.updateOrCreateGitProfileForCurrentUser(gitGlobalConfigDTO); StepVerifier.create(gitProfilesMono) .assertNext(gitProfileMap -> {