Skip to content

Commit

Permalink
chore: fetch remote reference in local (#38009)
Browse files Browse the repository at this point in the history
## Description
- Added fetch remote CGS Impl

Fixes #
> [!WARNING]  
> _If no issue exists, please create an issue first, and check with the
maintainers if the issue is valid._

## Automation

/ok-to-test tags="@tag.Git"

### 🔍 Cypress test results
<!-- This is an auto-generated comment: Cypress test results  -->
> [!WARNING]
> Tests have not run on the HEAD
d964d59 yet
> <hr>Mon, 09 Dec 2024 07:03:35 UTC
<!-- end of auto-generated comment: Cypress test results  -->


## Communication
Should the DevRel and Marketing teams inform users about this change?
- [ ] Yes
- [ ] No


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

## Release Notes

- **New Features**
- Introduced new methods for fetching remote changes in various Git
services, enhancing synchronization capabilities.
	- Added new fields for internal metadata management.
- Implemented user session management improvements for better handling
of user-related data.
- Added analytics tracking for unit execution time to improve
performance insights.

- **Bug Fixes**
	- Enhanced error handling in new fetch methods to ensure robustness.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
  • Loading branch information
sondermanish authored Dec 9, 2024
1 parent 59833b0 commit 49bed91
Show file tree
Hide file tree
Showing 8 changed files with 185 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.appsmith.external.models.AppsmithDomain;
import com.appsmith.external.views.Views;
import com.appsmith.server.constants.ce.RefType;
import com.appsmith.server.domains.AutoCommitConfig;
import com.appsmith.server.domains.GitAuth;
import com.appsmith.server.domains.GitProfile;
Expand All @@ -24,6 +25,16 @@ public class GitArtifactMetadataCE implements AppsmithDomain {
@JsonView(Views.Public.class)
String branchName;

// TODO: make this public view and remove transient annotation once implmentation completes
@Transient
@JsonView(Views.Internal.class)
String refName;

// TODO: make this public view and remove transient annotation once implementation completes
@Transient
@JsonView(Views.Internal.class)
RefType refType;

// Git default branch corresponding to the remote git repo to which the application is connected to
@JsonView(Views.Public.class)
String defaultBranchName;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.appsmith.git.dto.CommitDTO;
import com.appsmith.server.constants.ArtifactType;
import com.appsmith.server.constants.ce.RefType;
import com.appsmith.server.domains.Artifact;
import com.appsmith.server.dtos.ArtifactImportDTO;
import com.appsmith.server.dtos.GitConnectDTO;
Expand All @@ -23,4 +24,11 @@ Mono<String> commitArtifact(
CommitDTO commitDTO, String branchedArtifactId, ArtifactType artifactType, GitType gitType);

Mono<? extends Artifact> detachRemote(String branchedArtifactId, ArtifactType artifactType, GitType gitType);

Mono<String> fetchRemoteChanges(
String referenceArtifactId,
boolean isFileLock,
ArtifactType artifactType,
GitType gitType,
RefType refType);
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,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.services.SessionUserService;
import com.appsmith.server.services.UserDataService;
import com.appsmith.server.services.WorkspaceService;
import com.appsmith.server.solutions.DatasourcePermission;
Expand All @@ -26,6 +27,7 @@ public CentralGitServiceCECompatibleImpl(
GitProfileUtils gitProfileUtils,
GitAnalyticsUtils gitAnalyticsUtils,
UserDataService userDataService,
SessionUserService sessionUserService,
GitArtifactHelperResolver gitArtifactHelperResolver,
GitHandlingServiceResolver gitHandlingServiceResolver,
GitPrivateRepoHelper gitPrivateRepoHelper,
Expand All @@ -41,6 +43,7 @@ public CentralGitServiceCECompatibleImpl(
gitProfileUtils,
gitAnalyticsUtils,
userDataService,
sessionUserService,
gitArtifactHelperResolver,
gitHandlingServiceResolver,
gitPrivateRepoHelper,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import com.appsmith.server.domains.GitAuth;
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.ArtifactExchangeJson;
Expand All @@ -36,6 +37,7 @@
import com.appsmith.server.imports.internal.ImportService;
import com.appsmith.server.plugins.base.PluginService;
import com.appsmith.server.services.GitArtifactHelper;
import com.appsmith.server.services.SessionUserService;
import com.appsmith.server.services.UserDataService;
import com.appsmith.server.services.WorkspaceService;
import com.appsmith.server.solutions.DatasourcePermission;
Expand All @@ -44,6 +46,7 @@
import lombok.extern.slf4j.Slf4j;
import org.eclipse.jgit.api.errors.InvalidRemoteException;
import org.eclipse.jgit.api.errors.TransportException;
import org.eclipse.jgit.lib.BranchTrackingStatus;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
Expand All @@ -65,6 +68,7 @@
import static com.appsmith.external.git.constants.ce.GitConstantsCE.GIT_PROFILE_ERROR;
import static com.appsmith.external.git.constants.ce.GitSpanCE.OPS_COMMIT;
import static com.appsmith.external.helpers.AppsmithBeanUtils.copyNestedNonNullProperties;
import static com.appsmith.server.constants.FieldName.BRANCH_NAME;
import static com.appsmith.server.constants.FieldName.DEFAULT;
import static com.appsmith.server.constants.SerialiseArtifactObjective.VERSION_CONTROL;
import static java.lang.Boolean.FALSE;
Expand All @@ -79,6 +83,7 @@ public class CentralGitServiceCEImpl implements CentralGitServiceCE {
private final GitProfileUtils gitProfileUtils;
private final GitAnalyticsUtils gitAnalyticsUtils;
private final UserDataService userDataService;
private final SessionUserService sessionUserService;

protected final GitArtifactHelperResolver gitArtifactHelperResolver;
protected final GitHandlingServiceResolver gitHandlingServiceResolver;
Expand Down Expand Up @@ -940,6 +945,108 @@ private boolean isBaseGitMetadataInvalid(GitArtifactMetadata gitArtifactMetadata
.isGitAuthInvalid(gitArtifactMetadata.getGitAuth());
}

public Mono<String> fetchRemoteChanges(
Artifact baseArtifact, Artifact refArtifact, boolean isFileLock, GitType gitType, RefType refType) {

if (refArtifact == null
|| baseArtifact == null
|| isBaseGitMetadataInvalid(baseArtifact.getGitArtifactMetadata(), gitType)) {
return Mono.error(new AppsmithException(AppsmithError.GIT_GENERIC_ERROR));
}

GitArtifactMetadata baseArtifactGitData = baseArtifact.getGitArtifactMetadata();
GitArtifactMetadata refArtifactGitData = refArtifact.getGitArtifactMetadata();

String baseArtifactId = baseArtifactGitData.getDefaultArtifactId();

// TODO add gitType in all error messages.
if (refArtifactGitData == null || !hasText(refArtifactGitData.getRefName())) {
return Mono.error(new AppsmithException(AppsmithError.INVALID_PARAMETER, BRANCH_NAME));
}

Mono<User> currUserMono = sessionUserService.getCurrentUser().cache(); // will be used to send analytics event
Mono<Boolean> acquireGitLockMono =
gitRedisUtils.acquireGitLock(baseArtifactId, GitConstants.GitCommandConstants.FETCH_REMOTE, isFileLock);

ArtifactJsonTransformationDTO jsonTransformationDTO = new ArtifactJsonTransformationDTO();
jsonTransformationDTO.setWorkspaceId(baseArtifact.getWorkspaceId());
jsonTransformationDTO.setBaseArtifactId(baseArtifactGitData.getDefaultArtifactId());
jsonTransformationDTO.setRepoName(baseArtifactGitData.getRepoName());
jsonTransformationDTO.setArtifactType(baseArtifact.getArtifactType());
jsonTransformationDTO.setRefName(refArtifactGitData.getRefName());
jsonTransformationDTO.setRefType(refType);

GitHandlingService gitHandlingService = gitHandlingServiceResolver.getGitHandlingService(gitType);

// current user mono has been zipped just to run in parallel.
Mono<String> fetchRemoteMono = acquireGitLockMono
.then(Mono.defer(() ->
gitHandlingService.fetchRemoteChanges(jsonTransformationDTO, baseArtifactGitData.getGitAuth())))
.flatMap(fetchedRemoteStatusString -> {
return gitRedisUtils.releaseFileLock(baseArtifactId).thenReturn(fetchedRemoteStatusString);
})
.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: {}, git type {}",
baseArtifactId,
refArtifactGitData.getRefName(),
gitType,
throwable);
return Mono.error(
new AppsmithException(AppsmithError.GIT_ACTION_FAILED, "fetch", throwable.getMessage()));
})
.elapsed()
.zipWith(currUserMono)
.flatMap(objects -> {
Long elapsedTime = objects.getT1().getT1();
String fetchRemote = objects.getT1().getT2();
User currentUser = objects.getT2();
return gitAnalyticsUtils
.sendUnitExecutionTimeAnalyticsEvent(
AnalyticsEvents.GIT_FETCH.getEventName(), elapsedTime, currentUser, refArtifact)
.thenReturn(fetchRemote);
})
.name(GitSpan.OPS_FETCH_REMOTE)
.tap(Micrometer.observation(observationRegistry));

return Mono.create(sink -> {
fetchRemoteMono.subscribe(sink::success, sink::error, null, sink.currentContext());
});
}

/**
* 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 refArtifactId id of the reference
* @param isFileLock whether to add file lock or not
* @param artifactType
* @return Mono of {@link BranchTrackingStatus}
*/
@Override
public Mono<String> fetchRemoteChanges(
String refArtifactId, boolean isFileLock, ArtifactType artifactType, GitType gitType, RefType refType) {
GitArtifactHelper<?> artifactGitHelper = gitArtifactHelperResolver.getArtifactHelper(artifactType);
AclPermission artifactEditPermission = artifactGitHelper.getArtifactEditPermission();

Mono<Tuple2<? extends Artifact, ? extends Artifact>> baseAndBranchedArtifactMono =
getBaseAndBranchedArtifacts(refArtifactId, artifactType, artifactEditPermission);

return baseAndBranchedArtifactMono.flatMap(artifactTuples -> {
Artifact baseArtifact = artifactTuples.getT1();
Artifact refArtifact = artifactTuples.getT2();

return fetchRemoteChanges(baseArtifact, refArtifact, isFileLock, gitType, refType);
});
}

/**
* Returns baseArtifact and branchedArtifact
* This operation is quite frequently used, hence providing the right set
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,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.services.SessionUserService;
import com.appsmith.server.services.UserDataService;
import com.appsmith.server.services.WorkspaceService;
import com.appsmith.server.solutions.DatasourcePermission;
Expand All @@ -25,6 +26,7 @@ public CentralGitServiceImpl(
GitProfileUtils gitProfileUtils,
GitAnalyticsUtils gitAnalyticsUtils,
UserDataService userDataService,
SessionUserService sessionUserService,
GitArtifactHelperResolver gitArtifactHelperResolver,
GitHandlingServiceResolver gitHandlingServiceResolver,
GitPrivateRepoHelper gitPrivateRepoHelper,
Expand All @@ -40,6 +42,7 @@ public CentralGitServiceImpl(
gitProfileUtils,
gitAnalyticsUtils,
userDataService,
sessionUserService,
gitArtifactHelperResolver,
gitHandlingServiceResolver,
gitPrivateRepoHelper,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,6 @@ Mono<Boolean> prepareChangesToBeCommitted(

Mono<Tuple2<? extends Artifact, String>> commitArtifact(
Artifact branchedArtifact, CommitDTO commitDTO, ArtifactJsonTransformationDTO jsonTransformationDTO);

Mono<String> fetchRemoteChanges(ArtifactJsonTransformationDTO jsonTransformationDTO, GitAuth gitAuth);
}
Original file line number Diff line number Diff line change
Expand Up @@ -568,4 +568,31 @@ private Mono<String> pushArtifactErrorRecovery(String pushResult, Artifact artif
}
return Mono.just(pushResult);
}

/**
* File system implementation of fetching remote changes. equivalent to git fetch <ref-name>
* @param jsonTransformationDTO : DTO to create path and other ref related details
* @param gitAuth : authentication holder
* @return : returns string for remote fetch
*/
@Override
public Mono<String> fetchRemoteChanges(ArtifactJsonTransformationDTO jsonTransformationDTO, GitAuth gitAuth) {

String workspaceId = jsonTransformationDTO.getWorkspaceId();
String baseArtifactId = jsonTransformationDTO.getBaseArtifactId();
String repoName = jsonTransformationDTO.getRepoName();
String refName = jsonTransformationDTO.getRefName();

ArtifactType artifactType = jsonTransformationDTO.getArtifactType();
GitArtifactHelper<?> gitArtifactHelper = gitArtifactHelperResolver.getArtifactHelper(artifactType);
Path repoSuffix = gitArtifactHelper.getRepoSuffixPath(workspaceId, baseArtifactId, repoName);

Path repoPath = fsGitHandler.createRepoPath(repoSuffix);
Mono<Boolean> checkoutBranchMono = fsGitHandler.checkoutToBranch(repoSuffix, refName);

Mono<String> fetchRemoteMono = fsGitHandler.fetchRemote(
repoPath, gitAuth.getPublicKey(), gitAuth.getPrivateKey(), true, refName, false);

return checkoutBranchMono.then(Mono.defer(() -> fetchRemoteMono));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.appsmith.server.domains.ApplicationMode;
import com.appsmith.server.domains.Artifact;
import com.appsmith.server.domains.GitArtifactMetadata;
import com.appsmith.server.domains.User;
import com.appsmith.server.helpers.GitUtils;
import com.appsmith.server.services.AnalyticsService;
import com.appsmith.server.services.SessionUserService;
Expand Down Expand Up @@ -122,4 +123,27 @@ public Mono<? extends Artifact> addAnalyticsForGitOperation(
.sendEvent(event.getEventName(), user.getUsername(), analyticsProps)
.thenReturn(artifact));
}

public Mono<Void> sendUnitExecutionTimeAnalyticsEvent(
String flowName, Long elapsedTime, User currentUser, Artifact artifact) {
GitArtifactMetadata gitArtifactMetadata = artifact.getGitArtifactMetadata();

final Map<String, Object> data = Map.of(
FieldName.FLOW_NAME,
flowName,
FieldName.APPLICATION_ID,
gitArtifactMetadata.getDefaultArtifactId(),
"appId",
gitArtifactMetadata.getDefaultArtifactId(),
FieldName.BRANCH_NAME,
gitArtifactMetadata.getBranchName(),
"organizationId",
artifact.getWorkspaceId(),
"repoUrl",
gitArtifactMetadata.getRemoteUrl(),
"executionTime",
elapsedTime);
return analyticsService.sendEvent(
AnalyticsEvents.UNIT_EXECUTION_TIME.getEventName(), currentUser.getUsername(), data);
}
}

0 comments on commit 49bed91

Please sign in to comment.