diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/constants/ce/UrlCE.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/constants/ce/UrlCE.java index 08f65c8013db..c070dbeabc2e 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/constants/ce/UrlCE.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/constants/ce/UrlCE.java @@ -32,6 +32,8 @@ public class UrlCE { public static final String PRODUCT_ALERT = BASE_URL + VERSION + "/product-alert"; public static final String SEARCH_ENTITY_URL = BASE_URL + VERSION + "/search-entities"; public static final String CONSOLIDATED_API_URL = BASE_URL + VERSION + "/consolidated-api"; + public static final String GIT_APPLICATION_URL = BASE_URL + VERSION + "/git/applications"; + public static final String GIT_ARTIFACT_URL = BASE_URL + VERSION + "/git/artifacts"; // Sub-paths public static final String MOCKS = "/mocks"; diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/git/central/CentralGitServiceCE.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/git/central/CentralGitServiceCE.java index 23e668a80ea9..a9fc22f6a066 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/git/central/CentralGitServiceCE.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/git/central/CentralGitServiceCE.java @@ -1,14 +1,18 @@ package com.appsmith.server.git.central; +import com.appsmith.external.dtos.GitBranchDTO; import com.appsmith.external.dtos.GitRefDTO; import com.appsmith.external.dtos.GitStatusDTO; import com.appsmith.external.git.constants.ce.RefType; import com.appsmith.git.dto.CommitDTO; import com.appsmith.server.constants.ArtifactType; import com.appsmith.server.domains.Artifact; +import com.appsmith.server.domains.GitArtifactMetadata; +import com.appsmith.server.domains.GitAuth; import com.appsmith.server.dtos.ArtifactImportDTO; import com.appsmith.server.dtos.AutoCommitResponseDTO; import com.appsmith.server.dtos.GitConnectDTO; +import com.appsmith.server.dtos.GitDocsDTO; import com.appsmith.server.dtos.GitPullDTO; import reactor.core.publisher.Mono; @@ -31,6 +35,9 @@ Mono commitArtifact( Mono detachRemote(String branchedArtifactId, ArtifactType artifactType, GitType gitType); + Mono> listBranchForArtifact( + String branchedArtifactId, Boolean pruneBranches, ArtifactType artifactType, GitType gitType); + Mono fetchRemoteChanges( String referenceArtifactId, boolean isFileLock, @@ -67,4 +74,10 @@ Mono> updateProtectedBranches( Mono getAutoCommitProgress( String baseArtifactId, String branchName, ArtifactType artifactType); + + Mono generateSSHKey(String keyType); + + Mono getGitArtifactMetadata(String baseArtifactId, ArtifactType artifactType); + + Mono> getGitDocUrls(); } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/git/central/CentralGitServiceCECompatibleImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/git/central/CentralGitServiceCECompatibleImpl.java index 9c80cfa47cd9..c8c44990d8d1 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/git/central/CentralGitServiceCECompatibleImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/git/central/CentralGitServiceCECompatibleImpl.java @@ -11,6 +11,7 @@ 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.SessionUserService; import com.appsmith.server.services.UserDataService; import com.appsmith.server.services.WorkspaceService; @@ -34,6 +35,7 @@ public CentralGitServiceCECompatibleImpl( GitArtifactHelperResolver gitArtifactHelperResolver, GitHandlingServiceResolver gitHandlingServiceResolver, GitPrivateRepoHelper gitPrivateRepoHelper, + GitDeployKeysRepository gitDeployKeysRepository, DatasourceService datasourceService, DatasourcePermission datasourcePermission, WorkspaceService workspaceService, @@ -52,6 +54,7 @@ public CentralGitServiceCECompatibleImpl( gitArtifactHelperResolver, gitHandlingServiceResolver, gitPrivateRepoHelper, + gitDeployKeysRepository, datasourceService, datasourcePermission, workspaceService, diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/git/central/CentralGitServiceCEImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/git/central/CentralGitServiceCEImpl.java index bfb33678dc7e..bc6c38ebabad 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/git/central/CentralGitServiceCEImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/git/central/CentralGitServiceCEImpl.java @@ -1,6 +1,8 @@ package com.appsmith.server.git.central; import com.appsmith.external.constants.AnalyticsEvents; +import com.appsmith.external.constants.ErrorReferenceDocUrl; +import com.appsmith.external.dtos.GitBranchDTO; import com.appsmith.external.dtos.GitRefDTO; import com.appsmith.external.dtos.GitStatusDTO; import com.appsmith.external.dtos.MergeStatusDTO; @@ -13,6 +15,7 @@ import com.appsmith.git.dto.GitUser; import com.appsmith.server.acl.AclPermission; 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; @@ -20,6 +23,7 @@ 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; @@ -29,6 +33,7 @@ import com.appsmith.server.dtos.ArtifactImportDTO; import com.appsmith.server.dtos.AutoCommitResponseDTO; import com.appsmith.server.dtos.GitConnectDTO; +import com.appsmith.server.dtos.GitDocsDTO; import com.appsmith.server.dtos.GitPullDTO; import com.appsmith.server.exceptions.AppsmithError; import com.appsmith.server.exceptions.AppsmithException; @@ -40,9 +45,12 @@ import com.appsmith.server.git.resolver.GitHandlingServiceResolver; import com.appsmith.server.git.utils.GitAnalyticsUtils; import com.appsmith.server.git.utils.GitProfileUtils; +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.GitArtifactHelper; import com.appsmith.server.services.SessionUserService; import com.appsmith.server.services.UserDataService; @@ -52,7 +60,9 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.eclipse.jgit.api.errors.InvalidRemoteException; +import org.eclipse.jgit.api.errors.RefNotFoundException; import org.eclipse.jgit.api.errors.TransportException; +import org.eclipse.jgit.errors.RepositoryNotFoundException; import org.eclipse.jgit.lib.BranchTrackingStatus; import org.springframework.stereotype.Service; import org.springframework.transaction.reactive.TransactionalOperator; @@ -68,6 +78,7 @@ import java.nio.file.Path; import java.time.Instant; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; @@ -95,14 +106,15 @@ public class CentralGitServiceCEImpl implements CentralGitServiceCE { private final GitRedisUtils gitRedisUtils; private final GitProfileUtils gitProfileUtils; - private final GitAnalyticsUtils gitAnalyticsUtils; + protected final GitAnalyticsUtils gitAnalyticsUtils; private final UserDataService userDataService; - private final SessionUserService sessionUserService; + protected final SessionUserService sessionUserService; protected final GitArtifactHelperResolver gitArtifactHelperResolver; protected final GitHandlingServiceResolver gitHandlingServiceResolver; private final GitPrivateRepoHelper gitPrivateRepoHelper; + private final GitDeployKeysRepository gitDeployKeysRepository; private final DatasourceService datasourceService; private final DatasourcePermission datasourcePermission; @@ -369,8 +381,8 @@ public Mono checkoutReference( getBaseAndBranchedArtifacts(referenceArtifactId, artifactType); return baseAndBranchedArtifactMono.flatMap(artifactTuples -> { - Artifact sourceArtifact = artifactTuples.getT1(); - return checkoutReference(sourceArtifact, gitRefDTO, addFileLock, gitType); + Artifact baseArtifact = artifactTuples.getT1(); + return checkoutReference(baseArtifact, gitRefDTO, addFileLock, gitType); }); } @@ -2053,6 +2065,330 @@ protected Mono discardChanges(Artifact branchedArtifact, Git recreatedArtifactFromLastCommit.subscribe(sink::success, sink::error, null, sink.currentContext())); } + public Mono> listBranchForArtifact( + String branchedArtifactId, Boolean pruneBranches, ArtifactType artifactType, GitType gitType) { + return getBranchList(branchedArtifactId, pruneBranches, true, artifactType, gitType); + } + + protected Mono> getBranchList( + String branchedArtifactId, + Boolean pruneBranches, + boolean syncDefaultBranchWithRemote, + ArtifactType artifactType, + GitType gitType) { + + GitArtifactHelper gitArtifactHelper = gitArtifactHelperResolver.getArtifactHelper(artifactType); + AclPermission artifactEditPermission = gitArtifactHelper.getArtifactEditPermission(); + + Mono> baseAndBranchedArtifactMono = + getBaseAndBranchedArtifacts(branchedArtifactId, artifactType, artifactEditPermission); + + return baseAndBranchedArtifactMono.flatMap(artifactTuples -> { + return getBranchList( + artifactTuples.getT1(), + artifactTuples.getT2(), + pruneBranches, + syncDefaultBranchWithRemote, + gitType); + }); + } + + protected Mono> getBranchList( + Artifact baseArtifact, + Artifact branchedArtifact, + Boolean pruneBranches, + boolean syncDefaultBranchWithRemote, + GitType gitType) { + + GitArtifactMetadata baseGitData = baseArtifact.getGitArtifactMetadata(); + GitArtifactMetadata branchedGitData = branchedArtifact.getGitArtifactMetadata(); + + if (isBaseGitMetadataInvalid(baseGitData, gitType) || branchedGitData == null) { + return Mono.error(new AppsmithException(AppsmithError.INVALID_GIT_CONFIGURATION, GIT_CONFIG_ERROR)); + } + + final String workspaceId = baseArtifact.getWorkspaceId(); + final String baseArtifactId = baseGitData.getDefaultArtifactId(); + final String repoName = baseGitData.getRepoName(); + final String currentBranch = branchedGitData.getRefName(); + + ArtifactJsonTransformationDTO jsonTransformationDTO = new ArtifactJsonTransformationDTO(); + jsonTransformationDTO.setRepoName(repoName); + jsonTransformationDTO.setWorkspaceId(workspaceId); + jsonTransformationDTO.setBaseArtifactId(baseArtifactId); + jsonTransformationDTO.setRefName(currentBranch); + // not that it matters + jsonTransformationDTO.setRefType(branchedGitData.getRefType()); + jsonTransformationDTO.setArtifactType(baseArtifact.getArtifactType()); + + if (!hasText(baseArtifactId) || !hasText(repoName) || !hasText(currentBranch)) { + log.error( + "Git config is not present for artifact {} of type {}", + baseArtifact.getId(), + baseArtifact.getArtifactType()); + return Mono.error(new AppsmithException(AppsmithError.INVALID_GIT_CONFIGURATION, GIT_CONFIG_ERROR)); + } + + Mono baseBranchMono; + if (TRUE.equals(pruneBranches) && syncDefaultBranchWithRemote) { + baseBranchMono = syncDefaultBranchNameFromRemote(baseGitData, jsonTransformationDTO, gitType); + } else { + baseBranchMono = Mono.just(GitUtils.getDefaultBranchName(baseGitData)); + } + + Mono> branchMono = baseBranchMono + .flatMap(baseBranchName -> { + return getBranchListWithDefaultBranchName( + baseArtifact, baseBranchName, currentBranch, pruneBranches, gitType); + }) + .onErrorResume(throwable -> { + if (throwable instanceof RepositoryNotFoundException) { + return handleRepoNotFoundException(jsonTransformationDTO, gitType); + } + return Mono.error(throwable); + }); + + return Mono.create(sink -> branchMono.subscribe(sink::success, sink::error, null, sink.currentContext())); + } + + private Mono syncDefaultBranchNameFromRemote( + GitArtifactMetadata metadata, ArtifactJsonTransformationDTO jsonTransformationDTO, GitType gitType) { + ArtifactType artifactType = jsonTransformationDTO.getArtifactType(); + GitHandlingService gitHandlingService = gitHandlingServiceResolver.getGitHandlingService(gitType); + + return gitRedisUtils + .acquireGitLock( + jsonTransformationDTO.getArtifactType(), + metadata.getDefaultArtifactId(), + GitConstants.GitCommandConstants.SYNC_BRANCH, + TRUE) + .then(gitHandlingService + .getDefaultBranchFromRepository(jsonTransformationDTO, metadata) + .flatMap(defaultBranchNameInRemote -> { + String defaultBranchInDb = GitUtils.getDefaultBranchName(metadata); + // If the default branch name in remote is empty or same as the one in DB, nothing to do + + if (!hasText(defaultBranchNameInRemote) + || defaultBranchNameInRemote.equals(defaultBranchInDb)) { + return Mono.just(defaultBranchInDb); + } + + // default branch has been changed in remote + return updateDefaultBranchName( + metadata.getDefaultArtifactId(), + defaultBranchNameInRemote, + jsonTransformationDTO, + artifactType, + gitType) + .then() + .thenReturn(defaultBranchNameInRemote); + }) + .flatMap(branchName -> gitRedisUtils + .releaseFileLock( + jsonTransformationDTO.getArtifactType(), metadata.getDefaultArtifactId(), TRUE) + .thenReturn(branchName))); + } + + private Flux updateDefaultBranchName( + String baseArtifactId, + String newDefaultBranchName, + ArtifactJsonTransformationDTO jsonTransformationDTO, + ArtifactType artifactType, + GitType gitType) { + // Get the artifact from DB by new defaultBranchName + GitArtifactHelper gitArtifactHelper = gitArtifactHelperResolver.getArtifactHelper(artifactType); + AclPermission artifactEditPermission = gitArtifactHelper.getArtifactEditPermission(); + + Mono baseArtifactMono = + gitArtifactHelper.getArtifactById(baseArtifactId, artifactEditPermission); + + GitRefDTO gitRefDTO = new GitRefDTO(); + gitRefDTO.setRefName(newDefaultBranchName); + gitRefDTO.setRefType(RefType.branch); + gitRefDTO.setDefault(true); + + // potentially problem in the flow, + // we are checking out to the branch after creation, + // and this is just a remote reference + return baseArtifactMono + .flatMap(baseArtifact -> { + // if the artifact with newDefaultBranch name is present locally then it could be checked out + // since this operation would happen inside a file lock, we don't require it. + return checkoutReference(baseArtifact, gitRefDTO, false, gitType) + .map(newDefaultBranchArtifact -> (Artifact) newDefaultBranchArtifact) + .onErrorResume(error -> { + if (error instanceof RefNotFoundException + || (error instanceof AppsmithException appsmithException + && appsmithException + .getAppErrorCode() + .equals(AppsmithError.NO_RESOURCE_FOUND.getAppErrorCode()))) { + log.error( + "Artifact with base id {} and branch name {} not found locally", + baseArtifactId, + newDefaultBranchName); + return checkoutRemoteReference(baseArtifact, gitRefDTO, gitType); + } + + return Mono.error(error); + }); + }) + .thenMany(Flux.defer( + () -> gitArtifactHelper.getAllArtifactByBaseId(baseArtifactId, artifactEditPermission))) + .flatMap(artifact -> { + artifact.getGitArtifactMetadata().setDefaultBranchName(newDefaultBranchName); + // clear the branch protection rules as the default branch name has been changed + artifact.getGitArtifactMetadata().setBranchProtectionRules(null); + return gitArtifactHelper.saveArtifact(artifact); + }); + } + + private Mono> handleRepoNotFoundException( + ArtifactJsonTransformationDTO jsonTransformationDTO, GitType gitType) { + // 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 + + GitHandlingService gitHandlingService = gitHandlingServiceResolver.getGitHandlingService(gitType); + GitArtifactHelper gitArtifactHelper = + gitArtifactHelperResolver.getArtifactHelper(jsonTransformationDTO.getArtifactType()); + AclPermission artifactEditPermission = gitArtifactHelper.getArtifactEditPermission(); + AclPermission artifactReadPermission = gitArtifactHelper.getArtifactReadPermission(); + + Mono baseArtifactMono = + gitArtifactHelper.getArtifactById(jsonTransformationDTO.getBaseArtifactId(), artifactEditPermission); + + return baseArtifactMono.flatMap(baseArtifact -> { + GitArtifactMetadata gitArtifactMetadata = baseArtifact.getGitArtifactMetadata(); + GitAuth gitAuth = gitArtifactMetadata.getGitAuth(); + GitConnectDTO gitConnectDTO = new GitConnectDTO(); + gitConnectDTO.setRemoteUrl(gitArtifactMetadata.getRemoteUrl()); + + return gitHandlingService + .fetchRemoteRepository(gitConnectDTO, gitAuth, baseArtifact, gitArtifactMetadata.getRepoName()) + .flatMap(defaultBranch -> gitHandlingService.listReferences(jsonTransformationDTO, true)) + .flatMap(branches -> { + List branchesToCheckout = new ArrayList<>(); + List gitBranchDTOList = new ArrayList<>(); + for (String branch : branches) { + GitBranchDTO gitBranchDTO = new GitBranchDTO(); + gitBranchDTO.setBranchName(branch); + + if (branch.startsWith(ORIGIN)) { + // remove origin/ prefix from the remote branch name + String branchName = branch.replace(ORIGIN, REMOTE_NAME_REPLACEMENT); + // The root defaultArtifact is always there, no need to check out it again + if (!branchName.equals(gitArtifactMetadata.getBranchName())) { + branchesToCheckout.add(branchName); + } + + } else if (branch.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); + } + } + + ArtifactJsonTransformationDTO branchCheckoutDTO = new ArtifactJsonTransformationDTO(); + branchCheckoutDTO.setWorkspaceId(baseArtifact.getWorkspaceId()); + branchCheckoutDTO.setArtifactType(baseArtifact.getArtifactType()); + branchCheckoutDTO.setRepoName(gitArtifactMetadata.getRepoName()); + + return Flux.fromIterable(branchesToCheckout) + .flatMap(branchName -> gitArtifactHelper + .getArtifactByBaseIdAndBranchName( + gitArtifactMetadata.getDefaultArtifactId(), + branchName, + artifactReadPermission) + // checkout the branch locally + .flatMap(artifact -> { + // 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); + + branchCheckoutDTO.setRefName(branchName); + return gitHandlingService.checkoutRemoteReference(branchCheckoutDTO); + }) + // Return empty mono when the branched defaultArtifact is not in db + .onErrorResume(throwable -> Mono.empty())) + .then(Mono.just(gitBranchDTOList)); + }); + }); + } + + private Mono> getBranchListWithDefaultBranchName( + Artifact baseArtifact, + String defaultBranchName, + String currentBranch, + boolean pruneBranches, + GitType gitType) { + + ArtifactType artifactType = baseArtifact.getArtifactType(); + GitArtifactMetadata baseGitData = baseArtifact.getGitArtifactMetadata(); + GitHandlingService gitHandlingService = gitHandlingServiceResolver.getGitHandlingService(gitType); + + ArtifactJsonTransformationDTO jsonTransformationDTO = new ArtifactJsonTransformationDTO(); + jsonTransformationDTO.setRepoName(baseGitData.getRepoName()); + jsonTransformationDTO.setWorkspaceId(baseArtifact.getWorkspaceId()); + jsonTransformationDTO.setBaseArtifactId(baseGitData.getDefaultArtifactId()); + jsonTransformationDTO.setRefName(currentBranch); + jsonTransformationDTO.setRefType(baseGitData.getRefType()); + jsonTransformationDTO.setArtifactType(baseArtifact.getArtifactType()); + + return gitRedisUtils + .acquireGitLock( + artifactType, + baseGitData.getDefaultArtifactId(), + GitConstants.GitCommandConstants.LIST_BRANCH, + TRUE) + .flatMap(ignoredLock -> { + Mono> listBranchesMono = + Mono.defer(() -> gitHandlingService.listReferences(jsonTransformationDTO, false)); + + if (TRUE.equals(pruneBranches)) { + return gitHandlingService + .fetchRemoteChanges(jsonTransformationDTO, baseGitData.getGitAuth(), TRUE) + .then(listBranchesMono); + } + return listBranchesMono; + }) + .onErrorResume(error -> { + return gitRedisUtils + .releaseFileLock(artifactType, baseGitData.getDefaultArtifactId(), TRUE) + .then(Mono.error(error)); + }) + .flatMap(branches -> { + return gitRedisUtils + .releaseFileLock(artifactType, baseGitData.getDefaultArtifactId(), TRUE) + .thenReturn(branches.stream() + .map(branchName -> { + GitBranchDTO gitBranchDTO = new GitBranchDTO(); + gitBranchDTO.setBranchName(branchName); + if (branchName.equalsIgnoreCase(defaultBranchName)) { + gitBranchDTO.setDefault(true); + } + return gitBranchDTO; + }) + .toList()); + }) + .flatMap(gitBranchDTOList -> FALSE.equals(pruneBranches) + ? Mono.just(gitBranchDTOList) + : gitAnalyticsUtils + .addAnalyticsForGitOperation( + AnalyticsEvents.GIT_PRUNE, + baseArtifact, + baseArtifact.getGitArtifactMetadata().getIsRepoPrivate()) + .thenReturn(gitBranchDTOList)); + } + @Override public Mono> updateProtectedBranches( String baseArtifactId, List branchNames, ArtifactType artifactType) { @@ -2175,4 +2511,85 @@ public Mono getAutoCommitProgress( String artifactId, String branchName, ArtifactType artifactType) { return gitAutoCommitHelper.getAutoCommitProgress(artifactId, branchName); } + + @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 getGitArtifactMetadata(String baseArtifactId, ArtifactType artifactType) { + + GitArtifactHelper gitArtifactHelper = gitArtifactHelperResolver.getArtifactHelper(artifactType); + AclPermission artifactEditPermission = gitArtifactHelper.getArtifactEditPermission(); + + Mono baseArtifactMono = + gitArtifactHelper.getArtifactById(baseArtifactId, artifactEditPermission); + + return Mono.zip(baseArtifactMono, userDataService.getForCurrentUser()).map(tuple -> { + Artifact baseArtifact = tuple.getT1(); + UserData userData = tuple.getT2(); + Map gitProfiles = new HashMap<>(); + GitArtifactMetadata baseGitMetadata = baseArtifact.getGitArtifactMetadata(); + + if (!CollectionUtils.isEmpty(userData.getGitProfiles())) { + gitProfiles.put(DEFAULT, userData.getGitProfileByKey(DEFAULT)); + gitProfiles.put(baseArtifactId, userData.getGitProfileByKey(baseArtifactId)); + } + if (baseGitMetadata == null) { + GitArtifactMetadata res = new GitArtifactMetadata(); + res.setGitProfiles(gitProfiles); + return res; + } + + baseGitMetadata.setGitProfiles(gitProfiles); + if (baseGitMetadata.getGitAuth() != null) { + baseGitMetadata.setPublicKey(baseGitMetadata.getGitAuth().getPublicKey()); + } + + baseGitMetadata.setDocUrl(Assets.GIT_DEPLOY_KEY_DOC_URL); + return baseGitMetadata; + }); + } + + /** + * 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 artifact 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); + } } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/git/central/CentralGitServiceImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/git/central/CentralGitServiceImpl.java index 7e1843a4b0ba..2a77924f307e 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/git/central/CentralGitServiceImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/git/central/CentralGitServiceImpl.java @@ -11,6 +11,7 @@ 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.SessionUserService; import com.appsmith.server.services.UserDataService; import com.appsmith.server.services.WorkspaceService; @@ -33,6 +34,7 @@ public CentralGitServiceImpl( GitArtifactHelperResolver gitArtifactHelperResolver, GitHandlingServiceResolver gitHandlingServiceResolver, GitPrivateRepoHelper gitPrivateRepoHelper, + GitDeployKeysRepository gitDeployKeysRepository, DatasourceService datasourceService, DatasourcePermission datasourcePermission, WorkspaceService workspaceService, @@ -51,6 +53,7 @@ public CentralGitServiceImpl( gitArtifactHelperResolver, gitHandlingServiceResolver, gitPrivateRepoHelper, + gitDeployKeysRepository, datasourceService, datasourcePermission, workspaceService, diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/git/central/GitHandlingServiceCE.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/git/central/GitHandlingServiceCE.java index 89b3d7d64a0e..43459bd9682a 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/git/central/GitHandlingServiceCE.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/git/central/GitHandlingServiceCE.java @@ -50,6 +50,9 @@ Mono> listBranches( Mono> listReferences( ArtifactJsonTransformationDTO artifactJsonTransformationDTO, Boolean checkRemoteReferences); + Mono getDefaultBranchFromRepository( + ArtifactJsonTransformationDTO jsonTransformationDTO, GitArtifactMetadata gitArtifactMetadata); + Mono validateEmptyRepository(ArtifactJsonTransformationDTO artifactJsonTransformationDTO); Mono initialiseReadMe( @@ -77,6 +80,8 @@ Mono recreateArtifactJsonFromLastCommit( Mono createGitReference(ArtifactJsonTransformationDTO artifactJsonTransformationDTO, GitRefDTO gitRefDTO); + Mono checkoutRemoteReference(ArtifactJsonTransformationDTO jsonTransformationDTO); + Mono deleteGitReference(ArtifactJsonTransformationDTO jsonTransformationDTO); Mono checkoutArtifact(ArtifactJsonTransformationDTO jsonTransformationDTO); diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/git/controllers/GitApplicationController.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/git/controllers/GitApplicationController.java new file mode 100644 index 000000000000..ccff87e094cf --- /dev/null +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/git/controllers/GitApplicationController.java @@ -0,0 +1,20 @@ +package com.appsmith.server.git.controllers; + +import com.appsmith.server.constants.Url; +import com.appsmith.server.git.autocommit.AutoCommitService; +import com.appsmith.server.git.central.CentralGitService; +import com.appsmith.server.git.utils.GitProfileUtils; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@Slf4j +@RestController +@RequestMapping(Url.GIT_APPLICATION_URL) +public class GitApplicationController extends GitApplicationControllerCE { + + public GitApplicationController( + CentralGitService centralGitService, GitProfileUtils gitProfileUtils, AutoCommitService autoCommitService) { + super(centralGitService, gitProfileUtils, autoCommitService); + } +} diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/git/controllers/GitApplicationControllerCE.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/git/controllers/GitApplicationControllerCE.java new file mode 100644 index 000000000000..e08e5716fb3b --- /dev/null +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/git/controllers/GitApplicationControllerCE.java @@ -0,0 +1,225 @@ +package com.appsmith.server.git.controllers; + +import com.appsmith.external.dtos.GitBranchDTO; +import com.appsmith.external.dtos.GitRefDTO; +import com.appsmith.external.dtos.GitStatusDTO; +import com.appsmith.external.git.constants.ce.RefType; +import com.appsmith.external.views.Views; +import com.appsmith.git.dto.CommitDTO; +import com.appsmith.server.constants.ArtifactType; +import com.appsmith.server.constants.FieldName; +import com.appsmith.server.constants.Url; +import com.appsmith.server.domains.Artifact; +import com.appsmith.server.domains.GitArtifactMetadata; +import com.appsmith.server.dtos.AutoCommitResponseDTO; +import com.appsmith.server.dtos.BranchProtectionRequestDTO; +import com.appsmith.server.dtos.GitConnectDTO; +import com.appsmith.server.dtos.GitPullDTO; +import com.appsmith.server.dtos.ResponseDTO; +import com.appsmith.server.git.autocommit.AutoCommitService; +import com.appsmith.server.git.central.CentralGitService; +import com.appsmith.server.git.central.GitType; +import com.appsmith.server.git.utils.GitProfileUtils; +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.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PatchMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseStatus; +import reactor.core.publisher.Mono; + +import java.util.List; + +@Slf4j +@RequestMapping(Url.GIT_APPLICATION_URL) +@RequiredArgsConstructor +public class GitApplicationControllerCE { + + protected final CentralGitService centralGitService; + protected final GitProfileUtils gitProfileUtils; + protected final AutoCommitService autoCommitService; + + protected static final ArtifactType ARTIFACT_TYPE = ArtifactType.APPLICATION; + protected static final GitType GIT_TYPE = GitType.FILE_SYSTEM; + + @JsonView({Views.Metadata.class}) + @GetMapping("/{baseApplicationId}/metadata") + public Mono> getGitMetadata(@PathVariable String baseApplicationId) { + return centralGitService + .getGitArtifactMetadata(baseApplicationId, ARTIFACT_TYPE) + .map(metadata -> new ResponseDTO<>(HttpStatus.OK.value(), metadata, null)); + } + + @JsonView(Views.Public.class) + @PostMapping("/{applicationId}/connect") + public Mono> connectApplicationToRemoteRepo( + @PathVariable String applicationId, + @RequestBody GitConnectDTO gitConnectDTO, + @RequestHeader("Origin") String originHeader) { + return centralGitService + .connectArtifactToGit(applicationId, gitConnectDTO, originHeader, ARTIFACT_TYPE, GIT_TYPE) + .map(application -> new ResponseDTO<>(HttpStatus.OK.value(), application, null)); + } + + @JsonView(Views.Public.class) + @PostMapping("/{branchedApplicationId}/commit") + @ResponseStatus(HttpStatus.CREATED) + public Mono> commit( + @RequestBody CommitDTO commitDTO, @PathVariable String branchedApplicationId) { + log.info("Going to commit branchedApplicationId {}", branchedApplicationId); + return centralGitService + .commitArtifact(commitDTO, branchedApplicationId, ARTIFACT_TYPE, GIT_TYPE) + .map(result -> new ResponseDTO<>(HttpStatus.CREATED.value(), result, null)); + } + + @JsonView(Views.Public.class) + @PostMapping("/{referencedApplicationId}/create-ref") + @ResponseStatus(HttpStatus.CREATED) + public Mono> createReference( + @PathVariable String referencedApplicationId, + @RequestHeader(name = FieldName.BRANCH_NAME, required = false) String srcBranch, + @RequestBody GitRefDTO gitRefDTO) { + log.info( + "Going to create a reference from referencedApplicationId {}, srcBranch {}", + referencedApplicationId, + srcBranch); + return centralGitService + .createReference(referencedApplicationId, gitRefDTO, ArtifactType.APPLICATION, GIT_TYPE) + .map(result -> new ResponseDTO<>(HttpStatus.CREATED.value(), result, null)); + } + + @JsonView(Views.Public.class) + @PostMapping("/{referencedApplicationId}/checkout-ref") + public Mono> checkoutReference( + @PathVariable String referencedApplicationId, @RequestBody GitRefDTO gitRefDTO) { + return centralGitService + .checkoutReference(referencedApplicationId, gitRefDTO, true, ARTIFACT_TYPE, GIT_TYPE) + .map(result -> new ResponseDTO<>(HttpStatus.OK.value(), result, null)); + } + + @JsonView(Views.Public.class) + @PostMapping("/{branchedApplicationId}/disconnect") + public Mono> disconnectFromRemote(@PathVariable String branchedApplicationId) { + log.info("Going to remove the remoteUrl for application {}", branchedApplicationId); + return centralGitService + .detachRemote(branchedApplicationId, ARTIFACT_TYPE, GIT_TYPE) + .map(result -> new ResponseDTO<>(HttpStatus.OK.value(), result, null)); + } + + @JsonView(Views.Public.class) + @GetMapping("/{branchedApplicationId}/pull") + public Mono> pull(@PathVariable String branchedApplicationId) { + log.info("Going to pull the latest for branchedApplicationId {}", branchedApplicationId); + return centralGitService + .pullArtifact(branchedApplicationId, ARTIFACT_TYPE, GIT_TYPE) + .map(result -> new ResponseDTO<>(HttpStatus.OK.value(), result, null)); + } + + @JsonView(Views.Public.class) + @GetMapping("/{branchedApplicationId}/status") + public Mono> getStatus( + @PathVariable String branchedApplicationId, + @RequestParam(required = false, defaultValue = "true") Boolean compareRemote) { + log.info("Going to get status for branchedApplicationId {}", branchedApplicationId); + return centralGitService + .getStatus(branchedApplicationId, compareRemote, ARTIFACT_TYPE, GIT_TYPE) + .map(result -> new ResponseDTO<>(HttpStatus.OK.value(), result, null)); + } + + @JsonView(Views.Public.class) + @GetMapping("/{referencedApplicationId}/fetch/remote") + public Mono> fetchRemoteChanges( + @PathVariable String referencedApplicationId, + @RequestHeader(required = false, defaultValue = "branch") RefType refType) { + log.info("Going to compare with remote for default referencedApplicationId {}", referencedApplicationId); + return centralGitService + .fetchRemoteChanges(referencedApplicationId, true, ARTIFACT_TYPE, GIT_TYPE, refType) + .map(result -> new ResponseDTO<>(HttpStatus.OK.value(), result, null)); + } + + @JsonView(Views.Public.class) + @DeleteMapping("/{baseArtifactId}/ref") + public Mono> deleteBranch( + @PathVariable String baseArtifactId, @RequestBody GitRefDTO gitRefDTO) { + log.info("Going to delete ref {} for baseApplicationId {}", gitRefDTO.getRefName(), baseArtifactId); + return centralGitService + .deleteGitReference(baseArtifactId, gitRefDTO, ARTIFACT_TYPE, GIT_TYPE) + .map(application -> new ResponseDTO<>(HttpStatus.OK.value(), application, null)); + } + + @JsonView(Views.Public.class) + @PutMapping("/{branchedApplicationId}/discard") + public Mono> discardChanges(@PathVariable String branchedApplicationId) { + log.info("Going to discard changes for branchedApplicationId {}", branchedApplicationId); + return centralGitService + .discardChanges(branchedApplicationId, ARTIFACT_TYPE, GIT_TYPE) + .map(result -> new ResponseDTO<>((HttpStatus.OK.value()), result, null)); + } + + @JsonView(Views.Public.class) + @PostMapping("/{baseArtifactId}/branch/protected") + public Mono>> updateProtectedBranches( + @PathVariable String baseArtifactId, + @RequestBody @Valid BranchProtectionRequestDTO branchProtectionRequestDTO) { + return centralGitService + .updateProtectedBranches(baseArtifactId, branchProtectionRequestDTO.getBranchNames(), ARTIFACT_TYPE) + .map(data -> new ResponseDTO<>(HttpStatus.OK.value(), data, null)); + } + + @JsonView(Views.Public.class) + @GetMapping("/{baseArtifactId}/branch/protected") + public Mono>> getProtectedBranches(@PathVariable String baseArtifactId) { + return centralGitService + .getProtectedBranches(baseArtifactId, ARTIFACT_TYPE) + .map(list -> new ResponseDTO<>(HttpStatus.OK.value(), list, null)); + } + + @JsonView(Views.Public.class) + @PostMapping("/{branchedApplicationId}/auto-commit") + public Mono> autoCommitApplication(@PathVariable String branchedApplicationId) { + return autoCommitService + .autoCommitApplication(branchedApplicationId) + .map(data -> new ResponseDTO<>(HttpStatus.OK.value(), data, null)); + } + + @JsonView(Views.Public.class) + @GetMapping("/{baseApplicationId}/auto-commit/progress") + public Mono> getAutoCommitProgress( + @PathVariable String baseApplicationId, + @RequestHeader(name = FieldName.BRANCH_NAME, required = false) String branchName) { + return centralGitService + .getAutoCommitProgress(baseApplicationId, branchName, ARTIFACT_TYPE) + .map(data -> new ResponseDTO<>(HttpStatus.OK.value(), data, null)); + } + + @JsonView(Views.Public.class) + @PatchMapping("/{baseArtifactId}/auto-commit/toggle") + public Mono> toggleAutoCommitEnabled(@PathVariable String baseArtifactId) { + return centralGitService + .toggleAutoCommitEnabled(baseArtifactId, ARTIFACT_TYPE) + .map(data -> new ResponseDTO<>(HttpStatus.OK.value(), data, null)); + } + + @JsonView(Views.Public.class) + @GetMapping("/{branchedApplicationId}/branches") + public Mono>> branch( + @PathVariable String branchedApplicationId, + @RequestParam(required = false, defaultValue = "false") Boolean pruneBranches) { + log.debug("Going to get branch list for application {}", branchedApplicationId); + return centralGitService + .listBranchForArtifact( + branchedApplicationId, BooleanUtils.isTrue(pruneBranches), ARTIFACT_TYPE, GIT_TYPE) + .map(result -> new ResponseDTO<>(HttpStatus.OK.value(), result, null)); + } +} diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/git/controllers/GitArtifactController.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/git/controllers/GitArtifactController.java new file mode 100644 index 000000000000..e607a0dd151a --- /dev/null +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/git/controllers/GitArtifactController.java @@ -0,0 +1,18 @@ +package com.appsmith.server.git.controllers; + +import com.appsmith.server.constants.Url; +import com.appsmith.server.git.central.CentralGitService; +import com.appsmith.server.git.utils.GitProfileUtils; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@Slf4j +@RestController +@RequestMapping(Url.GIT_ARTIFACT_URL) +public class GitArtifactController extends GitArtifactControllerCE { + + public GitArtifactController(CentralGitService centralGitService, GitProfileUtils gitProfileUtils) { + super(centralGitService, gitProfileUtils); + } +} diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/git/controllers/GitArtifactControllerCE.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/git/controllers/GitArtifactControllerCE.java new file mode 100644 index 000000000000..71215783c16e --- /dev/null +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/git/controllers/GitArtifactControllerCE.java @@ -0,0 +1,74 @@ +package com.appsmith.server.git.controllers; + +import com.appsmith.external.views.Views; +import com.appsmith.server.constants.ArtifactType; +import com.appsmith.server.constants.Url; +import com.appsmith.server.domains.GitAuth; +import com.appsmith.server.dtos.ApplicationImportDTO; +import com.appsmith.server.dtos.GitConnectDTO; +import com.appsmith.server.dtos.GitDeployKeyDTO; +import com.appsmith.server.dtos.GitDocsDTO; +import com.appsmith.server.dtos.ResponseDTO; +import com.appsmith.server.git.central.CentralGitService; +import com.appsmith.server.git.central.GitType; +import com.appsmith.server.git.utils.GitProfileUtils; +import com.appsmith.server.helpers.GitDeployKeyGenerator; +import com.fasterxml.jackson.annotation.JsonView; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import reactor.core.publisher.Mono; + +import java.util.List; + +@Slf4j +@RequestMapping(Url.GIT_ARTIFACT_URL) +@RequiredArgsConstructor +public class GitArtifactControllerCE { + + protected final CentralGitService centralGitService; + protected final GitProfileUtils gitProfileUtils; + + protected static final GitType GIT_TYPE = GitType.FILE_SYSTEM; + + @JsonView(Views.Public.class) + @PostMapping("/import") + public Mono> importApplicationFromGit( + @RequestParam String workspaceId, @RequestBody GitConnectDTO gitConnectDTO) { + + // TODO: remove artifact type from methods. + return centralGitService + .importArtifactFromGit(workspaceId, gitConnectDTO, ArtifactType.APPLICATION, GIT_TYPE) + .map(artifactImportDTO -> (ApplicationImportDTO) artifactImportDTO) + .map(result -> new ResponseDTO<>(HttpStatus.CREATED.value(), result, null)); + } + + @JsonView(Views.Public.class) + @GetMapping("/doc-urls") + public Mono>> getGitDocs() { + return centralGitService + .getGitDocUrls() + .map(gitDocDTO -> new ResponseDTO<>(HttpStatus.OK.value(), gitDocDTO, null)); + } + + @JsonView(Views.Public.class) + @GetMapping("/protocol/key-types") + public Mono>> getSupportedKeys() { + log.info("Going to list the list of supported keys"); + return Mono.just(GitDeployKeyGenerator.getSupportedProtocols()) + .map(gitDeployKeyDTOS -> new ResponseDTO<>(HttpStatus.OK.value(), gitDeployKeyDTOS, null)); + } + + @JsonView(Views.Public.class) + @GetMapping("/import/keys") + public Mono> generateKeyForGitImport(@RequestParam(required = false) String keyType) { + return centralGitService + .generateSSHKey(keyType) + .map(result -> new ResponseDTO<>(HttpStatus.OK.value(), result, null)); + } +} diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/git/fs/GitFSServiceCEImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/git/fs/GitFSServiceCEImpl.java index 60d834763246..eafd070e04fe 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/git/fs/GitFSServiceCEImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/git/fs/GitFSServiceCEImpl.java @@ -273,6 +273,27 @@ public Mono> listReferences( return Mono.just(List.of()); } + @Override + public Mono getDefaultBranchFromRepository( + ArtifactJsonTransformationDTO jsonTransformationDTO, GitArtifactMetadata baseGitData) { + if (isGitAuthInvalid(baseGitData.getGitAuth())) { + return Mono.error(new AppsmithException(AppsmithError.INVALID_GIT_CONFIGURATION, GIT_CONFIG_ERROR)); + } + + String publicKey = baseGitData.getGitAuth().getPublicKey(); + String privateKey = baseGitData.getGitAuth().getPrivateKey(); + + GitArtifactHelper gitArtifactHelper = + gitArtifactHelperResolver.getArtifactHelper(jsonTransformationDTO.getArtifactType()); + + Path repoSuffixPath = gitArtifactHelper.getRepoSuffixPath( + jsonTransformationDTO.getWorkspaceId(), + jsonTransformationDTO.getBaseArtifactId(), + jsonTransformationDTO.getRepoName()); + + return fsGitHandler.getRemoteDefaultBranch(repoSuffixPath, baseGitData.getRemoteUrl(), privateKey, publicKey); + } + @Override public Mono validateEmptyRepository(ArtifactJsonTransformationDTO artifactJsonTransformationDTO) { GitArtifactHelper gitArtifactHelper = @@ -629,6 +650,19 @@ public Mono createGitReference(ArtifactJsonTransformationDTO jsonTransfo return fsGitHandler.createAndCheckoutReference(repoSuffix, gitRefDTO); } + @Override + public Mono checkoutRemoteReference(ArtifactJsonTransformationDTO jsonTransformationDTO) { + GitArtifactHelper gitArtifactHelper = + gitArtifactHelperResolver.getArtifactHelper(jsonTransformationDTO.getArtifactType()); + + Path repoSuffix = gitArtifactHelper.getRepoSuffixPath( + jsonTransformationDTO.getWorkspaceId(), + jsonTransformationDTO.getBaseArtifactId(), + jsonTransformationDTO.getRepoName()); + + return fsGitHandler.checkoutRemoteBranch(repoSuffix, jsonTransformationDTO.getRefName()); + } + @Override public Mono deleteGitReference(ArtifactJsonTransformationDTO jsonTransformationDTO) { ArtifactType artifactType = jsonTransformationDTO.getArtifactType(); @@ -661,7 +695,6 @@ public Mono deleteGitReference(ArtifactJsonTransformationDTO jsonTransf @Override public Mono checkoutArtifact(ArtifactJsonTransformationDTO jsonTransformationDTO) { - GitArtifactHelper gitArtifactHelper = gitArtifactHelperResolver.getArtifactHelper(jsonTransformationDTO.getArtifactType()); diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/git/utils/GitAnalyticsUtils.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/git/utils/GitAnalyticsUtils.java index bc8f8922db3d..171f201dba6f 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/git/utils/GitAnalyticsUtils.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/git/utils/GitAnalyticsUtils.java @@ -187,4 +187,33 @@ public Mono sendBranchProtectionAnalytics( return Flux.merge(eventSenderMonos).then(); } + + /** + * Generic method to send analytics for git operations. + * + * @param analyticsEvents Name of the event + * @param artifact Application object + * @param extraProps Extra properties that need to be passed along with default ones. + * @return A void mono + */ + public Mono sendGitAnalyticsEvent( + AnalyticsEvents analyticsEvents, Artifact artifact, Map extraProps) { + GitArtifactMetadata gitData = artifact.getGitArtifactMetadata(); + Map analyticsProps = new HashMap<>(); + + // TODO: analytics generalisation + analyticsProps.put("appId", gitData.getDefaultArtifactId()); + analyticsProps.put("orgId", artifact.getWorkspaceId()); + analyticsProps.put(FieldName.GIT_HOSTING_PROVIDER, GitUtils.getGitProviderName(gitData.getRemoteUrl())); + analyticsProps.put(FieldName.REPO_URL, gitData.getRemoteUrl()); + + if (extraProps != null) { + analyticsProps.putAll(extraProps); + } + + return sessionUserService + .getCurrentUser() + .flatMap(user -> + analyticsService.sendEvent(analyticsEvents.getEventName(), user.getUsername(), analyticsProps)); + } }