chore: Added changes for splitting git related files for mi (#30202)
This commit is contained in:
parent
d12b8ff820
commit
861c6e0fa5
|
|
@ -1,9 +1,5 @@
|
||||||
package com.appsmith.git.constants;
|
package com.appsmith.git.constants;
|
||||||
|
|
||||||
public interface GitDirectories {
|
import com.appsmith.git.constants.ce.GitDirectoriesCE;
|
||||||
String PAGE_DIRECTORY = "pages";
|
|
||||||
String ACTION_DIRECTORY = "queries";
|
public interface GitDirectories extends GitDirectoriesCE {}
|
||||||
String ACTION_COLLECTION_DIRECTORY = "jsobjects";
|
|
||||||
String DATASOURCE_DIRECTORY = "datasources";
|
|
||||||
String JS_LIB_DIRECTORY = "jslibs";
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
package com.appsmith.git.constants.ce;
|
||||||
|
|
||||||
|
public interface GitDirectoriesCE {
|
||||||
|
String PAGE_DIRECTORY = "pages";
|
||||||
|
String ACTION_DIRECTORY = "queries";
|
||||||
|
String ACTION_COLLECTION_DIRECTORY = "jsobjects";
|
||||||
|
String DATASOURCE_DIRECTORY = "datasources";
|
||||||
|
String JS_LIB_DIRECTORY = "jslibs";
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -1,913 +1,16 @@
|
||||||
package com.appsmith.git.service;
|
package com.appsmith.git.service;
|
||||||
|
|
||||||
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.GitExecutor;
|
||||||
import com.appsmith.external.helpers.Stopwatch;
|
|
||||||
import com.appsmith.git.configurations.GitServiceConfig;
|
import com.appsmith.git.configurations.GitServiceConfig;
|
||||||
import com.appsmith.git.constants.AppsmithBotAsset;
|
import com.appsmith.git.service.ce.GitExecutorCEImpl;
|
||||||
import com.appsmith.git.constants.CommonConstants;
|
|
||||||
import com.appsmith.git.constants.Constraint;
|
|
||||||
import com.appsmith.git.constants.GitDirectories;
|
|
||||||
import com.appsmith.git.helpers.RepositoryHelper;
|
|
||||||
import com.appsmith.git.helpers.SshTransportConfigCallback;
|
|
||||||
import com.appsmith.git.helpers.StopwatchHelpers;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.eclipse.jgit.api.CreateBranchCommand;
|
|
||||||
import org.eclipse.jgit.api.Git;
|
|
||||||
import org.eclipse.jgit.api.ListBranchCommand;
|
|
||||||
import org.eclipse.jgit.api.MergeCommand;
|
|
||||||
import org.eclipse.jgit.api.MergeResult;
|
|
||||||
import org.eclipse.jgit.api.RebaseCommand;
|
|
||||||
import org.eclipse.jgit.api.RebaseResult;
|
|
||||||
import org.eclipse.jgit.api.ResetCommand;
|
|
||||||
import org.eclipse.jgit.api.Status;
|
|
||||||
import org.eclipse.jgit.api.TransportConfigCallback;
|
|
||||||
import org.eclipse.jgit.api.errors.CheckoutConflictException;
|
|
||||||
import org.eclipse.jgit.api.errors.GitAPIException;
|
|
||||||
import org.eclipse.jgit.lib.BranchTrackingStatus;
|
|
||||||
import org.eclipse.jgit.lib.PersonIdent;
|
|
||||||
import org.eclipse.jgit.lib.Ref;
|
|
||||||
import org.eclipse.jgit.lib.StoredConfig;
|
|
||||||
import org.eclipse.jgit.merge.MergeStrategy;
|
|
||||||
import org.eclipse.jgit.revwalk.RevCommit;
|
|
||||||
import org.eclipse.jgit.transport.RefSpec;
|
|
||||||
import org.eclipse.jgit.util.StringUtils;
|
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.util.FileSystemUtils;
|
|
||||||
import reactor.core.publisher.Mono;
|
|
||||||
import reactor.core.scheduler.Scheduler;
|
|
||||||
import reactor.core.scheduler.Schedulers;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.nio.file.Paths;
|
|
||||||
import java.time.Duration;
|
|
||||||
import java.time.ZoneId;
|
|
||||||
import java.time.ZoneOffset;
|
|
||||||
import java.time.format.DateTimeFormatter;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import static com.appsmith.git.constants.CommonConstants.FILE_MIGRATION_MESSAGE;
|
|
||||||
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
@Component
|
@Component
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class GitExecutorImpl implements GitExecutor {
|
public class GitExecutorImpl extends GitExecutorCEImpl implements GitExecutor {
|
||||||
|
|
||||||
private final RepositoryHelper repositoryHelper = new RepositoryHelper();
|
public GitExecutorImpl(GitServiceConfig gitServiceConfig) {
|
||||||
|
super(gitServiceConfig);
|
||||||
private final GitServiceConfig gitServiceConfig;
|
|
||||||
|
|
||||||
public static final DateTimeFormatter ISO_FORMATTER =
|
|
||||||
DateTimeFormatter.ISO_INSTANT.withZone(ZoneId.from(ZoneOffset.UTC));
|
|
||||||
|
|
||||||
private final Scheduler scheduler = Schedulers.boundedElastic();
|
|
||||||
|
|
||||||
private static final String SUCCESS_MERGE_STATUS = "This branch has no conflicts with the base branch.";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method will handle the git-commit functionality. Under the hood it checks if the repo has already been
|
|
||||||
* initialised and will be initialised if git repo is not present
|
|
||||||
* @param path parent path to repo
|
|
||||||
* @param commitMessage message which will be registered for this commit
|
|
||||||
* @param authorName author details
|
|
||||||
* @param authorEmail author details
|
|
||||||
* @param doAmend To amend with the previous commit
|
|
||||||
* @return if the commit was successful
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public Mono<String> commitApplication(
|
|
||||||
Path path,
|
|
||||||
String commitMessage,
|
|
||||||
String authorName,
|
|
||||||
String authorEmail,
|
|
||||||
boolean isSuffixedPath,
|
|
||||||
boolean doAmend) {
|
|
||||||
|
|
||||||
final String finalAuthorName =
|
|
||||||
StringUtils.isEmptyOrNull(authorName) ? AppsmithBotAsset.APPSMITH_BOT_USERNAME : authorName;
|
|
||||||
final String finalAuthorEmail =
|
|
||||||
StringUtils.isEmptyOrNull(authorEmail) ? AppsmithBotAsset.APPSMITH_BOT_EMAIL : authorEmail;
|
|
||||||
return Mono.fromCallable(() -> {
|
|
||||||
log.debug("Trying to commit to local repo path, {}", path);
|
|
||||||
Path repoPath = path;
|
|
||||||
if (Boolean.TRUE.equals(isSuffixedPath)) {
|
|
||||||
repoPath = createRepoPath(repoPath);
|
|
||||||
}
|
|
||||||
Stopwatch processStopwatch =
|
|
||||||
StopwatchHelpers.startStopwatch(repoPath, AnalyticsEvents.GIT_COMMIT.getEventName());
|
|
||||||
// Just need to open a repository here and make a commit
|
|
||||||
try (Git git = Git.open(repoPath.toFile())) {
|
|
||||||
// Stage all the files added and modified
|
|
||||||
git.add().addFilepattern(".").call();
|
|
||||||
// Stage modified and deleted files
|
|
||||||
git.add().setUpdate(true).addFilepattern(".").call();
|
|
||||||
|
|
||||||
// Commit the changes
|
|
||||||
git.commit()
|
|
||||||
.setMessage(commitMessage)
|
|
||||||
// Only make a commit if there are any updates
|
|
||||||
.setAllowEmpty(false)
|
|
||||||
.setAuthor(finalAuthorName, finalAuthorEmail)
|
|
||||||
.setCommitter(finalAuthorName, finalAuthorEmail)
|
|
||||||
.setAmend(doAmend)
|
|
||||||
.call();
|
|
||||||
processStopwatch.stopAndLogTimeInMillis();
|
|
||||||
return "Committed successfully!";
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.timeout(Duration.ofMillis(Constraint.TIMEOUT_MILLIS))
|
|
||||||
.subscribeOn(scheduler);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Method to create a new repository to provided path
|
|
||||||
* @param repoPath path where new repo needs to be created
|
|
||||||
* @return if the operation was successful
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public boolean createNewRepository(Path repoPath) throws GitAPIException {
|
|
||||||
// create new repo to the mentioned path
|
|
||||||
log.debug("Trying to create new repository: {}", repoPath);
|
|
||||||
Git.init().setDirectory(repoPath.toFile()).call();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Method to get the commit history
|
|
||||||
* @param repoSuffix Path used to generate the repo url specific to the application for which the commit history is requested
|
|
||||||
* @return list of git commits
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public Mono<List<GitLogDTO>> getCommitHistory(Path repoSuffix) {
|
|
||||||
return Mono.fromCallable(() -> {
|
|
||||||
log.debug(Thread.currentThread().getName() + ": get commit history for " + repoSuffix);
|
|
||||||
List<GitLogDTO> commitLogs = new ArrayList<>();
|
|
||||||
Path repoPath = createRepoPath(repoSuffix);
|
|
||||||
Stopwatch processStopwatch = StopwatchHelpers.startStopwatch(
|
|
||||||
repoPath, AnalyticsEvents.GIT_COMMIT_HISTORY.getEventName());
|
|
||||||
try (Git git = Git.open(repoPath.toFile())) {
|
|
||||||
Iterable<RevCommit> gitLogs = git.log()
|
|
||||||
.setMaxCount(Constraint.MAX_COMMIT_LOGS)
|
|
||||||
.call();
|
|
||||||
gitLogs.forEach(revCommit -> {
|
|
||||||
PersonIdent author = revCommit.getAuthorIdent();
|
|
||||||
GitLogDTO gitLog = new GitLogDTO(
|
|
||||||
revCommit.getName(),
|
|
||||||
author.getName(),
|
|
||||||
author.getEmailAddress(),
|
|
||||||
revCommit.getFullMessage(),
|
|
||||||
ISO_FORMATTER.format(new Date(revCommit.getCommitTime() * 1000L).toInstant()));
|
|
||||||
processStopwatch.stopAndLogTimeInMillis();
|
|
||||||
commitLogs.add(gitLog);
|
|
||||||
});
|
|
||||||
return commitLogs;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.timeout(Duration.ofMillis(Constraint.TIMEOUT_MILLIS))
|
|
||||||
.subscribeOn(scheduler);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Path createRepoPath(Path suffix) {
|
|
||||||
return Paths.get(gitServiceConfig.getGitRootPath()).resolve(suffix);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Method to push changes to remote repo
|
|
||||||
* @param repoSuffix Path used to generate the repo url specific to the application which needs to be pushed to remote
|
|
||||||
* @param remoteUrl remote repo url
|
|
||||||
* @param publicKey
|
|
||||||
* @param privateKey
|
|
||||||
* @return Success message
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public Mono<String> pushApplication(
|
|
||||||
Path repoSuffix, String remoteUrl, String publicKey, String privateKey, String branchName) {
|
|
||||||
// We can safely assume that repo has been already initialised either in commit or clone flow and can directly
|
|
||||||
// open the repo
|
|
||||||
return Mono.fromCallable(() -> {
|
|
||||||
log.debug(Thread.currentThread().getName() + ": pushing changes to remote " + remoteUrl);
|
|
||||||
// open the repo
|
|
||||||
Path baseRepoPath = createRepoPath(repoSuffix);
|
|
||||||
Stopwatch processStopwatch =
|
|
||||||
StopwatchHelpers.startStopwatch(baseRepoPath, AnalyticsEvents.GIT_PUSH.getEventName());
|
|
||||||
try (Git git = Git.open(baseRepoPath.toFile())) {
|
|
||||||
TransportConfigCallback transportConfigCallback =
|
|
||||||
new SshTransportConfigCallback(privateKey, publicKey);
|
|
||||||
|
|
||||||
StringBuilder result = new StringBuilder("Pushed successfully with status : ");
|
|
||||||
git.push()
|
|
||||||
.setTransportConfigCallback(transportConfigCallback)
|
|
||||||
.setRemote(remoteUrl)
|
|
||||||
.call()
|
|
||||||
.forEach(pushResult -> pushResult
|
|
||||||
.getRemoteUpdates()
|
|
||||||
.forEach(remoteRefUpdate -> {
|
|
||||||
result.append(remoteRefUpdate.getStatus())
|
|
||||||
.append(",");
|
|
||||||
if (!StringUtils.isEmptyOrNull(remoteRefUpdate.getMessage())) {
|
|
||||||
result.append(remoteRefUpdate.getMessage())
|
|
||||||
.append(",");
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
// We can support username and password in future if needed
|
|
||||||
// pushCommand.setCredentialsProvider(new UsernamePasswordCredentialsProvider("username",
|
|
||||||
// "password"));
|
|
||||||
processStopwatch.stopAndLogTimeInMillis();
|
|
||||||
return result.substring(0, result.length() - 1);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.timeout(Duration.ofMillis(Constraint.TIMEOUT_MILLIS))
|
|
||||||
.subscribeOn(scheduler);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Clone the repo to the file path : container-volume/orgId/defaultAppId/repo/applicationData
|
|
||||||
*
|
|
||||||
* @param repoSuffix combination of orgId, defaultId and repoName
|
|
||||||
* @param remoteUrl ssh url of the git repo(we support cloning via ssh url only with deploy key)
|
|
||||||
* @param privateKey generated by us and specific to the defaultApplication
|
|
||||||
* @param publicKey generated by us and specific to the defaultApplication
|
|
||||||
* @return defaultBranchName of the repo
|
|
||||||
* */
|
|
||||||
@Override
|
|
||||||
public Mono<String> cloneApplication(Path repoSuffix, String remoteUrl, String privateKey, String publicKey) {
|
|
||||||
|
|
||||||
Stopwatch processStopwatch =
|
|
||||||
StopwatchHelpers.startStopwatch(repoSuffix, AnalyticsEvents.GIT_CLONE.getEventName());
|
|
||||||
return Mono.fromCallable(() -> {
|
|
||||||
log.debug(Thread.currentThread().getName() + ": Cloning the repo from the remote " + remoteUrl);
|
|
||||||
final TransportConfigCallback transportConfigCallback =
|
|
||||||
new SshTransportConfigCallback(privateKey, publicKey);
|
|
||||||
File file = Paths.get(gitServiceConfig.getGitRootPath())
|
|
||||||
.resolve(repoSuffix)
|
|
||||||
.toFile();
|
|
||||||
while (file.exists()) {
|
|
||||||
FileSystemUtils.deleteRecursively(file);
|
|
||||||
}
|
|
||||||
|
|
||||||
Git git = Git.cloneRepository()
|
|
||||||
.setURI(remoteUrl)
|
|
||||||
.setTransportConfigCallback(transportConfigCallback)
|
|
||||||
.setDirectory(file)
|
|
||||||
.call();
|
|
||||||
String branchName = git.getRepository().getBranch();
|
|
||||||
|
|
||||||
repositoryHelper.updateRemoteBranchTrackingConfig(branchName, git);
|
|
||||||
git.close();
|
|
||||||
processStopwatch.stopAndLogTimeInMillis();
|
|
||||||
return branchName;
|
|
||||||
})
|
|
||||||
.timeout(Duration.ofMillis(Constraint.TIMEOUT_MILLIS))
|
|
||||||
.subscribeOn(scheduler);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Mono<String> createAndCheckoutToBranch(Path repoSuffix, String branchName) {
|
|
||||||
// We can safely assume that repo has been already initialised either in commit or clone flow and can directly
|
|
||||||
// open the repo
|
|
||||||
Stopwatch processStopwatch =
|
|
||||||
StopwatchHelpers.startStopwatch(repoSuffix, AnalyticsEvents.GIT_CREATE_BRANCH.getEventName());
|
|
||||||
return Mono.fromCallable(() -> {
|
|
||||||
log.debug(Thread.currentThread().getName() + ": Creating branch " + branchName + "for the repo "
|
|
||||||
+ repoSuffix);
|
|
||||||
// open the repo
|
|
||||||
Path baseRepoPath = createRepoPath(repoSuffix);
|
|
||||||
try (Git git = Git.open(baseRepoPath.toFile())) {
|
|
||||||
// Create and checkout to new branch
|
|
||||||
git.checkout()
|
|
||||||
.setCreateBranch(Boolean.TRUE)
|
|
||||||
.setName(branchName)
|
|
||||||
.setUpstreamMode(CreateBranchCommand.SetupUpstreamMode.TRACK)
|
|
||||||
.call();
|
|
||||||
|
|
||||||
repositoryHelper.updateRemoteBranchTrackingConfig(branchName, git);
|
|
||||||
processStopwatch.stopAndLogTimeInMillis();
|
|
||||||
return git.getRepository().getBranch();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.timeout(Duration.ofMillis(Constraint.TIMEOUT_MILLIS))
|
|
||||||
.subscribeOn(scheduler);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Mono<Boolean> deleteBranch(Path repoSuffix, String branchName) {
|
|
||||||
// We can safely assume that repo has been already initialised either in commit or clone flow and can directly
|
|
||||||
// open the repo
|
|
||||||
Stopwatch processStopwatch =
|
|
||||||
StopwatchHelpers.startStopwatch(repoSuffix, AnalyticsEvents.GIT_DELETE_BRANCH.getEventName());
|
|
||||||
return Mono.fromCallable(() -> {
|
|
||||||
log.debug(Thread.currentThread().getName() + ": Deleting branch " + branchName + "for the repo "
|
|
||||||
+ repoSuffix);
|
|
||||||
// open the repo
|
|
||||||
Path baseRepoPath = createRepoPath(repoSuffix);
|
|
||||||
try (Git git = Git.open(baseRepoPath.toFile())) {
|
|
||||||
// Create and checkout to new branch
|
|
||||||
List<String> deleteBranchList = git.branchDelete()
|
|
||||||
.setBranchNames(branchName)
|
|
||||||
.setForce(Boolean.TRUE)
|
|
||||||
.call();
|
|
||||||
processStopwatch.stopAndLogTimeInMillis();
|
|
||||||
if (deleteBranchList.isEmpty()) {
|
|
||||||
return Boolean.FALSE;
|
|
||||||
}
|
|
||||||
return Boolean.TRUE;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.timeout(Duration.ofMillis(Constraint.TIMEOUT_MILLIS))
|
|
||||||
.subscribeOn(scheduler);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Mono<Boolean> checkoutToBranch(Path repoSuffix, String branchName) {
|
|
||||||
|
|
||||||
Stopwatch processStopwatch =
|
|
||||||
StopwatchHelpers.startStopwatch(repoSuffix, AnalyticsEvents.GIT_CHECKOUT.getEventName());
|
|
||||||
return Mono.fromCallable(() -> {
|
|
||||||
log.debug(Thread.currentThread().getName() + ": Switching to the branch " + branchName);
|
|
||||||
// We can safely assume that repo has been already initialised either in commit or clone flow and
|
|
||||||
// can directly
|
|
||||||
// open the repo
|
|
||||||
Path baseRepoPath = createRepoPath(repoSuffix);
|
|
||||||
try (Git git = Git.open(baseRepoPath.toFile())) {
|
|
||||||
if (StringUtils.equalsIgnoreCase(
|
|
||||||
branchName, git.getRepository().getBranch())) {
|
|
||||||
return Boolean.TRUE;
|
|
||||||
}
|
|
||||||
// Create and checkout to new branch
|
|
||||||
String checkedOutBranch = git.checkout()
|
|
||||||
.setCreateBranch(Boolean.FALSE)
|
|
||||||
.setName(branchName)
|
|
||||||
.setUpstreamMode(CreateBranchCommand.SetupUpstreamMode.SET_UPSTREAM)
|
|
||||||
.call()
|
|
||||||
.getName();
|
|
||||||
processStopwatch.stopAndLogTimeInMillis();
|
|
||||||
return StringUtils.equalsIgnoreCase(checkedOutBranch, "refs/heads/" + branchName);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new Exception(e);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.timeout(Duration.ofMillis(Constraint.TIMEOUT_MILLIS))
|
|
||||||
.subscribeOn(scheduler);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Mono<MergeStatusDTO> pullApplication(
|
|
||||||
Path repoSuffix, String remoteUrl, String branchName, String privateKey, String publicKey)
|
|
||||||
throws IOException {
|
|
||||||
|
|
||||||
Stopwatch processStopwatch =
|
|
||||||
StopwatchHelpers.startStopwatch(repoSuffix, AnalyticsEvents.GIT_PULL.getEventName());
|
|
||||||
TransportConfigCallback transportConfigCallback = new SshTransportConfigCallback(privateKey, publicKey);
|
|
||||||
|
|
||||||
try (Git git = Git.open(createRepoPath(repoSuffix).toFile())) {
|
|
||||||
return Mono.fromCallable(() -> {
|
|
||||||
log.debug(Thread.currentThread().getName() + ": Pull changes from remote " + remoteUrl
|
|
||||||
+ " for the branch " + branchName);
|
|
||||||
// checkout the branch on which the merge command is run
|
|
||||||
git.checkout()
|
|
||||||
.setName(branchName)
|
|
||||||
.setCreateBranch(false)
|
|
||||||
.call();
|
|
||||||
MergeResult mergeResult = git.pull()
|
|
||||||
.setRemoteBranchName(branchName)
|
|
||||||
.setTransportConfigCallback(transportConfigCallback)
|
|
||||||
.setFastForward(MergeCommand.FastForwardMode.FF)
|
|
||||||
.call()
|
|
||||||
.getMergeResult();
|
|
||||||
MergeStatusDTO mergeStatus = new MergeStatusDTO();
|
|
||||||
Long count =
|
|
||||||
Arrays.stream(mergeResult.getMergedCommits()).count();
|
|
||||||
if (mergeResult.getMergeStatus().isSuccessful()) {
|
|
||||||
mergeStatus.setMergeAble(true);
|
|
||||||
mergeStatus.setStatus(count + " commits merged from origin/" + branchName);
|
|
||||||
} else {
|
|
||||||
// If there are conflicts add the conflicting file names to the response structure
|
|
||||||
mergeStatus.setMergeAble(false);
|
|
||||||
List<String> mergeConflictFiles = new ArrayList<>();
|
|
||||||
if (!Optional.ofNullable(mergeResult.getConflicts()).isEmpty()) {
|
|
||||||
mergeConflictFiles.addAll(
|
|
||||||
mergeResult.getConflicts().keySet());
|
|
||||||
}
|
|
||||||
mergeStatus.setConflictingFiles(mergeConflictFiles);
|
|
||||||
// On merge conflicts abort the merge => git merge --abort
|
|
||||||
git.getRepository().writeMergeCommitMsg(null);
|
|
||||||
git.getRepository().writeMergeHeads(null);
|
|
||||||
processStopwatch.stopAndLogTimeInMillis();
|
|
||||||
throw new org.eclipse.jgit.errors.CheckoutConflictException(mergeConflictFiles.toString());
|
|
||||||
}
|
|
||||||
processStopwatch.stopAndLogTimeInMillis();
|
|
||||||
return mergeStatus;
|
|
||||||
})
|
|
||||||
.onErrorResume(error -> {
|
|
||||||
try {
|
|
||||||
return resetToLastCommit(git).flatMap(ignore -> Mono.error(error));
|
|
||||||
} catch (GitAPIException e) {
|
|
||||||
log.error("Error for hard resetting to latest commit {0}", e);
|
|
||||||
return Mono.error(e);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.timeout(Duration.ofMillis(Constraint.TIMEOUT_MILLIS))
|
|
||||||
.subscribeOn(scheduler);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Mono<List<GitBranchDTO>> listBranches(Path repoSuffix) {
|
|
||||||
Path baseRepoPath = createRepoPath(repoSuffix);
|
|
||||||
return Mono.fromCallable(() -> {
|
|
||||||
log.debug(Thread.currentThread().getName() + ": Get branches for the application " + repoSuffix);
|
|
||||||
Git git = Git.open(baseRepoPath.toFile());
|
|
||||||
List<Ref> refList = git.branchList()
|
|
||||||
.setListMode(ListBranchCommand.ListMode.ALL)
|
|
||||||
.call();
|
|
||||||
|
|
||||||
List<GitBranchDTO> branchList = new ArrayList<>();
|
|
||||||
GitBranchDTO gitBranchDTO = new GitBranchDTO();
|
|
||||||
if (refList.isEmpty()) {
|
|
||||||
gitBranchDTO.setBranchName(git.getRepository().getBranch());
|
|
||||||
branchList.add(gitBranchDTO);
|
|
||||||
} else {
|
|
||||||
for (Ref ref : refList) {
|
|
||||||
// if (!ref.getName().equals(defaultBranch)) {
|
|
||||||
gitBranchDTO = new GitBranchDTO();
|
|
||||||
gitBranchDTO.setBranchName(ref.getName()
|
|
||||||
.replace("refs/", "")
|
|
||||||
.replace("heads/", "")
|
|
||||||
.replace("remotes/", ""));
|
|
||||||
branchList.add(gitBranchDTO);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
git.close();
|
|
||||||
return branchList;
|
|
||||||
})
|
|
||||||
.timeout(Duration.ofMillis(Constraint.TIMEOUT_MILLIS))
|
|
||||||
.subscribeOn(scheduler);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Mono<String> getRemoteDefaultBranch(Path repoSuffix, String remoteUrl, String privateKey, String publicKey) {
|
|
||||||
Path baseRepoPath = createRepoPath(repoSuffix);
|
|
||||||
return Mono.fromCallable(() -> {
|
|
||||||
TransportConfigCallback transportConfigCallback =
|
|
||||||
new SshTransportConfigCallback(privateKey, publicKey);
|
|
||||||
Git git = Git.open(baseRepoPath.toFile());
|
|
||||||
|
|
||||||
return git.lsRemote()
|
|
||||||
.setRemote(remoteUrl)
|
|
||||||
.setTransportConfigCallback(transportConfigCallback)
|
|
||||||
.callAsMap()
|
|
||||||
.get("HEAD")
|
|
||||||
.getTarget()
|
|
||||||
.getName()
|
|
||||||
.replace("refs/heads/", "");
|
|
||||||
})
|
|
||||||
.timeout(Duration.ofMillis(Constraint.TIMEOUT_MILLIS))
|
|
||||||
.subscribeOn(scheduler);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method will handle the git-status functionality
|
|
||||||
*
|
|
||||||
* @param repoPath Path to actual repo
|
|
||||||
* @param branchName branch name for which the status is required
|
|
||||||
* @return Map of file names those are modified, conflicted etc.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public Mono<GitStatusDTO> getStatus(Path repoPath, String branchName) {
|
|
||||||
Stopwatch processStopwatch =
|
|
||||||
StopwatchHelpers.startStopwatch(repoPath, AnalyticsEvents.GIT_STATUS.getEventName());
|
|
||||||
return Mono.fromCallable(() -> {
|
|
||||||
try (Git git = Git.open(repoPath.toFile())) {
|
|
||||||
log.debug(Thread.currentThread().getName() + ": Get status for repo " + repoPath + ", branch "
|
|
||||||
+ branchName);
|
|
||||||
Status status = git.status().call();
|
|
||||||
GitStatusDTO response = new GitStatusDTO();
|
|
||||||
Set<String> modifiedAssets = new HashSet<>();
|
|
||||||
modifiedAssets.addAll(status.getModified());
|
|
||||||
modifiedAssets.addAll(status.getAdded());
|
|
||||||
modifiedAssets.addAll(status.getRemoved());
|
|
||||||
modifiedAssets.addAll(status.getUncommittedChanges());
|
|
||||||
modifiedAssets.addAll(status.getUntracked());
|
|
||||||
response.setAdded(status.getAdded());
|
|
||||||
response.setRemoved(status.getRemoved());
|
|
||||||
|
|
||||||
Set<String> queriesModified = new HashSet<>();
|
|
||||||
Set<String> jsObjectsModified = new HashSet<>();
|
|
||||||
Set<String> pagesModified = new HashSet<>();
|
|
||||||
int modifiedPages = 0;
|
|
||||||
int modifiedQueries = 0;
|
|
||||||
int modifiedJSObjects = 0;
|
|
||||||
int modifiedDatasources = 0;
|
|
||||||
int modifiedJSLibs = 0;
|
|
||||||
for (String x : modifiedAssets) {
|
|
||||||
// begins with pages and filename and parent name should be same or contains widgets
|
|
||||||
if (x.contains(CommonConstants.WIDGETS)) {
|
|
||||||
if (!pagesModified.contains(getPageName(x))) {
|
|
||||||
pagesModified.add(getPageName(x));
|
|
||||||
modifiedPages++;
|
|
||||||
}
|
|
||||||
} else if (!x.contains(CommonConstants.WIDGETS)
|
|
||||||
&& x.startsWith(GitDirectories.PAGE_DIRECTORY)
|
|
||||||
&& !x.contains(GitDirectories.ACTION_DIRECTORY)
|
|
||||||
&& !x.contains(GitDirectories.ACTION_COLLECTION_DIRECTORY)) {
|
|
||||||
if (!pagesModified.contains(getPageName(x))) {
|
|
||||||
pagesModified.add(getPageName(x));
|
|
||||||
modifiedPages++;
|
|
||||||
}
|
|
||||||
} else if (x.contains(GitDirectories.ACTION_DIRECTORY + CommonConstants.DELIMITER_PATH)) {
|
|
||||||
String queryName =
|
|
||||||
x.split(GitDirectories.ACTION_DIRECTORY + CommonConstants.DELIMITER_PATH)[1];
|
|
||||||
int position = queryName.indexOf(CommonConstants.DELIMITER_PATH);
|
|
||||||
if (position != -1) {
|
|
||||||
queryName = queryName.substring(0, position);
|
|
||||||
String pageName = x.split(CommonConstants.DELIMITER_PATH)[1];
|
|
||||||
if (!queriesModified.contains(pageName + queryName)) {
|
|
||||||
queriesModified.add(pageName + queryName);
|
|
||||||
modifiedQueries++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (x.contains(
|
|
||||||
GitDirectories.ACTION_COLLECTION_DIRECTORY + CommonConstants.DELIMITER_PATH)
|
|
||||||
&& !x.endsWith(CommonConstants.JSON_EXTENSION)) {
|
|
||||||
String queryName = x.substring(x.lastIndexOf(CommonConstants.DELIMITER_PATH) + 1);
|
|
||||||
String pageName = x.split(CommonConstants.DELIMITER_PATH)[1];
|
|
||||||
if (!jsObjectsModified.contains(pageName + queryName)) {
|
|
||||||
jsObjectsModified.add(pageName + queryName);
|
|
||||||
modifiedJSObjects++;
|
|
||||||
}
|
|
||||||
} else if (x.contains(
|
|
||||||
GitDirectories.DATASOURCE_DIRECTORY + CommonConstants.DELIMITER_PATH)) {
|
|
||||||
modifiedDatasources++;
|
|
||||||
} else if (x.contains(GitDirectories.JS_LIB_DIRECTORY + CommonConstants.DELIMITER_PATH)) {
|
|
||||||
modifiedJSLibs++;
|
|
||||||
// remove this code in future when all the older format js libs are migrated to new
|
|
||||||
// format
|
|
||||||
|
|
||||||
if (x.contains("js.json")) {
|
|
||||||
/*
|
|
||||||
As this updated filename has color(:), it means this is the older format js
|
|
||||||
lib file that we're going to rename with the format without colon.
|
|
||||||
Hence, we need to show a message to user saying this might be a system level change.
|
|
||||||
*/
|
|
||||||
response.setMigrationMessage(FILE_MIGRATION_MESSAGE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
response.setModified(modifiedAssets);
|
|
||||||
response.setConflicting(status.getConflicting());
|
|
||||||
response.setIsClean(status.isClean());
|
|
||||||
response.setModifiedPages(modifiedPages);
|
|
||||||
response.setModifiedQueries(modifiedQueries);
|
|
||||||
response.setModifiedJSObjects(modifiedJSObjects);
|
|
||||||
response.setModifiedDatasources(modifiedDatasources);
|
|
||||||
response.setModifiedJSLibs(modifiedJSLibs);
|
|
||||||
|
|
||||||
BranchTrackingStatus trackingStatus = BranchTrackingStatus.of(git.getRepository(), branchName);
|
|
||||||
if (trackingStatus != null) {
|
|
||||||
response.setAheadCount(trackingStatus.getAheadCount());
|
|
||||||
response.setBehindCount(trackingStatus.getBehindCount());
|
|
||||||
response.setRemoteBranch(trackingStatus.getRemoteTrackingBranch());
|
|
||||||
} else {
|
|
||||||
log.debug(
|
|
||||||
"Remote tracking details not present for branch: {}, repo: {}",
|
|
||||||
branchName,
|
|
||||||
repoPath);
|
|
||||||
response.setAheadCount(0);
|
|
||||||
response.setBehindCount(0);
|
|
||||||
response.setRemoteBranch("untracked");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove modified changes from current branch so that checkout to other branches will be
|
|
||||||
// possible
|
|
||||||
if (!status.isClean()) {
|
|
||||||
return resetToLastCommit(git).map(ref -> {
|
|
||||||
processStopwatch.stopAndLogTimeInMillis();
|
|
||||||
return response;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
processStopwatch.stopAndLogTimeInMillis();
|
|
||||||
return Mono.just(response);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.timeout(Duration.ofMillis(Constraint.TIMEOUT_MILLIS))
|
|
||||||
.flatMap(response -> response)
|
|
||||||
.subscribeOn(scheduler);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getPageName(String path) {
|
|
||||||
String[] pathArray = path.split(CommonConstants.DELIMITER_PATH);
|
|
||||||
return pathArray[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Mono<String> mergeBranch(Path repoSuffix, String sourceBranch, String destinationBranch) {
|
|
||||||
return Mono.fromCallable(() -> {
|
|
||||||
Stopwatch processStopwatch =
|
|
||||||
StopwatchHelpers.startStopwatch(repoSuffix, AnalyticsEvents.GIT_MERGE.getEventName());
|
|
||||||
log.debug(Thread.currentThread().getName() + ": Merge branch " + sourceBranch + " on "
|
|
||||||
+ destinationBranch);
|
|
||||||
try (Git git = Git.open(createRepoPath(repoSuffix).toFile())) {
|
|
||||||
try {
|
|
||||||
// checkout the branch on which the merge command is run
|
|
||||||
git.checkout()
|
|
||||||
.setName(destinationBranch)
|
|
||||||
.setCreateBranch(false)
|
|
||||||
.call();
|
|
||||||
|
|
||||||
MergeResult mergeResult = git.merge()
|
|
||||||
.include(git.getRepository().findRef(sourceBranch))
|
|
||||||
.setStrategy(MergeStrategy.RECURSIVE)
|
|
||||||
.call();
|
|
||||||
processStopwatch.stopAndLogTimeInMillis();
|
|
||||||
return mergeResult.getMergeStatus().name();
|
|
||||||
} catch (GitAPIException e) {
|
|
||||||
// On merge conflicts abort the merge => git merge --abort
|
|
||||||
git.getRepository().writeMergeCommitMsg(null);
|
|
||||||
git.getRepository().writeMergeHeads(null);
|
|
||||||
processStopwatch.stopAndLogTimeInMillis();
|
|
||||||
throw new Exception(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.onErrorResume(error -> {
|
|
||||||
try {
|
|
||||||
return resetToLastCommit(repoSuffix, destinationBranch).thenReturn(error.getMessage());
|
|
||||||
} catch (GitAPIException | IOException e) {
|
|
||||||
log.error("Error while hard resetting to latest commit {0}", e);
|
|
||||||
return Mono.error(e);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.timeout(Duration.ofMillis(Constraint.TIMEOUT_MILLIS))
|
|
||||||
.subscribeOn(scheduler);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Mono<String> fetchRemote(
|
|
||||||
Path repoSuffix,
|
|
||||||
String publicKey,
|
|
||||||
String privateKey,
|
|
||||||
boolean isRepoPath,
|
|
||||||
String branchName,
|
|
||||||
boolean isFetchAll) {
|
|
||||||
Stopwatch processStopwatch =
|
|
||||||
StopwatchHelpers.startStopwatch(repoSuffix, AnalyticsEvents.GIT_FETCH.getEventName());
|
|
||||||
Path repoPath = Boolean.TRUE.equals(isRepoPath) ? repoSuffix : createRepoPath(repoSuffix);
|
|
||||||
return Mono.fromCallable(() -> {
|
|
||||||
TransportConfigCallback config = new SshTransportConfigCallback(privateKey, publicKey);
|
|
||||||
try (Git git = Git.open(repoPath.toFile())) {
|
|
||||||
String fetchMessages;
|
|
||||||
if (Boolean.TRUE.equals(isFetchAll)) {
|
|
||||||
fetchMessages = git.fetch()
|
|
||||||
.setRemoveDeletedRefs(true)
|
|
||||||
.setTransportConfigCallback(config)
|
|
||||||
.call()
|
|
||||||
.getMessages();
|
|
||||||
} else {
|
|
||||||
RefSpec ref =
|
|
||||||
new RefSpec("refs/heads/" + branchName + ":refs/remotes/origin/" + branchName);
|
|
||||||
fetchMessages = git.fetch()
|
|
||||||
.setRefSpecs(ref)
|
|
||||||
.setRemoveDeletedRefs(true)
|
|
||||||
.setTransportConfigCallback(config)
|
|
||||||
.call()
|
|
||||||
.getMessages();
|
|
||||||
}
|
|
||||||
processStopwatch.stopAndLogTimeInMillis();
|
|
||||||
return fetchMessages;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.onErrorResume(error -> {
|
|
||||||
log.error(error.getMessage());
|
|
||||||
return Mono.error(error);
|
|
||||||
})
|
|
||||||
.timeout(Duration.ofMillis(Constraint.TIMEOUT_MILLIS))
|
|
||||||
.subscribeOn(scheduler);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Mono<MergeStatusDTO> isMergeBranch(Path repoSuffix, String sourceBranch, String destinationBranch) {
|
|
||||||
Stopwatch processStopwatch =
|
|
||||||
StopwatchHelpers.startStopwatch(repoSuffix, AnalyticsEvents.GIT_MERGE_CHECK.getEventName());
|
|
||||||
return Mono.fromCallable(() -> {
|
|
||||||
log.debug(
|
|
||||||
Thread.currentThread().getName()
|
|
||||||
+ ": Check mergeability for repo {} with src: {}, dest: {}",
|
|
||||||
repoSuffix,
|
|
||||||
sourceBranch,
|
|
||||||
destinationBranch);
|
|
||||||
|
|
||||||
try (Git git = Git.open(createRepoPath(repoSuffix).toFile())) {
|
|
||||||
|
|
||||||
// checkout the branch on which the merge command is run
|
|
||||||
try {
|
|
||||||
git.checkout()
|
|
||||||
.setName(destinationBranch)
|
|
||||||
.setCreateBranch(false)
|
|
||||||
.call();
|
|
||||||
} catch (GitAPIException e) {
|
|
||||||
if (e instanceof CheckoutConflictException) {
|
|
||||||
MergeStatusDTO mergeStatus = new MergeStatusDTO();
|
|
||||||
mergeStatus.setMergeAble(false);
|
|
||||||
mergeStatus.setConflictingFiles(((CheckoutConflictException) e).getConflictingPaths());
|
|
||||||
processStopwatch.stopAndLogTimeInMillis();
|
|
||||||
return mergeStatus;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MergeResult mergeResult = git.merge()
|
|
||||||
.include(git.getRepository().findRef(sourceBranch))
|
|
||||||
.setFastForward(MergeCommand.FastForwardMode.NO_FF)
|
|
||||||
.setCommit(false)
|
|
||||||
.call();
|
|
||||||
|
|
||||||
MergeStatusDTO mergeStatus = new MergeStatusDTO();
|
|
||||||
if (mergeResult.getMergeStatus().isSuccessful()) {
|
|
||||||
mergeStatus.setMergeAble(true);
|
|
||||||
mergeStatus.setMessage(SUCCESS_MERGE_STATUS);
|
|
||||||
} else {
|
|
||||||
// If there aer conflicts add the conflicting file names to the response structure
|
|
||||||
mergeStatus.setMergeAble(false);
|
|
||||||
List<String> mergeConflictFiles =
|
|
||||||
new ArrayList<>(mergeResult.getConflicts().keySet());
|
|
||||||
mergeStatus.setConflictingFiles(mergeConflictFiles);
|
|
||||||
StringBuilder errorMessage = new StringBuilder();
|
|
||||||
if (mergeResult.getMergeStatus().equals(MergeResult.MergeStatus.CONFLICTING)) {
|
|
||||||
errorMessage.append("Conflicts");
|
|
||||||
} else {
|
|
||||||
errorMessage.append(mergeResult.getMergeStatus().toString());
|
|
||||||
}
|
|
||||||
errorMessage
|
|
||||||
.append(" while merging branch: ")
|
|
||||||
.append(destinationBranch)
|
|
||||||
.append(" <= ")
|
|
||||||
.append(sourceBranch);
|
|
||||||
mergeStatus.setMessage(errorMessage.toString());
|
|
||||||
mergeStatus.setReferenceDoc(ErrorReferenceDocUrl.GIT_MERGE_CONFLICT.getDocUrl());
|
|
||||||
}
|
|
||||||
mergeStatus.setStatus(mergeResult.getMergeStatus().name());
|
|
||||||
return mergeStatus;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.flatMap(status -> {
|
|
||||||
try {
|
|
||||||
// Revert uncommitted changes if any
|
|
||||||
return resetToLastCommit(repoSuffix, destinationBranch).map(ignore -> {
|
|
||||||
processStopwatch.stopAndLogTimeInMillis();
|
|
||||||
return status;
|
|
||||||
});
|
|
||||||
} catch (GitAPIException | IOException e) {
|
|
||||||
log.error("Error for hard resetting to latest commit {0}", e);
|
|
||||||
return Mono.error(e);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.timeout(Duration.ofMillis(Constraint.TIMEOUT_MILLIS))
|
|
||||||
.subscribeOn(scheduler);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Mono<String> checkoutRemoteBranch(Path repoSuffix, String branchName) {
|
|
||||||
// We can safely assume that repo has been already initialised either in commit or clone flow and can directly
|
|
||||||
// open the repo
|
|
||||||
return Mono.fromCallable(() -> {
|
|
||||||
log.debug(Thread.currentThread().getName() + ": Checking out remote branch origin/" + branchName
|
|
||||||
+ " for the repo " + repoSuffix);
|
|
||||||
// open the repo
|
|
||||||
Path baseRepoPath = createRepoPath(repoSuffix);
|
|
||||||
try (Git git = Git.open(baseRepoPath.toFile())) {
|
|
||||||
// Create and checkout to new branch
|
|
||||||
git.checkout()
|
|
||||||
.setCreateBranch(Boolean.TRUE)
|
|
||||||
.setName(branchName)
|
|
||||||
.setUpstreamMode(CreateBranchCommand.SetupUpstreamMode.TRACK)
|
|
||||||
.setStartPoint("origin/" + branchName)
|
|
||||||
.call();
|
|
||||||
|
|
||||||
StoredConfig config = git.getRepository().getConfig();
|
|
||||||
config.setString("branch", branchName, "remote", "origin");
|
|
||||||
config.setString("branch", branchName, "merge", "refs/heads/" + branchName);
|
|
||||||
config.save();
|
|
||||||
return git.getRepository().getBranch();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.timeout(Duration.ofMillis(Constraint.TIMEOUT_MILLIS))
|
|
||||||
.subscribeOn(scheduler);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Mono<Boolean> testConnection(String publicKey, String privateKey, String remoteUrl) {
|
|
||||||
return Mono.fromCallable(() -> {
|
|
||||||
TransportConfigCallback transportConfigCallback =
|
|
||||||
new SshTransportConfigCallback(privateKey, publicKey);
|
|
||||||
Git.lsRemoteRepository()
|
|
||||||
.setTransportConfigCallback(transportConfigCallback)
|
|
||||||
.setRemote(remoteUrl)
|
|
||||||
.setHeads(true)
|
|
||||||
.setTags(true)
|
|
||||||
.call();
|
|
||||||
return true;
|
|
||||||
})
|
|
||||||
.timeout(Duration.ofMillis(Constraint.TIMEOUT_MILLIS))
|
|
||||||
.subscribeOn(scheduler);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Mono<Ref> resetToLastCommit(Git git) throws GitAPIException {
|
|
||||||
Stopwatch processStopwatch = StopwatchHelpers.startStopwatch(
|
|
||||||
git.getRepository().getDirectory().toPath().getParent(), AnalyticsEvents.GIT_RESET.getEventName());
|
|
||||||
return Mono.fromCallable(() -> {
|
|
||||||
// Remove tracked files
|
|
||||||
Ref ref = git.reset().setMode(ResetCommand.ResetType.HARD).call();
|
|
||||||
// Remove untracked files
|
|
||||||
git.clean().setForce(true).setCleanDirectories(true).call();
|
|
||||||
processStopwatch.stopAndLogTimeInMillis();
|
|
||||||
return ref;
|
|
||||||
})
|
|
||||||
.timeout(Duration.ofMillis(Constraint.TIMEOUT_MILLIS))
|
|
||||||
.subscribeOn(scheduler);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Mono<Boolean> resetToLastCommit(Path repoSuffix, String branchName) throws GitAPIException, IOException {
|
|
||||||
try (Git git = Git.open(createRepoPath(repoSuffix).toFile())) {
|
|
||||||
return this.resetToLastCommit(git)
|
|
||||||
.flatMap(ref -> checkoutToBranch(repoSuffix, branchName))
|
|
||||||
.flatMap(checkedOut -> {
|
|
||||||
try {
|
|
||||||
return resetToLastCommit(git).thenReturn(true);
|
|
||||||
} catch (GitAPIException e) {
|
|
||||||
log.error(e.getMessage());
|
|
||||||
return Mono.error(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Mono<Boolean> resetHard(Path repoSuffix, String branchName) {
|
|
||||||
return this.checkoutToBranch(repoSuffix, branchName)
|
|
||||||
.flatMap(aBoolean -> {
|
|
||||||
try (Git git = Git.open(createRepoPath(repoSuffix).toFile())) {
|
|
||||||
Ref ref = git.reset()
|
|
||||||
.setMode(ResetCommand.ResetType.HARD)
|
|
||||||
.setRef("HEAD~1")
|
|
||||||
.call();
|
|
||||||
return Mono.just(true);
|
|
||||||
} catch (GitAPIException | IOException e) {
|
|
||||||
log.error("Error while resetting the commit, {}", e.getMessage());
|
|
||||||
}
|
|
||||||
return Mono.just(false);
|
|
||||||
})
|
|
||||||
.timeout(Duration.ofMillis(Constraint.TIMEOUT_MILLIS))
|
|
||||||
.subscribeOn(scheduler);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Mono<Boolean> rebaseBranch(Path repoSuffix, String branchName) {
|
|
||||||
return this.checkoutToBranch(repoSuffix, branchName)
|
|
||||||
.flatMap(isCheckedOut -> {
|
|
||||||
try (Git git = Git.open(createRepoPath(repoSuffix).toFile())) {
|
|
||||||
RebaseResult result =
|
|
||||||
git.rebase().setUpstream("origin/" + branchName).call();
|
|
||||||
if (result.getStatus().isSuccessful()) {
|
|
||||||
return Mono.just(true);
|
|
||||||
} else {
|
|
||||||
log.error(
|
|
||||||
"Error while rebasing the branch, {}, {}",
|
|
||||||
result.getStatus().name(),
|
|
||||||
result.getConflicts());
|
|
||||||
git.rebase()
|
|
||||||
.setUpstream("origin/" + branchName)
|
|
||||||
.setOperation(RebaseCommand.Operation.ABORT)
|
|
||||||
.call();
|
|
||||||
return Mono.error(new Exception("Error while rebasing the branch, "
|
|
||||||
+ result.getStatus().name()));
|
|
||||||
}
|
|
||||||
} catch (GitAPIException | IOException e) {
|
|
||||||
log.error("Error while rebasing the branch, {}", e.getMessage());
|
|
||||||
return Mono.error(e);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.timeout(Duration.ofMillis(Constraint.TIMEOUT_MILLIS))
|
|
||||||
.subscribeOn(scheduler);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Mono<BranchTrackingStatus> getBranchTrackingStatus(Path repoPath, String branchName) {
|
|
||||||
return Mono.fromCallable(() -> {
|
|
||||||
try (Git git = Git.open(createRepoPath(repoPath).toFile())) {
|
|
||||||
return BranchTrackingStatus.of(git.getRepository(), branchName);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.timeout(Duration.ofMillis(Constraint.TIMEOUT_MILLIS))
|
|
||||||
.subscribeOn(scheduler);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,917 @@
|
||||||
|
package com.appsmith.git.service.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.helpers.Stopwatch;
|
||||||
|
import com.appsmith.git.configurations.GitServiceConfig;
|
||||||
|
import com.appsmith.git.constants.AppsmithBotAsset;
|
||||||
|
import com.appsmith.git.constants.CommonConstants;
|
||||||
|
import com.appsmith.git.constants.Constraint;
|
||||||
|
import com.appsmith.git.constants.GitDirectories;
|
||||||
|
import com.appsmith.git.helpers.RepositoryHelper;
|
||||||
|
import com.appsmith.git.helpers.SshTransportConfigCallback;
|
||||||
|
import com.appsmith.git.helpers.StopwatchHelpers;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.eclipse.jgit.api.CreateBranchCommand;
|
||||||
|
import org.eclipse.jgit.api.Git;
|
||||||
|
import org.eclipse.jgit.api.ListBranchCommand;
|
||||||
|
import org.eclipse.jgit.api.MergeCommand;
|
||||||
|
import org.eclipse.jgit.api.MergeResult;
|
||||||
|
import org.eclipse.jgit.api.RebaseCommand;
|
||||||
|
import org.eclipse.jgit.api.RebaseResult;
|
||||||
|
import org.eclipse.jgit.api.ResetCommand;
|
||||||
|
import org.eclipse.jgit.api.Status;
|
||||||
|
import org.eclipse.jgit.api.TransportConfigCallback;
|
||||||
|
import org.eclipse.jgit.api.errors.CheckoutConflictException;
|
||||||
|
import org.eclipse.jgit.api.errors.GitAPIException;
|
||||||
|
import org.eclipse.jgit.lib.BranchTrackingStatus;
|
||||||
|
import org.eclipse.jgit.lib.PersonIdent;
|
||||||
|
import org.eclipse.jgit.lib.Ref;
|
||||||
|
import org.eclipse.jgit.lib.StoredConfig;
|
||||||
|
import org.eclipse.jgit.merge.MergeStrategy;
|
||||||
|
import org.eclipse.jgit.revwalk.RevCommit;
|
||||||
|
import org.eclipse.jgit.transport.RefSpec;
|
||||||
|
import org.eclipse.jgit.util.StringUtils;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.util.FileSystemUtils;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
import reactor.core.scheduler.Scheduler;
|
||||||
|
import reactor.core.scheduler.Schedulers;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.time.ZoneId;
|
||||||
|
import java.time.ZoneOffset;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static com.appsmith.git.constants.CommonConstants.FILE_MIGRATION_MESSAGE;
|
||||||
|
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@Component
|
||||||
|
@Slf4j
|
||||||
|
public class GitExecutorCEImpl implements GitExecutor {
|
||||||
|
|
||||||
|
private final RepositoryHelper repositoryHelper = new RepositoryHelper();
|
||||||
|
|
||||||
|
private final GitServiceConfig gitServiceConfig;
|
||||||
|
|
||||||
|
public static final DateTimeFormatter ISO_FORMATTER =
|
||||||
|
DateTimeFormatter.ISO_INSTANT.withZone(ZoneId.from(ZoneOffset.UTC));
|
||||||
|
|
||||||
|
private final Scheduler scheduler = Schedulers.boundedElastic();
|
||||||
|
|
||||||
|
private static final String SUCCESS_MERGE_STATUS = "This branch has no conflicts with the base branch.";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method will handle the git-commit functionality. Under the hood it checks if the repo has already been
|
||||||
|
* initialised and will be initialised if git repo is not present
|
||||||
|
* @param path parent path to repo
|
||||||
|
* @param commitMessage message which will be registered for this commit
|
||||||
|
* @param authorName author details
|
||||||
|
* @param authorEmail author details
|
||||||
|
* @param doAmend To amend with the previous commit
|
||||||
|
* @return if the commit was successful
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Mono<String> commitApplication(
|
||||||
|
Path path,
|
||||||
|
String commitMessage,
|
||||||
|
String authorName,
|
||||||
|
String authorEmail,
|
||||||
|
boolean isSuffixedPath,
|
||||||
|
boolean doAmend) {
|
||||||
|
|
||||||
|
final String finalAuthorName =
|
||||||
|
StringUtils.isEmptyOrNull(authorName) ? AppsmithBotAsset.APPSMITH_BOT_USERNAME : authorName;
|
||||||
|
final String finalAuthorEmail =
|
||||||
|
StringUtils.isEmptyOrNull(authorEmail) ? AppsmithBotAsset.APPSMITH_BOT_EMAIL : authorEmail;
|
||||||
|
return Mono.fromCallable(() -> {
|
||||||
|
log.debug("Trying to commit to local repo path, {}", path);
|
||||||
|
Path repoPath = path;
|
||||||
|
if (Boolean.TRUE.equals(isSuffixedPath)) {
|
||||||
|
repoPath = createRepoPath(repoPath);
|
||||||
|
}
|
||||||
|
Stopwatch processStopwatch =
|
||||||
|
StopwatchHelpers.startStopwatch(repoPath, AnalyticsEvents.GIT_COMMIT.getEventName());
|
||||||
|
// Just need to open a repository here and make a commit
|
||||||
|
try (Git git = Git.open(repoPath.toFile())) {
|
||||||
|
// Stage all the files added and modified
|
||||||
|
git.add().addFilepattern(".").call();
|
||||||
|
// Stage modified and deleted files
|
||||||
|
git.add().setUpdate(true).addFilepattern(".").call();
|
||||||
|
|
||||||
|
// Commit the changes
|
||||||
|
git.commit()
|
||||||
|
.setMessage(commitMessage)
|
||||||
|
// Only make a commit if there are any updates
|
||||||
|
.setAllowEmpty(false)
|
||||||
|
.setAuthor(finalAuthorName, finalAuthorEmail)
|
||||||
|
.setCommitter(finalAuthorName, finalAuthorEmail)
|
||||||
|
.setAmend(doAmend)
|
||||||
|
.call();
|
||||||
|
processStopwatch.stopAndLogTimeInMillis();
|
||||||
|
return "Committed successfully!";
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.timeout(Duration.ofMillis(Constraint.TIMEOUT_MILLIS))
|
||||||
|
.subscribeOn(scheduler);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method to create a new repository to provided path
|
||||||
|
* @param repoPath path where new repo needs to be created
|
||||||
|
* @return if the operation was successful
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean createNewRepository(Path repoPath) throws GitAPIException {
|
||||||
|
// create new repo to the mentioned path
|
||||||
|
log.debug("Trying to create new repository: {}", repoPath);
|
||||||
|
Git.init().setDirectory(repoPath.toFile()).call();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method to get the commit history
|
||||||
|
* @param repoSuffix Path used to generate the repo url specific to the application for which the commit history is requested
|
||||||
|
* @return list of git commits
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Mono<List<GitLogDTO>> getCommitHistory(Path repoSuffix) {
|
||||||
|
return Mono.fromCallable(() -> {
|
||||||
|
log.debug(Thread.currentThread().getName() + ": get commit history for " + repoSuffix);
|
||||||
|
List<GitLogDTO> commitLogs = new ArrayList<>();
|
||||||
|
Path repoPath = createRepoPath(repoSuffix);
|
||||||
|
Stopwatch processStopwatch = StopwatchHelpers.startStopwatch(
|
||||||
|
repoPath, AnalyticsEvents.GIT_COMMIT_HISTORY.getEventName());
|
||||||
|
try (Git git = Git.open(repoPath.toFile())) {
|
||||||
|
Iterable<RevCommit> gitLogs = git.log()
|
||||||
|
.setMaxCount(Constraint.MAX_COMMIT_LOGS)
|
||||||
|
.call();
|
||||||
|
gitLogs.forEach(revCommit -> {
|
||||||
|
PersonIdent author = revCommit.getAuthorIdent();
|
||||||
|
GitLogDTO gitLog = new GitLogDTO(
|
||||||
|
revCommit.getName(),
|
||||||
|
author.getName(),
|
||||||
|
author.getEmailAddress(),
|
||||||
|
revCommit.getFullMessage(),
|
||||||
|
ISO_FORMATTER.format(new Date(revCommit.getCommitTime() * 1000L).toInstant()));
|
||||||
|
processStopwatch.stopAndLogTimeInMillis();
|
||||||
|
commitLogs.add(gitLog);
|
||||||
|
});
|
||||||
|
return commitLogs;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.timeout(Duration.ofMillis(Constraint.TIMEOUT_MILLIS))
|
||||||
|
.subscribeOn(scheduler);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Path createRepoPath(Path suffix) {
|
||||||
|
return Paths.get(gitServiceConfig.getGitRootPath()).resolve(suffix);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method to push changes to remote repo
|
||||||
|
* @param repoSuffix Path used to generate the repo url specific to the application which needs to be pushed to remote
|
||||||
|
* @param remoteUrl remote repo url
|
||||||
|
* @param publicKey
|
||||||
|
* @param privateKey
|
||||||
|
* @return Success message
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Mono<String> pushApplication(
|
||||||
|
Path repoSuffix, String remoteUrl, String publicKey, String privateKey, String branchName) {
|
||||||
|
// We can safely assume that repo has been already initialised either in commit or clone flow and can directly
|
||||||
|
// open the repo
|
||||||
|
return Mono.fromCallable(() -> {
|
||||||
|
log.debug(Thread.currentThread().getName() + ": pushing changes to remote " + remoteUrl);
|
||||||
|
// open the repo
|
||||||
|
Path baseRepoPath = createRepoPath(repoSuffix);
|
||||||
|
Stopwatch processStopwatch =
|
||||||
|
StopwatchHelpers.startStopwatch(baseRepoPath, AnalyticsEvents.GIT_PUSH.getEventName());
|
||||||
|
try (Git git = Git.open(baseRepoPath.toFile())) {
|
||||||
|
TransportConfigCallback transportConfigCallback =
|
||||||
|
new SshTransportConfigCallback(privateKey, publicKey);
|
||||||
|
|
||||||
|
StringBuilder result = new StringBuilder("Pushed successfully with status : ");
|
||||||
|
git.push()
|
||||||
|
.setTransportConfigCallback(transportConfigCallback)
|
||||||
|
.setRemote(remoteUrl)
|
||||||
|
.call()
|
||||||
|
.forEach(pushResult -> pushResult
|
||||||
|
.getRemoteUpdates()
|
||||||
|
.forEach(remoteRefUpdate -> {
|
||||||
|
result.append(remoteRefUpdate.getStatus())
|
||||||
|
.append(",");
|
||||||
|
if (!StringUtils.isEmptyOrNull(remoteRefUpdate.getMessage())) {
|
||||||
|
result.append(remoteRefUpdate.getMessage())
|
||||||
|
.append(",");
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
// We can support username and password in future if needed
|
||||||
|
// pushCommand.setCredentialsProvider(new UsernamePasswordCredentialsProvider("username",
|
||||||
|
// "password"));
|
||||||
|
processStopwatch.stopAndLogTimeInMillis();
|
||||||
|
return result.substring(0, result.length() - 1);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.timeout(Duration.ofMillis(Constraint.TIMEOUT_MILLIS))
|
||||||
|
.subscribeOn(scheduler);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Clone the repo to the file path : container-volume/orgId/defaultAppId/repo/applicationData
|
||||||
|
*
|
||||||
|
* @param repoSuffix combination of orgId, defaultId and repoName
|
||||||
|
* @param remoteUrl ssh url of the git repo(we support cloning via ssh url only with deploy key)
|
||||||
|
* @param privateKey generated by us and specific to the defaultApplication
|
||||||
|
* @param publicKey generated by us and specific to the defaultApplication
|
||||||
|
* @return defaultBranchName of the repo
|
||||||
|
* */
|
||||||
|
@Override
|
||||||
|
public Mono<String> cloneApplication(Path repoSuffix, String remoteUrl, String privateKey, String publicKey) {
|
||||||
|
|
||||||
|
Stopwatch processStopwatch =
|
||||||
|
StopwatchHelpers.startStopwatch(repoSuffix, AnalyticsEvents.GIT_CLONE.getEventName());
|
||||||
|
return Mono.fromCallable(() -> {
|
||||||
|
log.debug(Thread.currentThread().getName() + ": Cloning the repo from the remote " + remoteUrl);
|
||||||
|
final TransportConfigCallback transportConfigCallback =
|
||||||
|
new SshTransportConfigCallback(privateKey, publicKey);
|
||||||
|
File file = Paths.get(gitServiceConfig.getGitRootPath())
|
||||||
|
.resolve(repoSuffix)
|
||||||
|
.toFile();
|
||||||
|
while (file.exists()) {
|
||||||
|
FileSystemUtils.deleteRecursively(file);
|
||||||
|
}
|
||||||
|
Git git = Git.cloneRepository()
|
||||||
|
.setURI(remoteUrl)
|
||||||
|
.setTransportConfigCallback(transportConfigCallback)
|
||||||
|
.setDirectory(file)
|
||||||
|
.call();
|
||||||
|
String branchName = git.getRepository().getBranch();
|
||||||
|
|
||||||
|
repositoryHelper.updateRemoteBranchTrackingConfig(branchName, git);
|
||||||
|
git.close();
|
||||||
|
processStopwatch.stopAndLogTimeInMillis();
|
||||||
|
return branchName;
|
||||||
|
})
|
||||||
|
.timeout(Duration.ofMillis(Constraint.TIMEOUT_MILLIS))
|
||||||
|
.subscribeOn(scheduler);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mono<String> createAndCheckoutToBranch(Path repoSuffix, String branchName) {
|
||||||
|
// We can safely assume that repo has been already initialised either in commit or clone flow and can directly
|
||||||
|
// open the repo
|
||||||
|
Stopwatch processStopwatch =
|
||||||
|
StopwatchHelpers.startStopwatch(repoSuffix, AnalyticsEvents.GIT_CREATE_BRANCH.getEventName());
|
||||||
|
return Mono.fromCallable(() -> {
|
||||||
|
log.debug(Thread.currentThread().getName() + ": Creating branch " + branchName + "for the repo "
|
||||||
|
+ repoSuffix);
|
||||||
|
// open the repo
|
||||||
|
Path baseRepoPath = createRepoPath(repoSuffix);
|
||||||
|
try (Git git = Git.open(baseRepoPath.toFile())) {
|
||||||
|
// Create and checkout to new branch
|
||||||
|
git.checkout()
|
||||||
|
.setCreateBranch(Boolean.TRUE)
|
||||||
|
.setName(branchName)
|
||||||
|
.setUpstreamMode(CreateBranchCommand.SetupUpstreamMode.TRACK)
|
||||||
|
.call();
|
||||||
|
|
||||||
|
repositoryHelper.updateRemoteBranchTrackingConfig(branchName, git);
|
||||||
|
processStopwatch.stopAndLogTimeInMillis();
|
||||||
|
return git.getRepository().getBranch();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.timeout(Duration.ofMillis(Constraint.TIMEOUT_MILLIS))
|
||||||
|
.subscribeOn(scheduler);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mono<Boolean> deleteBranch(Path repoSuffix, String branchName) {
|
||||||
|
// We can safely assume that repo has been already initialised either in commit or clone flow and can directly
|
||||||
|
// open the repo
|
||||||
|
Stopwatch processStopwatch =
|
||||||
|
StopwatchHelpers.startStopwatch(repoSuffix, AnalyticsEvents.GIT_DELETE_BRANCH.getEventName());
|
||||||
|
return Mono.fromCallable(() -> {
|
||||||
|
log.debug(Thread.currentThread().getName() + ": Deleting branch " + branchName + "for the repo "
|
||||||
|
+ repoSuffix);
|
||||||
|
// open the repo
|
||||||
|
Path baseRepoPath = createRepoPath(repoSuffix);
|
||||||
|
try (Git git = Git.open(baseRepoPath.toFile())) {
|
||||||
|
// Create and checkout to new branch
|
||||||
|
List<String> deleteBranchList = git.branchDelete()
|
||||||
|
.setBranchNames(branchName)
|
||||||
|
.setForce(Boolean.TRUE)
|
||||||
|
.call();
|
||||||
|
processStopwatch.stopAndLogTimeInMillis();
|
||||||
|
if (deleteBranchList.isEmpty()) {
|
||||||
|
return Boolean.FALSE;
|
||||||
|
}
|
||||||
|
return Boolean.TRUE;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.timeout(Duration.ofMillis(Constraint.TIMEOUT_MILLIS))
|
||||||
|
.subscribeOn(scheduler);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mono<Boolean> checkoutToBranch(Path repoSuffix, String branchName) {
|
||||||
|
|
||||||
|
Stopwatch processStopwatch =
|
||||||
|
StopwatchHelpers.startStopwatch(repoSuffix, AnalyticsEvents.GIT_CHECKOUT.getEventName());
|
||||||
|
return Mono.fromCallable(() -> {
|
||||||
|
log.debug(Thread.currentThread().getName() + ": Switching to the branch " + branchName);
|
||||||
|
// We can safely assume that repo has been already initialised either in commit or clone flow and
|
||||||
|
// can directly
|
||||||
|
// open the repo
|
||||||
|
Path baseRepoPath = createRepoPath(repoSuffix);
|
||||||
|
try (Git git = Git.open(baseRepoPath.toFile())) {
|
||||||
|
if (StringUtils.equalsIgnoreCase(
|
||||||
|
branchName, git.getRepository().getBranch())) {
|
||||||
|
return Boolean.TRUE;
|
||||||
|
}
|
||||||
|
// Create and checkout to new branch
|
||||||
|
String checkedOutBranch = git.checkout()
|
||||||
|
.setCreateBranch(Boolean.FALSE)
|
||||||
|
.setName(branchName)
|
||||||
|
.setUpstreamMode(CreateBranchCommand.SetupUpstreamMode.SET_UPSTREAM)
|
||||||
|
.call()
|
||||||
|
.getName();
|
||||||
|
processStopwatch.stopAndLogTimeInMillis();
|
||||||
|
return StringUtils.equalsIgnoreCase(checkedOutBranch, "refs/heads/" + branchName);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new Exception(e);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.timeout(Duration.ofMillis(Constraint.TIMEOUT_MILLIS))
|
||||||
|
.subscribeOn(scheduler);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mono<MergeStatusDTO> pullApplication(
|
||||||
|
Path repoSuffix, String remoteUrl, String branchName, String privateKey, String publicKey)
|
||||||
|
throws IOException {
|
||||||
|
|
||||||
|
Stopwatch processStopwatch =
|
||||||
|
StopwatchHelpers.startStopwatch(repoSuffix, AnalyticsEvents.GIT_PULL.getEventName());
|
||||||
|
TransportConfigCallback transportConfigCallback = new SshTransportConfigCallback(privateKey, publicKey);
|
||||||
|
|
||||||
|
try (Git git = Git.open(createRepoPath(repoSuffix).toFile())) {
|
||||||
|
return Mono.fromCallable(() -> {
|
||||||
|
log.debug(Thread.currentThread().getName() + ": Pull changes from remote " + remoteUrl
|
||||||
|
+ " for the branch " + branchName);
|
||||||
|
// checkout the branch on which the merge command is run
|
||||||
|
git.checkout()
|
||||||
|
.setName(branchName)
|
||||||
|
.setCreateBranch(false)
|
||||||
|
.call();
|
||||||
|
MergeResult mergeResult = git.pull()
|
||||||
|
.setRemoteBranchName(branchName)
|
||||||
|
.setTransportConfigCallback(transportConfigCallback)
|
||||||
|
.setFastForward(MergeCommand.FastForwardMode.FF)
|
||||||
|
.call()
|
||||||
|
.getMergeResult();
|
||||||
|
MergeStatusDTO mergeStatus = new MergeStatusDTO();
|
||||||
|
Long count =
|
||||||
|
Arrays.stream(mergeResult.getMergedCommits()).count();
|
||||||
|
if (mergeResult.getMergeStatus().isSuccessful()) {
|
||||||
|
mergeStatus.setMergeAble(true);
|
||||||
|
mergeStatus.setStatus(count + " commits merged from origin/" + branchName);
|
||||||
|
} else {
|
||||||
|
// If there are conflicts add the conflicting file names to the response structure
|
||||||
|
mergeStatus.setMergeAble(false);
|
||||||
|
List<String> mergeConflictFiles = new ArrayList<>();
|
||||||
|
if (!Optional.ofNullable(mergeResult.getConflicts()).isEmpty()) {
|
||||||
|
mergeConflictFiles.addAll(
|
||||||
|
mergeResult.getConflicts().keySet());
|
||||||
|
}
|
||||||
|
mergeStatus.setConflictingFiles(mergeConflictFiles);
|
||||||
|
// On merge conflicts abort the merge => git merge --abort
|
||||||
|
git.getRepository().writeMergeCommitMsg(null);
|
||||||
|
git.getRepository().writeMergeHeads(null);
|
||||||
|
processStopwatch.stopAndLogTimeInMillis();
|
||||||
|
throw new org.eclipse.jgit.errors.CheckoutConflictException(mergeConflictFiles.toString());
|
||||||
|
}
|
||||||
|
processStopwatch.stopAndLogTimeInMillis();
|
||||||
|
return mergeStatus;
|
||||||
|
})
|
||||||
|
.onErrorResume(error -> {
|
||||||
|
try {
|
||||||
|
return resetToLastCommit(git).flatMap(ignore -> Mono.error(error));
|
||||||
|
} catch (GitAPIException e) {
|
||||||
|
log.error("Error for hard resetting to latest commit {0}", e);
|
||||||
|
return Mono.error(e);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.timeout(Duration.ofMillis(Constraint.TIMEOUT_MILLIS))
|
||||||
|
.subscribeOn(scheduler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mono<List<GitBranchDTO>> listBranches(Path repoSuffix) {
|
||||||
|
Path baseRepoPath = createRepoPath(repoSuffix);
|
||||||
|
return Mono.fromCallable(() -> {
|
||||||
|
log.debug(Thread.currentThread().getName() + ": Get branches for the application " + repoSuffix);
|
||||||
|
Git git = Git.open(baseRepoPath.toFile());
|
||||||
|
List<Ref> refList = git.branchList()
|
||||||
|
.setListMode(ListBranchCommand.ListMode.ALL)
|
||||||
|
.call();
|
||||||
|
|
||||||
|
List<GitBranchDTO> branchList = new ArrayList<>();
|
||||||
|
GitBranchDTO gitBranchDTO = new GitBranchDTO();
|
||||||
|
if (refList.isEmpty()) {
|
||||||
|
gitBranchDTO.setBranchName(git.getRepository().getBranch());
|
||||||
|
branchList.add(gitBranchDTO);
|
||||||
|
} else {
|
||||||
|
for (Ref ref : refList) {
|
||||||
|
// if (!ref.getName().equals(defaultBranch)) {
|
||||||
|
gitBranchDTO = new GitBranchDTO();
|
||||||
|
gitBranchDTO.setBranchName(ref.getName()
|
||||||
|
.replace("refs/", "")
|
||||||
|
.replace("heads/", "")
|
||||||
|
.replace("remotes/", ""));
|
||||||
|
branchList.add(gitBranchDTO);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
git.close();
|
||||||
|
return branchList;
|
||||||
|
})
|
||||||
|
.timeout(Duration.ofMillis(Constraint.TIMEOUT_MILLIS))
|
||||||
|
.subscribeOn(scheduler);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mono<String> getRemoteDefaultBranch(Path repoSuffix, String remoteUrl, String privateKey, String publicKey) {
|
||||||
|
Path baseRepoPath = createRepoPath(repoSuffix);
|
||||||
|
return Mono.fromCallable(() -> {
|
||||||
|
TransportConfigCallback transportConfigCallback =
|
||||||
|
new SshTransportConfigCallback(privateKey, publicKey);
|
||||||
|
Git git = Git.open(baseRepoPath.toFile());
|
||||||
|
|
||||||
|
return git.lsRemote()
|
||||||
|
.setRemote(remoteUrl)
|
||||||
|
.setTransportConfigCallback(transportConfigCallback)
|
||||||
|
.callAsMap()
|
||||||
|
.get("HEAD")
|
||||||
|
.getTarget()
|
||||||
|
.getName()
|
||||||
|
.replace("refs/heads/", "");
|
||||||
|
})
|
||||||
|
.timeout(Duration.ofMillis(Constraint.TIMEOUT_MILLIS))
|
||||||
|
.subscribeOn(scheduler);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method will handle the git-status functionality
|
||||||
|
*
|
||||||
|
* @param repoPath Path to actual repo
|
||||||
|
* @param branchName branch name for which the status is required
|
||||||
|
* @return Map of file names those are modified, conflicted etc.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Mono<GitStatusDTO> getStatus(Path repoPath, String branchName) {
|
||||||
|
Stopwatch processStopwatch =
|
||||||
|
StopwatchHelpers.startStopwatch(repoPath, AnalyticsEvents.GIT_STATUS.getEventName());
|
||||||
|
return Mono.fromCallable(() -> {
|
||||||
|
try (Git git = Git.open(repoPath.toFile())) {
|
||||||
|
log.debug(Thread.currentThread().getName() + ": Get status for repo " + repoPath + ", branch "
|
||||||
|
+ branchName);
|
||||||
|
Status status = git.status().call();
|
||||||
|
GitStatusDTO response = new GitStatusDTO();
|
||||||
|
Set<String> modifiedAssets = new HashSet<>();
|
||||||
|
modifiedAssets.addAll(status.getModified());
|
||||||
|
modifiedAssets.addAll(status.getAdded());
|
||||||
|
modifiedAssets.addAll(status.getRemoved());
|
||||||
|
modifiedAssets.addAll(status.getUncommittedChanges());
|
||||||
|
modifiedAssets.addAll(status.getUntracked());
|
||||||
|
response.setAdded(status.getAdded());
|
||||||
|
response.setRemoved(status.getRemoved());
|
||||||
|
|
||||||
|
populateModifiedEntities(status, response, modifiedAssets);
|
||||||
|
|
||||||
|
BranchTrackingStatus trackingStatus = BranchTrackingStatus.of(git.getRepository(), branchName);
|
||||||
|
if (trackingStatus != null) {
|
||||||
|
response.setAheadCount(trackingStatus.getAheadCount());
|
||||||
|
response.setBehindCount(trackingStatus.getBehindCount());
|
||||||
|
response.setRemoteBranch(trackingStatus.getRemoteTrackingBranch());
|
||||||
|
} else {
|
||||||
|
log.debug(
|
||||||
|
"Remote tracking details not present for branch: {}, repo: {}",
|
||||||
|
branchName,
|
||||||
|
repoPath);
|
||||||
|
response.setAheadCount(0);
|
||||||
|
response.setBehindCount(0);
|
||||||
|
response.setRemoteBranch("untracked");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove modified changes from current branch so that checkout to other branches will be
|
||||||
|
// possible
|
||||||
|
if (!status.isClean()) {
|
||||||
|
return resetToLastCommit(git).map(ref -> {
|
||||||
|
processStopwatch.stopAndLogTimeInMillis();
|
||||||
|
return response;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
processStopwatch.stopAndLogTimeInMillis();
|
||||||
|
return Mono.just(response);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.timeout(Duration.ofMillis(Constraint.TIMEOUT_MILLIS))
|
||||||
|
.flatMap(response -> response)
|
||||||
|
.subscribeOn(scheduler);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void populateModifiedEntities(Status status, GitStatusDTO response, Set<String> modifiedAssets) {
|
||||||
|
Set<String> queriesModified = new HashSet<>();
|
||||||
|
Set<String> jsObjectsModified = new HashSet<>();
|
||||||
|
Set<String> pagesModified = new HashSet<>();
|
||||||
|
int modifiedPages = 0;
|
||||||
|
int modifiedQueries = 0;
|
||||||
|
int modifiedJSObjects = 0;
|
||||||
|
int modifiedDatasources = 0;
|
||||||
|
int modifiedJSLibs = 0;
|
||||||
|
for (String x : modifiedAssets) {
|
||||||
|
// begins with pages and filename and parent name should be same or contains widgets
|
||||||
|
if (x.contains(CommonConstants.WIDGETS)) {
|
||||||
|
if (!pagesModified.contains(getPageName(x))) {
|
||||||
|
pagesModified.add(getPageName(x));
|
||||||
|
modifiedPages++;
|
||||||
|
}
|
||||||
|
} else if (isAModifiedPage(x)) {
|
||||||
|
if (!pagesModified.contains(getPageName(x))) {
|
||||||
|
pagesModified.add(getPageName(x));
|
||||||
|
modifiedPages++;
|
||||||
|
}
|
||||||
|
} else if (x.contains(GitDirectories.ACTION_DIRECTORY + CommonConstants.DELIMITER_PATH)) {
|
||||||
|
String queryName = x.split(GitDirectories.ACTION_DIRECTORY + CommonConstants.DELIMITER_PATH)[1];
|
||||||
|
int position = queryName.indexOf(CommonConstants.DELIMITER_PATH);
|
||||||
|
if (position != -1) {
|
||||||
|
queryName = queryName.substring(0, position);
|
||||||
|
String pageName = x.split(CommonConstants.DELIMITER_PATH)[1];
|
||||||
|
if (!queriesModified.contains(pageName + queryName)) {
|
||||||
|
queriesModified.add(pageName + queryName);
|
||||||
|
modifiedQueries++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (x.contains(GitDirectories.ACTION_COLLECTION_DIRECTORY + CommonConstants.DELIMITER_PATH)
|
||||||
|
&& !x.endsWith(CommonConstants.JSON_EXTENSION)) {
|
||||||
|
String queryName = x.substring(x.lastIndexOf(CommonConstants.DELIMITER_PATH) + 1);
|
||||||
|
String pageName = x.split(CommonConstants.DELIMITER_PATH)[1];
|
||||||
|
if (!jsObjectsModified.contains(pageName + queryName)) {
|
||||||
|
jsObjectsModified.add(pageName + queryName);
|
||||||
|
modifiedJSObjects++;
|
||||||
|
}
|
||||||
|
} else if (x.contains(GitDirectories.DATASOURCE_DIRECTORY + CommonConstants.DELIMITER_PATH)) {
|
||||||
|
modifiedDatasources++;
|
||||||
|
} else if (x.contains(GitDirectories.JS_LIB_DIRECTORY + CommonConstants.DELIMITER_PATH)) {
|
||||||
|
modifiedJSLibs++;
|
||||||
|
// remove this code in future when all the older format js libs are migrated to new
|
||||||
|
// format
|
||||||
|
|
||||||
|
if (x.contains("js.json")) {
|
||||||
|
/*
|
||||||
|
As this updated filename has color(:), it means this is the older format js
|
||||||
|
lib file that we're going to rename with the format without colon.
|
||||||
|
Hence, we need to show a message to user saying this might be a system level change.
|
||||||
|
*/
|
||||||
|
response.setMigrationMessage(FILE_MIGRATION_MESSAGE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
response.setModified(modifiedAssets);
|
||||||
|
response.setConflicting(status.getConflicting());
|
||||||
|
response.setIsClean(status.isClean());
|
||||||
|
response.setModifiedPages(modifiedPages);
|
||||||
|
response.setModifiedQueries(modifiedQueries);
|
||||||
|
response.setModifiedJSObjects(modifiedJSObjects);
|
||||||
|
response.setModifiedDatasources(modifiedDatasources);
|
||||||
|
response.setModifiedJSLibs(modifiedJSLibs);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean isAModifiedPage(String x) {
|
||||||
|
return !x.contains(CommonConstants.WIDGETS)
|
||||||
|
&& x.startsWith(GitDirectories.PAGE_DIRECTORY)
|
||||||
|
&& !x.contains(GitDirectories.ACTION_DIRECTORY)
|
||||||
|
&& !x.contains(GitDirectories.ACTION_COLLECTION_DIRECTORY);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getPageName(String path) {
|
||||||
|
String[] pathArray = path.split(CommonConstants.DELIMITER_PATH);
|
||||||
|
return pathArray[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mono<String> mergeBranch(Path repoSuffix, String sourceBranch, String destinationBranch) {
|
||||||
|
return Mono.fromCallable(() -> {
|
||||||
|
Stopwatch processStopwatch =
|
||||||
|
StopwatchHelpers.startStopwatch(repoSuffix, AnalyticsEvents.GIT_MERGE.getEventName());
|
||||||
|
log.debug(Thread.currentThread().getName() + ": Merge branch " + sourceBranch + " on "
|
||||||
|
+ destinationBranch);
|
||||||
|
try (Git git = Git.open(createRepoPath(repoSuffix).toFile())) {
|
||||||
|
try {
|
||||||
|
// checkout the branch on which the merge command is run
|
||||||
|
git.checkout()
|
||||||
|
.setName(destinationBranch)
|
||||||
|
.setCreateBranch(false)
|
||||||
|
.call();
|
||||||
|
|
||||||
|
MergeResult mergeResult = git.merge()
|
||||||
|
.include(git.getRepository().findRef(sourceBranch))
|
||||||
|
.setStrategy(MergeStrategy.RECURSIVE)
|
||||||
|
.call();
|
||||||
|
processStopwatch.stopAndLogTimeInMillis();
|
||||||
|
return mergeResult.getMergeStatus().name();
|
||||||
|
} catch (GitAPIException e) {
|
||||||
|
// On merge conflicts abort the merge => git merge --abort
|
||||||
|
git.getRepository().writeMergeCommitMsg(null);
|
||||||
|
git.getRepository().writeMergeHeads(null);
|
||||||
|
processStopwatch.stopAndLogTimeInMillis();
|
||||||
|
throw new Exception(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.onErrorResume(error -> {
|
||||||
|
try {
|
||||||
|
return resetToLastCommit(repoSuffix, destinationBranch).thenReturn(error.getMessage());
|
||||||
|
} catch (GitAPIException | IOException e) {
|
||||||
|
log.error("Error while hard resetting to latest commit {0}", e);
|
||||||
|
return Mono.error(e);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.timeout(Duration.ofMillis(Constraint.TIMEOUT_MILLIS))
|
||||||
|
.subscribeOn(scheduler);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mono<String> fetchRemote(
|
||||||
|
Path repoSuffix,
|
||||||
|
String publicKey,
|
||||||
|
String privateKey,
|
||||||
|
boolean isRepoPath,
|
||||||
|
String branchName,
|
||||||
|
boolean isFetchAll) {
|
||||||
|
Stopwatch processStopwatch =
|
||||||
|
StopwatchHelpers.startStopwatch(repoSuffix, AnalyticsEvents.GIT_FETCH.getEventName());
|
||||||
|
Path repoPath = Boolean.TRUE.equals(isRepoPath) ? repoSuffix : createRepoPath(repoSuffix);
|
||||||
|
return Mono.fromCallable(() -> {
|
||||||
|
TransportConfigCallback config = new SshTransportConfigCallback(privateKey, publicKey);
|
||||||
|
try (Git git = Git.open(repoPath.toFile())) {
|
||||||
|
String fetchMessages;
|
||||||
|
if (Boolean.TRUE.equals(isFetchAll)) {
|
||||||
|
fetchMessages = git.fetch()
|
||||||
|
.setRemoveDeletedRefs(true)
|
||||||
|
.setTransportConfigCallback(config)
|
||||||
|
.call()
|
||||||
|
.getMessages();
|
||||||
|
} else {
|
||||||
|
RefSpec ref =
|
||||||
|
new RefSpec("refs/heads/" + branchName + ":refs/remotes/origin/" + branchName);
|
||||||
|
fetchMessages = git.fetch()
|
||||||
|
.setRefSpecs(ref)
|
||||||
|
.setRemoveDeletedRefs(true)
|
||||||
|
.setTransportConfigCallback(config)
|
||||||
|
.call()
|
||||||
|
.getMessages();
|
||||||
|
}
|
||||||
|
processStopwatch.stopAndLogTimeInMillis();
|
||||||
|
return fetchMessages;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.onErrorResume(error -> {
|
||||||
|
log.error(error.getMessage());
|
||||||
|
return Mono.error(error);
|
||||||
|
})
|
||||||
|
.timeout(Duration.ofMillis(Constraint.TIMEOUT_MILLIS))
|
||||||
|
.subscribeOn(scheduler);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mono<MergeStatusDTO> isMergeBranch(Path repoSuffix, String sourceBranch, String destinationBranch) {
|
||||||
|
Stopwatch processStopwatch =
|
||||||
|
StopwatchHelpers.startStopwatch(repoSuffix, AnalyticsEvents.GIT_MERGE_CHECK.getEventName());
|
||||||
|
return Mono.fromCallable(() -> {
|
||||||
|
log.debug(
|
||||||
|
Thread.currentThread().getName()
|
||||||
|
+ ": Check mergeability for repo {} with src: {}, dest: {}",
|
||||||
|
repoSuffix,
|
||||||
|
sourceBranch,
|
||||||
|
destinationBranch);
|
||||||
|
|
||||||
|
try (Git git = Git.open(createRepoPath(repoSuffix).toFile())) {
|
||||||
|
|
||||||
|
// checkout the branch on which the merge command is run
|
||||||
|
try {
|
||||||
|
git.checkout()
|
||||||
|
.setName(destinationBranch)
|
||||||
|
.setCreateBranch(false)
|
||||||
|
.call();
|
||||||
|
} catch (GitAPIException e) {
|
||||||
|
if (e instanceof CheckoutConflictException) {
|
||||||
|
MergeStatusDTO mergeStatus = new MergeStatusDTO();
|
||||||
|
mergeStatus.setMergeAble(false);
|
||||||
|
mergeStatus.setConflictingFiles(((CheckoutConflictException) e).getConflictingPaths());
|
||||||
|
processStopwatch.stopAndLogTimeInMillis();
|
||||||
|
return mergeStatus;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MergeResult mergeResult = git.merge()
|
||||||
|
.include(git.getRepository().findRef(sourceBranch))
|
||||||
|
.setFastForward(MergeCommand.FastForwardMode.NO_FF)
|
||||||
|
.setCommit(false)
|
||||||
|
.call();
|
||||||
|
|
||||||
|
MergeStatusDTO mergeStatus = new MergeStatusDTO();
|
||||||
|
if (mergeResult.getMergeStatus().isSuccessful()) {
|
||||||
|
mergeStatus.setMergeAble(true);
|
||||||
|
mergeStatus.setMessage(SUCCESS_MERGE_STATUS);
|
||||||
|
} else {
|
||||||
|
// If there aer conflicts add the conflicting file names to the response structure
|
||||||
|
mergeStatus.setMergeAble(false);
|
||||||
|
List<String> mergeConflictFiles =
|
||||||
|
new ArrayList<>(mergeResult.getConflicts().keySet());
|
||||||
|
mergeStatus.setConflictingFiles(mergeConflictFiles);
|
||||||
|
StringBuilder errorMessage = new StringBuilder();
|
||||||
|
if (mergeResult.getMergeStatus().equals(MergeResult.MergeStatus.CONFLICTING)) {
|
||||||
|
errorMessage.append("Conflicts");
|
||||||
|
} else {
|
||||||
|
errorMessage.append(mergeResult.getMergeStatus().toString());
|
||||||
|
}
|
||||||
|
errorMessage
|
||||||
|
.append(" while merging branch: ")
|
||||||
|
.append(destinationBranch)
|
||||||
|
.append(" <= ")
|
||||||
|
.append(sourceBranch);
|
||||||
|
mergeStatus.setMessage(errorMessage.toString());
|
||||||
|
mergeStatus.setReferenceDoc(ErrorReferenceDocUrl.GIT_MERGE_CONFLICT.getDocUrl());
|
||||||
|
}
|
||||||
|
mergeStatus.setStatus(mergeResult.getMergeStatus().name());
|
||||||
|
return mergeStatus;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.flatMap(status -> {
|
||||||
|
try {
|
||||||
|
// Revert uncommitted changes if any
|
||||||
|
return resetToLastCommit(repoSuffix, destinationBranch).map(ignore -> {
|
||||||
|
processStopwatch.stopAndLogTimeInMillis();
|
||||||
|
return status;
|
||||||
|
});
|
||||||
|
} catch (GitAPIException | IOException e) {
|
||||||
|
log.error("Error for hard resetting to latest commit {0}", e);
|
||||||
|
return Mono.error(e);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.timeout(Duration.ofMillis(Constraint.TIMEOUT_MILLIS))
|
||||||
|
.subscribeOn(scheduler);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Mono<String> checkoutRemoteBranch(Path repoSuffix, String branchName) {
|
||||||
|
// We can safely assume that repo has been already initialised either in commit or clone flow and can directly
|
||||||
|
// open the repo
|
||||||
|
return Mono.fromCallable(() -> {
|
||||||
|
log.debug(Thread.currentThread().getName() + ": Checking out remote branch origin/" + branchName
|
||||||
|
+ " for the repo " + repoSuffix);
|
||||||
|
// open the repo
|
||||||
|
Path baseRepoPath = createRepoPath(repoSuffix);
|
||||||
|
try (Git git = Git.open(baseRepoPath.toFile())) {
|
||||||
|
// Create and checkout to new branch
|
||||||
|
git.checkout()
|
||||||
|
.setCreateBranch(Boolean.TRUE)
|
||||||
|
.setName(branchName)
|
||||||
|
.setUpstreamMode(CreateBranchCommand.SetupUpstreamMode.TRACK)
|
||||||
|
.setStartPoint("origin/" + branchName)
|
||||||
|
.call();
|
||||||
|
|
||||||
|
StoredConfig config = git.getRepository().getConfig();
|
||||||
|
config.setString("branch", branchName, "remote", "origin");
|
||||||
|
config.setString("branch", branchName, "merge", "refs/heads/" + branchName);
|
||||||
|
config.save();
|
||||||
|
return git.getRepository().getBranch();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.timeout(Duration.ofMillis(Constraint.TIMEOUT_MILLIS))
|
||||||
|
.subscribeOn(scheduler);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mono<Boolean> testConnection(String publicKey, String privateKey, String remoteUrl) {
|
||||||
|
return Mono.fromCallable(() -> {
|
||||||
|
TransportConfigCallback transportConfigCallback =
|
||||||
|
new SshTransportConfigCallback(privateKey, publicKey);
|
||||||
|
Git.lsRemoteRepository()
|
||||||
|
.setTransportConfigCallback(transportConfigCallback)
|
||||||
|
.setRemote(remoteUrl)
|
||||||
|
.setHeads(true)
|
||||||
|
.setTags(true)
|
||||||
|
.call();
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.timeout(Duration.ofMillis(Constraint.TIMEOUT_MILLIS))
|
||||||
|
.subscribeOn(scheduler);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Mono<Ref> resetToLastCommit(Git git) throws GitAPIException {
|
||||||
|
Stopwatch processStopwatch = StopwatchHelpers.startStopwatch(
|
||||||
|
git.getRepository().getDirectory().toPath().getParent(), AnalyticsEvents.GIT_RESET.getEventName());
|
||||||
|
return Mono.fromCallable(() -> {
|
||||||
|
// Remove tracked files
|
||||||
|
Ref ref = git.reset().setMode(ResetCommand.ResetType.HARD).call();
|
||||||
|
// Remove untracked files
|
||||||
|
git.clean().setForce(true).setCleanDirectories(true).call();
|
||||||
|
processStopwatch.stopAndLogTimeInMillis();
|
||||||
|
return ref;
|
||||||
|
})
|
||||||
|
.timeout(Duration.ofMillis(Constraint.TIMEOUT_MILLIS))
|
||||||
|
.subscribeOn(scheduler);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Mono<Boolean> resetToLastCommit(Path repoSuffix, String branchName) throws GitAPIException, IOException {
|
||||||
|
try (Git git = Git.open(createRepoPath(repoSuffix).toFile())) {
|
||||||
|
return this.resetToLastCommit(git)
|
||||||
|
.flatMap(ref -> checkoutToBranch(repoSuffix, branchName))
|
||||||
|
.flatMap(checkedOut -> {
|
||||||
|
try {
|
||||||
|
return resetToLastCommit(git).thenReturn(true);
|
||||||
|
} catch (GitAPIException e) {
|
||||||
|
log.error(e.getMessage());
|
||||||
|
return Mono.error(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Mono<Boolean> resetHard(Path repoSuffix, String branchName) {
|
||||||
|
return this.checkoutToBranch(repoSuffix, branchName)
|
||||||
|
.flatMap(aBoolean -> {
|
||||||
|
try (Git git = Git.open(createRepoPath(repoSuffix).toFile())) {
|
||||||
|
Ref ref = git.reset()
|
||||||
|
.setMode(ResetCommand.ResetType.HARD)
|
||||||
|
.setRef("HEAD~1")
|
||||||
|
.call();
|
||||||
|
return Mono.just(true);
|
||||||
|
} catch (GitAPIException | IOException e) {
|
||||||
|
log.error("Error while resetting the commit, {}", e.getMessage());
|
||||||
|
}
|
||||||
|
return Mono.just(false);
|
||||||
|
})
|
||||||
|
.timeout(Duration.ofMillis(Constraint.TIMEOUT_MILLIS))
|
||||||
|
.subscribeOn(scheduler);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Mono<Boolean> rebaseBranch(Path repoSuffix, String branchName) {
|
||||||
|
return this.checkoutToBranch(repoSuffix, branchName)
|
||||||
|
.flatMap(isCheckedOut -> {
|
||||||
|
try (Git git = Git.open(createRepoPath(repoSuffix).toFile())) {
|
||||||
|
RebaseResult result =
|
||||||
|
git.rebase().setUpstream("origin/" + branchName).call();
|
||||||
|
if (result.getStatus().isSuccessful()) {
|
||||||
|
return Mono.just(true);
|
||||||
|
} else {
|
||||||
|
log.error(
|
||||||
|
"Error while rebasing the branch, {}, {}",
|
||||||
|
result.getStatus().name(),
|
||||||
|
result.getConflicts());
|
||||||
|
git.rebase()
|
||||||
|
.setUpstream("origin/" + branchName)
|
||||||
|
.setOperation(RebaseCommand.Operation.ABORT)
|
||||||
|
.call();
|
||||||
|
return Mono.error(new Exception("Error while rebasing the branch, "
|
||||||
|
+ result.getStatus().name()));
|
||||||
|
}
|
||||||
|
} catch (GitAPIException | IOException e) {
|
||||||
|
log.error("Error while rebasing the branch, {}", e.getMessage());
|
||||||
|
return Mono.error(e);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.timeout(Duration.ofMillis(Constraint.TIMEOUT_MILLIS))
|
||||||
|
.subscribeOn(scheduler);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mono<BranchTrackingStatus> getBranchTrackingStatus(Path repoPath, String branchName) {
|
||||||
|
return Mono.fromCallable(() -> {
|
||||||
|
try (Git git = Git.open(repoPath.toFile())) {
|
||||||
|
return BranchTrackingStatus.of(git.getRepository(), branchName);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.timeout(Duration.ofMillis(Constraint.TIMEOUT_MILLIS))
|
||||||
|
.subscribeOn(scheduler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,24 +1,5 @@
|
||||||
package com.appsmith.external.constants;
|
package com.appsmith.external.constants;
|
||||||
|
|
||||||
public class GitConstants {
|
import com.appsmith.external.constants.ce.GitConstantsCE;
|
||||||
// This will be used as a key separator for action and jsobjects name
|
|
||||||
// pageName{{seperator}}entityName this is needed to filter the entities to save in appropriate page directory
|
|
||||||
public static final String NAME_SEPARATOR = "##ENTITY_SEPARATOR##";
|
|
||||||
public static final String PAGE_LIST = "pageList";
|
|
||||||
public static final String CUSTOM_JS_LIB_LIST = "customJSLibList";
|
|
||||||
public static final String ACTION_LIST = "actionList";
|
|
||||||
public static final String ACTION_COLLECTION_LIST = "actionCollectionList";
|
|
||||||
|
|
||||||
public static final String DEFAULT_COMMIT_MESSAGE = "System generated commit, ";
|
public class GitConstants extends GitConstantsCE {}
|
||||||
public static final String EMPTY_COMMIT_ERROR_MESSAGE = "On current branch nothing to commit, working tree clean";
|
|
||||||
public static final String MERGE_CONFLICT_BRANCH_NAME = "_mergeConflict";
|
|
||||||
public static final String CONFLICTED_SUCCESS_MESSAGE = "branch has been created from conflicted state. Please "
|
|
||||||
+ "resolve merge conflicts in remote and pull again";
|
|
||||||
|
|
||||||
public static final String GIT_CONFIG_ERROR =
|
|
||||||
"Unable to find the git configuration, please configure your application "
|
|
||||||
+ "with git to use version control service";
|
|
||||||
|
|
||||||
public static final String GIT_PROFILE_ERROR = "Unable to find git author configuration for logged-in user. You can"
|
|
||||||
+ " set up a git profile from the user profile section.";
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
package com.appsmith.external.constants.ce;
|
||||||
|
|
||||||
|
public class GitConstantsCE {
|
||||||
|
// This will be used as a key separator for action and jsobjects name
|
||||||
|
// pageName{{seperator}}entityName this is needed to filter the entities to save in appropriate page directory
|
||||||
|
public static final String NAME_SEPARATOR = "##ENTITY_SEPARATOR##";
|
||||||
|
public static final String PAGE_LIST = "pageList";
|
||||||
|
public static final String CUSTOM_JS_LIB_LIST = "customJSLibList";
|
||||||
|
public static final String ACTION_LIST = "actionList";
|
||||||
|
public static final String ACTION_COLLECTION_LIST = "actionCollectionList";
|
||||||
|
|
||||||
|
public static final String DEFAULT_COMMIT_MESSAGE = "System generated commit, ";
|
||||||
|
public static final String EMPTY_COMMIT_ERROR_MESSAGE = "On current branch nothing to commit, working tree clean";
|
||||||
|
public static final String MERGE_CONFLICT_BRANCH_NAME = "_mergeConflict";
|
||||||
|
public static final String CONFLICTED_SUCCESS_MESSAGE = "branch has been created from conflicted state. Please "
|
||||||
|
+ "resolve merge conflicts in remote and pull again";
|
||||||
|
|
||||||
|
public static final String GIT_CONFIG_ERROR =
|
||||||
|
"Unable to find the git configuration, please configure your application "
|
||||||
|
+ "with git to use version control service";
|
||||||
|
|
||||||
|
public static final String GIT_PROFILE_ERROR = "Unable to find git author configuration for logged-in user. You can"
|
||||||
|
+ " set up a git profile from the user profile section.";
|
||||||
|
}
|
||||||
|
|
@ -1,57 +1,12 @@
|
||||||
package com.appsmith.external.dtos;
|
package com.appsmith.external.dtos;
|
||||||
|
|
||||||
import com.appsmith.external.constants.Assets;
|
import com.appsmith.external.dtos.ce.GitStatusCE_DTO;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DTO to convey the status local git repo
|
* DTO to convey the status local git repo
|
||||||
*/
|
*/
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
@Data
|
@Data
|
||||||
public class GitStatusDTO {
|
public class GitStatusDTO extends GitStatusCE_DTO {}
|
||||||
|
|
||||||
// Name of modified, added and deleted resources in local git repo
|
|
||||||
Set<String> modified;
|
|
||||||
|
|
||||||
// Name of added resources to local git repo
|
|
||||||
Set<String> added;
|
|
||||||
|
|
||||||
// Name of deleted resources from local git repo
|
|
||||||
Set<String> removed;
|
|
||||||
|
|
||||||
// Name of conflicting resources
|
|
||||||
Set<String> conflicting;
|
|
||||||
|
|
||||||
Boolean isClean;
|
|
||||||
|
|
||||||
// number of modified custom JS libs
|
|
||||||
int modifiedJSLibs;
|
|
||||||
|
|
||||||
// number of modified pages
|
|
||||||
int modifiedPages;
|
|
||||||
|
|
||||||
// number of modified actions
|
|
||||||
int modifiedQueries;
|
|
||||||
|
|
||||||
// number of modified JSObjects
|
|
||||||
int modifiedJSObjects;
|
|
||||||
|
|
||||||
// number of modified JSObjects
|
|
||||||
int modifiedDatasources;
|
|
||||||
|
|
||||||
// number of local commits which are not present in remote repo
|
|
||||||
Integer aheadCount;
|
|
||||||
|
|
||||||
// number of remote commits which are not present in local repo
|
|
||||||
Integer behindCount;
|
|
||||||
|
|
||||||
// Remote tracking branch name
|
|
||||||
String remoteBranch;
|
|
||||||
|
|
||||||
// Documentation url for discard and pull functionality
|
|
||||||
String discardDocUrl = Assets.GIT_DISCARD_DOC_URL;
|
|
||||||
|
|
||||||
// File Format migration
|
|
||||||
String migrationMessage = "";
|
|
||||||
}
|
|
||||||
|
|
|
||||||
57
app/server/appsmith-interfaces/src/main/java/com/appsmith/external/dtos/ce/GitStatusCE_DTO.java
vendored
Normal file
57
app/server/appsmith-interfaces/src/main/java/com/appsmith/external/dtos/ce/GitStatusCE_DTO.java
vendored
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
package com.appsmith.external.dtos.ce;
|
||||||
|
|
||||||
|
import com.appsmith.external.constants.Assets;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DTO to convey the status local git repo
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class GitStatusCE_DTO {
|
||||||
|
|
||||||
|
// Name of modified, added and deleted resources in local git repo
|
||||||
|
Set<String> modified;
|
||||||
|
|
||||||
|
// Name of added resources to local git repo
|
||||||
|
Set<String> added;
|
||||||
|
|
||||||
|
// Name of deleted resources from local git repo
|
||||||
|
Set<String> removed;
|
||||||
|
|
||||||
|
// Name of conflicting resources
|
||||||
|
Set<String> conflicting;
|
||||||
|
|
||||||
|
Boolean isClean;
|
||||||
|
|
||||||
|
// number of modified custom JS libs
|
||||||
|
int modifiedJSLibs;
|
||||||
|
|
||||||
|
// number of modified pages
|
||||||
|
int modifiedPages;
|
||||||
|
|
||||||
|
// number of modified actions
|
||||||
|
int modifiedQueries;
|
||||||
|
|
||||||
|
// number of modified JSObjects
|
||||||
|
int modifiedJSObjects;
|
||||||
|
|
||||||
|
// number of modified JSObjects
|
||||||
|
int modifiedDatasources;
|
||||||
|
|
||||||
|
// number of local commits which are not present in remote repo
|
||||||
|
Integer aheadCount;
|
||||||
|
|
||||||
|
// number of remote commits which are not present in local repo
|
||||||
|
Integer behindCount;
|
||||||
|
|
||||||
|
// Remote tracking branch name
|
||||||
|
String remoteBranch;
|
||||||
|
|
||||||
|
// Documentation url for discard and pull functionality
|
||||||
|
String discardDocUrl = Assets.GIT_DISCARD_DOC_URL;
|
||||||
|
|
||||||
|
// File Format migration
|
||||||
|
String migrationMessage = "";
|
||||||
|
}
|
||||||
|
|
@ -1,36 +1,15 @@
|
||||||
package com.appsmith.external.models;
|
package com.appsmith.external.models;
|
||||||
|
|
||||||
|
import com.appsmith.external.models.ce.ApplicationGitReferenceCE;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A DTO class to hold complete information about an application, which will then be serialized to a file so as to
|
* A DTO class to hold complete information about an application, which will then be serialized to a file so as to
|
||||||
* export/save that application into a json files.
|
* export/save that application into a json files.
|
||||||
*/
|
*/
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
@Data
|
@Data
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
public class ApplicationGitReference {
|
public class ApplicationGitReference extends ApplicationGitReferenceCE {}
|
||||||
|
|
||||||
Object application;
|
|
||||||
Object metadata;
|
|
||||||
Object theme;
|
|
||||||
Map<String, Object> actions;
|
|
||||||
Map<String, String> actionBody;
|
|
||||||
Map<String, Object> actionCollections;
|
|
||||||
Map<String, String> actionCollectionBody;
|
|
||||||
Map<String, Object> pages;
|
|
||||||
Map<String, String> pageDsl;
|
|
||||||
Map<String, Object> datasources;
|
|
||||||
Map<String, Object> jsLibraries;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This field will be used to store map of files to be updated in local file system by comparing the recent
|
|
||||||
* changes in database and the last local git commit.
|
|
||||||
* This field can be used while saving resources to local file system and only update the resource files which
|
|
||||||
* are updated in the database.
|
|
||||||
*/
|
|
||||||
Map<String, Set<String>> updatedResources;
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,37 +1,13 @@
|
||||||
package com.appsmith.external.models;
|
package com.appsmith.external.models;
|
||||||
|
|
||||||
|
import com.appsmith.external.models.ce.DefaultResourcesCE;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class will be used for connecting resources across branches for git connected application
|
* This class will be used for connecting resources across branches for git connected application
|
||||||
* e.g. Page1 in branch1 will have the same defaultResources.pageId as of Page1 of branch2
|
* e.g. Page1 in branch1 will have the same defaultResources.pageId as of Page1 of branch2
|
||||||
*/
|
*/
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
@Data
|
@Data
|
||||||
public class DefaultResources {
|
public class DefaultResources extends DefaultResourcesCE {}
|
||||||
/**
|
|
||||||
* When present, actionId will hold the default action id
|
|
||||||
*/
|
|
||||||
String actionId;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* When present, applicationId will hold the default application id
|
|
||||||
*/
|
|
||||||
String applicationId;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* When present, pageId will hold the default page id
|
|
||||||
*/
|
|
||||||
String pageId;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* When present, collectionId will hold the default collection id
|
|
||||||
*/
|
|
||||||
String collectionId;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* When present, branchName will hold the current branch name.
|
|
||||||
* For example, if we've a page in both main and develop branch, then default resources of those two pages will
|
|
||||||
* have same applicationId, pageId but branchName will contain the corresponding branch name.
|
|
||||||
*/
|
|
||||||
String branchName;
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -171,8 +171,7 @@ public class ActionCE_DTO implements Identifiable, Executable {
|
||||||
DefaultResources defaultResources;
|
DefaultResources defaultResources;
|
||||||
|
|
||||||
// This field will be used to store analytics data related to this specific domain object. It's been introduced in
|
// This field will be used to store analytics data related to this specific domain object. It's been introduced in
|
||||||
// order to track
|
// order to track success metrics of modules. Learn more on GitHub issue#24734
|
||||||
// success metrics of modules. Learn more on GitHub issue#24734
|
|
||||||
@JsonView(Views.Public.class)
|
@JsonView(Views.Public.class)
|
||||||
AnalyticsInfo eventData;
|
AnalyticsInfo eventData;
|
||||||
|
|
||||||
|
|
@ -216,6 +215,7 @@ public class ActionCE_DTO implements Identifiable, Executable {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sanitiseToExportDBObject() {
|
public void sanitiseToExportDBObject() {
|
||||||
|
this.resetTransientFields();
|
||||||
this.setEventData(null);
|
this.setEventData(null);
|
||||||
this.setDefaultResources(null);
|
this.setDefaultResources(null);
|
||||||
this.setCacheResponse(null);
|
this.setCacheResponse(null);
|
||||||
|
|
@ -302,4 +302,19 @@ public class ActionCE_DTO implements Identifiable, Executable {
|
||||||
this.datasource.setIsAutoGenerated(true);
|
this.datasource.setIsAutoGenerated(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void resetTransientFields() {
|
||||||
|
this.setId(null);
|
||||||
|
this.setApplicationId(null);
|
||||||
|
this.setWorkspaceId(null);
|
||||||
|
this.setPluginId(null);
|
||||||
|
this.setPluginName(null);
|
||||||
|
this.setPluginType(null);
|
||||||
|
this.setErrorReports(null);
|
||||||
|
// this.setMessages(null);
|
||||||
|
this.setTemplateId(null);
|
||||||
|
this.setProvider(null);
|
||||||
|
this.setProviderId(null);
|
||||||
|
this.setDocumentation(null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
package com.appsmith.external.models.ce;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A DTO class to hold complete information about an application, which will then be serialized to a file so as to
|
||||||
|
* export/save that application into a json files.
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class ApplicationGitReferenceCE {
|
||||||
|
|
||||||
|
Object application;
|
||||||
|
Object metadata;
|
||||||
|
Object theme;
|
||||||
|
Map<String, Object> actions;
|
||||||
|
Map<String, String> actionBody;
|
||||||
|
Map<String, Object> actionCollections;
|
||||||
|
Map<String, String> actionCollectionBody;
|
||||||
|
Map<String, Object> pages;
|
||||||
|
Map<String, String> pageDsl;
|
||||||
|
Map<String, Object> datasources;
|
||||||
|
Map<String, Object> jsLibraries;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This field will be used to store map of files to be updated in local file system by comparing the recent
|
||||||
|
* changes in database and the last local git commit.
|
||||||
|
* This field can be used while saving resources to local file system and only update the resource files which
|
||||||
|
* are updated in the database.
|
||||||
|
*/
|
||||||
|
Map<String, Set<String>> updatedResources;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
package com.appsmith.external.models.ce;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class will be used for connecting resources across branches for git connected application
|
||||||
|
* e.g. Page1 in branch1 will have the same defaultResources.pageId as of Page1 of branch2
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class DefaultResourcesCE {
|
||||||
|
/**
|
||||||
|
* When present, actionId will hold the default action id
|
||||||
|
*/
|
||||||
|
String actionId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When present, applicationId will hold the default application id
|
||||||
|
*/
|
||||||
|
String applicationId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When present, pageId will hold the default page id
|
||||||
|
*/
|
||||||
|
String pageId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When present, collectionId will hold the default collection id
|
||||||
|
*/
|
||||||
|
String collectionId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When present, branchName will hold the current branch name.
|
||||||
|
* For example, if we've a page in both main and develop branch, then default resources of those two pages will
|
||||||
|
* have same applicationId, pageId but branchName will contain the corresponding branch name.
|
||||||
|
*/
|
||||||
|
String branchName;
|
||||||
|
}
|
||||||
|
|
@ -9,6 +9,7 @@ import com.appsmith.server.acl.AclPermission;
|
||||||
import com.appsmith.server.acl.PolicyGenerator;
|
import com.appsmith.server.acl.PolicyGenerator;
|
||||||
import com.appsmith.server.applications.base.ApplicationService;
|
import com.appsmith.server.applications.base.ApplicationService;
|
||||||
import com.appsmith.server.constants.FieldName;
|
import com.appsmith.server.constants.FieldName;
|
||||||
|
import com.appsmith.server.defaultresources.DefaultResourcesService;
|
||||||
import com.appsmith.server.domains.Action;
|
import com.appsmith.server.domains.Action;
|
||||||
import com.appsmith.server.domains.ActionCollection;
|
import com.appsmith.server.domains.ActionCollection;
|
||||||
import com.appsmith.server.domains.NewAction;
|
import com.appsmith.server.domains.NewAction;
|
||||||
|
|
@ -29,7 +30,6 @@ import com.appsmith.server.solutions.ApplicationPermission;
|
||||||
import jakarta.validation.Validator;
|
import jakarta.validation.Validator;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.lang3.ObjectUtils;
|
import org.apache.commons.lang3.ObjectUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
import org.bson.types.ObjectId;
|
import org.bson.types.ObjectId;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.data.domain.Sort;
|
import org.springframework.data.domain.Sort;
|
||||||
|
|
@ -38,6 +38,7 @@ import org.springframework.data.mongodb.core.convert.MongoConverter;
|
||||||
import org.springframework.util.CollectionUtils;
|
import org.springframework.util.CollectionUtils;
|
||||||
import org.springframework.util.LinkedMultiValueMap;
|
import org.springframework.util.LinkedMultiValueMap;
|
||||||
import org.springframework.util.MultiValueMap;
|
import org.springframework.util.MultiValueMap;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
import reactor.core.publisher.Flux;
|
import reactor.core.publisher.Flux;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
import reactor.core.scheduler.Scheduler;
|
import reactor.core.scheduler.Scheduler;
|
||||||
|
|
@ -66,6 +67,7 @@ public class ActionCollectionServiceCEImpl extends BaseService<ActionCollectionR
|
||||||
private final ResponseUtils responseUtils;
|
private final ResponseUtils responseUtils;
|
||||||
private final ApplicationPermission applicationPermission;
|
private final ApplicationPermission applicationPermission;
|
||||||
private final ActionPermission actionPermission;
|
private final ActionPermission actionPermission;
|
||||||
|
private final DefaultResourcesService<ActionCollection> defaultResourcesService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public ActionCollectionServiceCEImpl(
|
public ActionCollectionServiceCEImpl(
|
||||||
|
|
@ -80,7 +82,8 @@ public class ActionCollectionServiceCEImpl extends BaseService<ActionCollectionR
|
||||||
ApplicationService applicationService,
|
ApplicationService applicationService,
|
||||||
ResponseUtils responseUtils,
|
ResponseUtils responseUtils,
|
||||||
ApplicationPermission applicationPermission,
|
ApplicationPermission applicationPermission,
|
||||||
ActionPermission actionPermission) {
|
ActionPermission actionPermission,
|
||||||
|
DefaultResourcesService<ActionCollection> defaultResourcesService) {
|
||||||
|
|
||||||
super(scheduler, validator, mongoConverter, reactiveMongoTemplate, repository, analyticsService);
|
super(scheduler, validator, mongoConverter, reactiveMongoTemplate, repository, analyticsService);
|
||||||
this.newActionService = newActionService;
|
this.newActionService = newActionService;
|
||||||
|
|
@ -89,6 +92,7 @@ public class ActionCollectionServiceCEImpl extends BaseService<ActionCollectionR
|
||||||
this.responseUtils = responseUtils;
|
this.responseUtils = responseUtils;
|
||||||
this.applicationPermission = applicationPermission;
|
this.applicationPermission = applicationPermission;
|
||||||
this.actionPermission = actionPermission;
|
this.actionPermission = actionPermission;
|
||||||
|
this.defaultResourcesService = defaultResourcesService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -333,9 +337,10 @@ public class ActionCollectionServiceCEImpl extends BaseService<ActionCollectionR
|
||||||
copyNewFieldValuesIntoOldObject(actionCollectionDTO, dbActionCollection.getUnpublishedCollection());
|
copyNewFieldValuesIntoOldObject(actionCollectionDTO, dbActionCollection.getUnpublishedCollection());
|
||||||
// No need to save defaultPageId at actionCollection level as this will be stored inside the
|
// No need to save defaultPageId at actionCollection level as this will be stored inside the
|
||||||
// actionCollectionDTO
|
// actionCollectionDTO
|
||||||
DefaultResourcesUtils.createDefaultIdsOrUpdateWithGivenResourceIds(
|
defaultResourcesService.initialize(
|
||||||
dbActionCollection,
|
dbActionCollection,
|
||||||
dbActionCollection.getDefaultResources().getBranchName());
|
dbActionCollection.getDefaultResources().getBranchName(),
|
||||||
|
false);
|
||||||
return dbActionCollection;
|
return dbActionCollection;
|
||||||
})
|
})
|
||||||
.flatMap(actionCollection -> this.update(id, actionCollection))
|
.flatMap(actionCollection -> this.update(id, actionCollection))
|
||||||
|
|
@ -492,7 +497,14 @@ public class ActionCollectionServiceCEImpl extends BaseService<ActionCollectionR
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Flux<ActionCollection> findByPageIdsForExport(List<String> pageIds, Optional<AclPermission> permission) {
|
public Flux<ActionCollection> findByPageIdsForExport(List<String> pageIds, Optional<AclPermission> permission) {
|
||||||
return repository.findByPageIds(pageIds, permission);
|
return repository.findByPageIds(pageIds, permission).doOnNext(actionCollection -> {
|
||||||
|
actionCollection.getUnpublishedCollection().populateTransientFields(actionCollection);
|
||||||
|
if (actionCollection.getPublishedCollection() != null
|
||||||
|
&& StringUtils.hasText(
|
||||||
|
actionCollection.getPublishedCollection().getName())) {
|
||||||
|
actionCollection.getPublishedCollection().populateTransientFields(actionCollection);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@ package com.appsmith.server.actioncollections.base;
|
||||||
|
|
||||||
import com.appsmith.server.acl.PolicyGenerator;
|
import com.appsmith.server.acl.PolicyGenerator;
|
||||||
import com.appsmith.server.applications.base.ApplicationService;
|
import com.appsmith.server.applications.base.ApplicationService;
|
||||||
|
import com.appsmith.server.defaultresources.DefaultResourcesService;
|
||||||
|
import com.appsmith.server.domains.ActionCollection;
|
||||||
import com.appsmith.server.helpers.ResponseUtils;
|
import com.appsmith.server.helpers.ResponseUtils;
|
||||||
import com.appsmith.server.newactions.base.NewActionService;
|
import com.appsmith.server.newactions.base.NewActionService;
|
||||||
import com.appsmith.server.repositories.ActionCollectionRepository;
|
import com.appsmith.server.repositories.ActionCollectionRepository;
|
||||||
|
|
@ -31,7 +33,8 @@ public class ActionCollectionServiceImpl extends ActionCollectionServiceCEImpl i
|
||||||
ApplicationService applicationService,
|
ApplicationService applicationService,
|
||||||
ResponseUtils responseUtils,
|
ResponseUtils responseUtils,
|
||||||
ApplicationPermission applicationPermission,
|
ApplicationPermission applicationPermission,
|
||||||
ActionPermission actionPermission) {
|
ActionPermission actionPermission,
|
||||||
|
DefaultResourcesService<ActionCollection> defaultResourcesService) {
|
||||||
super(
|
super(
|
||||||
scheduler,
|
scheduler,
|
||||||
validator,
|
validator,
|
||||||
|
|
@ -44,6 +47,7 @@ public class ActionCollectionServiceImpl extends ActionCollectionServiceCEImpl i
|
||||||
applicationService,
|
applicationService,
|
||||||
responseUtils,
|
responseUtils,
|
||||||
applicationPermission,
|
applicationPermission,
|
||||||
actionPermission);
|
actionPermission,
|
||||||
|
defaultResourcesService);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
package com.appsmith.server.actioncollections.defaultresources;
|
||||||
|
|
||||||
|
import com.appsmith.external.models.DefaultResources;
|
||||||
|
import com.appsmith.server.defaultresources.DefaultResourcesServiceCE;
|
||||||
|
import com.appsmith.server.dtos.ActionCollectionDTO;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class ActionCollectionDTODefaultResourcesServiceCEImpl
|
||||||
|
implements DefaultResourcesServiceCE<ActionCollectionDTO> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ActionCollectionDTO initialize(
|
||||||
|
ActionCollectionDTO domainObject, String branchName, boolean resetExistingValues) {
|
||||||
|
DefaultResources existingDefaultResources = domainObject.getDefaultResources();
|
||||||
|
DefaultResources defaultResources = new DefaultResources();
|
||||||
|
|
||||||
|
String defaultPageId = domainObject.getPageId();
|
||||||
|
|
||||||
|
if (existingDefaultResources != null && !resetExistingValues) {
|
||||||
|
// Check if there are properties to be copied over from existing
|
||||||
|
if (StringUtils.hasText(existingDefaultResources.getPageId())) {
|
||||||
|
defaultPageId = existingDefaultResources.getPageId();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultResources.setPageId(defaultPageId);
|
||||||
|
|
||||||
|
domainObject.setDefaultResources(defaultResources);
|
||||||
|
return domainObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ActionCollectionDTO setFromOtherBranch(
|
||||||
|
ActionCollectionDTO domainObject, ActionCollectionDTO defaultDomainObject, String branchName) {
|
||||||
|
DefaultResources defaultResources = new DefaultResources();
|
||||||
|
|
||||||
|
defaultResources.setPageId(defaultDomainObject.getDefaultResources().getPageId());
|
||||||
|
|
||||||
|
domainObject.setDefaultResources(defaultResources);
|
||||||
|
return domainObject;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
package com.appsmith.server.actioncollections.defaultresources;
|
||||||
|
|
||||||
|
import com.appsmith.server.defaultresources.DefaultResourcesService;
|
||||||
|
import com.appsmith.server.dtos.ActionCollectionDTO;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class ActionCollectionDTODefaultResourcesServiceImpl extends ActionCollectionDTODefaultResourcesServiceCEImpl
|
||||||
|
implements DefaultResourcesService<ActionCollectionDTO> {}
|
||||||
|
|
@ -0,0 +1,56 @@
|
||||||
|
package com.appsmith.server.actioncollections.defaultresources;
|
||||||
|
|
||||||
|
import com.appsmith.external.models.DefaultResources;
|
||||||
|
import com.appsmith.server.defaultresources.DefaultResourcesServiceCE;
|
||||||
|
import com.appsmith.server.domains.ActionCollection;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class ActionCollectionDefaultResourcesServiceCEImpl implements DefaultResourcesServiceCE<ActionCollection> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ActionCollection initialize(ActionCollection domainObject, String branchName, boolean resetExistingValues) {
|
||||||
|
DefaultResources existingDefaultResources = domainObject.getDefaultResources();
|
||||||
|
DefaultResources defaultResources = new DefaultResources();
|
||||||
|
|
||||||
|
String defaultApplicationId = domainObject.getApplicationId();
|
||||||
|
String defaultCollectionId = domainObject.getId();
|
||||||
|
|
||||||
|
if (existingDefaultResources != null && !resetExistingValues) {
|
||||||
|
// Check if there are properties to be copied over from existing
|
||||||
|
if (StringUtils.hasText(existingDefaultResources.getApplicationId())) {
|
||||||
|
defaultApplicationId = existingDefaultResources.getApplicationId();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StringUtils.hasText(existingDefaultResources.getCollectionId())) {
|
||||||
|
defaultCollectionId = existingDefaultResources.getCollectionId();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultResources.setCollectionId(defaultCollectionId);
|
||||||
|
defaultResources.setApplicationId(defaultApplicationId);
|
||||||
|
defaultResources.setBranchName(branchName);
|
||||||
|
|
||||||
|
domainObject.setDefaultResources(defaultResources);
|
||||||
|
return domainObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ActionCollection setFromOtherBranch(
|
||||||
|
ActionCollection domainObject, ActionCollection branchedDomainObject, String branchName) {
|
||||||
|
DefaultResources defaultResources = domainObject.getDefaultResources();
|
||||||
|
if (defaultResources == null) {
|
||||||
|
defaultResources = new DefaultResources();
|
||||||
|
}
|
||||||
|
|
||||||
|
DefaultResources otherDefaultResources = branchedDomainObject.getDefaultResources();
|
||||||
|
|
||||||
|
defaultResources.setCollectionId(otherDefaultResources.getCollectionId());
|
||||||
|
defaultResources.setApplicationId(otherDefaultResources.getApplicationId());
|
||||||
|
defaultResources.setBranchName(branchName);
|
||||||
|
|
||||||
|
domainObject.setDefaultResources(defaultResources);
|
||||||
|
return domainObject;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
package com.appsmith.server.actioncollections.defaultresources;
|
||||||
|
|
||||||
|
import com.appsmith.server.defaultresources.DefaultResourcesService;
|
||||||
|
import com.appsmith.server.domains.ActionCollection;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class ActionCollectionDefaultResourcesServiceImpl extends ActionCollectionDefaultResourcesServiceCEImpl
|
||||||
|
implements DefaultResourcesService<ActionCollection> {}
|
||||||
|
|
@ -4,6 +4,7 @@ import com.appsmith.external.models.DefaultResources;
|
||||||
import com.appsmith.external.models.Policy;
|
import com.appsmith.external.models.Policy;
|
||||||
import com.appsmith.server.actioncollections.base.ActionCollectionService;
|
import com.appsmith.server.actioncollections.base.ActionCollectionService;
|
||||||
import com.appsmith.server.constants.FieldName;
|
import com.appsmith.server.constants.FieldName;
|
||||||
|
import com.appsmith.server.defaultresources.DefaultResourcesService;
|
||||||
import com.appsmith.server.domains.ActionCollection;
|
import com.appsmith.server.domains.ActionCollection;
|
||||||
import com.appsmith.server.domains.Application;
|
import com.appsmith.server.domains.Application;
|
||||||
import com.appsmith.server.domains.NewPage;
|
import com.appsmith.server.domains.NewPage;
|
||||||
|
|
@ -23,6 +24,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.collections.CollectionUtils;
|
import org.apache.commons.collections.CollectionUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.bson.types.ObjectId;
|
import org.bson.types.ObjectId;
|
||||||
|
import reactor.core.publisher.Flux;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
@ -39,6 +41,8 @@ import static com.appsmith.external.helpers.AppsmithBeanUtils.copyNestedNonNullP
|
||||||
public class ActionCollectionImportableServiceCEImpl implements ImportableServiceCE<ActionCollection> {
|
public class ActionCollectionImportableServiceCEImpl implements ImportableServiceCE<ActionCollection> {
|
||||||
private final ActionCollectionService actionCollectionService;
|
private final ActionCollectionService actionCollectionService;
|
||||||
private final ActionCollectionRepository repository;
|
private final ActionCollectionRepository repository;
|
||||||
|
private final DefaultResourcesService<ActionCollection> defaultResourcesService;
|
||||||
|
private final DefaultResourcesService<ActionCollectionDTO> dtoDefaultResourcesService;
|
||||||
|
|
||||||
// Requires pageNameMap, pageNameToOldNameMap, pluginMap and actionResultDTO to be present in importable resources.
|
// Requires pageNameMap, pageNameToOldNameMap, pluginMap and actionResultDTO to be present in importable resources.
|
||||||
// Updates actionCollectionResultDTO in importable resources.
|
// Updates actionCollectionResultDTO in importable resources.
|
||||||
|
|
@ -135,17 +139,16 @@ public class ActionCollectionImportableServiceCEImpl implements ImportableServic
|
||||||
final String workspaceId = importedApplication.getWorkspaceId();
|
final String workspaceId = importedApplication.getWorkspaceId();
|
||||||
|
|
||||||
// Map of gitSyncId to actionCollection of the existing records in DB
|
// Map of gitSyncId to actionCollection of the existing records in DB
|
||||||
Mono<Map<String, ActionCollection>> actionCollectionsInCurrentAppMono = repository
|
Mono<Map<String, ActionCollection>> actionCollectionsInCurrentAppMono =
|
||||||
.findByApplicationId(importedApplication.getId())
|
getCollectionsInCurrentAppFlux(importedApplication)
|
||||||
.filter(collection -> collection.getGitSyncId() != null)
|
.filter(collection -> collection.getGitSyncId() != null)
|
||||||
.collectMap(ActionCollection::getGitSyncId);
|
.collectMap(ActionCollection::getGitSyncId);
|
||||||
|
|
||||||
Mono<Map<String, ActionCollection>> actionCollectionsInBranchesMono;
|
Mono<Map<String, ActionCollection>> actionCollectionsInBranchesMono;
|
||||||
if (importedApplication.getGitApplicationMetadata() != null) {
|
if (importedApplication.getGitApplicationMetadata() != null) {
|
||||||
final String defaultApplicationId =
|
final String defaultApplicationId =
|
||||||
importedApplication.getGitApplicationMetadata().getDefaultApplicationId();
|
importedApplication.getGitApplicationMetadata().getDefaultApplicationId();
|
||||||
actionCollectionsInBranchesMono = repository
|
actionCollectionsInBranchesMono = getCollectionsInOtherBranchesFlux(defaultApplicationId)
|
||||||
.findByDefaultApplicationId(defaultApplicationId, Optional.empty())
|
|
||||||
.filter(actionCollection -> actionCollection.getGitSyncId() != null)
|
.filter(actionCollection -> actionCollection.getGitSyncId() != null)
|
||||||
.collectMap(ActionCollection::getGitSyncId);
|
.collectMap(ActionCollection::getGitSyncId);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -215,6 +218,37 @@ public class ActionCollectionImportableServiceCEImpl implements ImportableServic
|
||||||
actionCollection.setWorkspaceId(workspaceId);
|
actionCollection.setWorkspaceId(workspaceId);
|
||||||
actionCollection.setApplicationId(importedApplication.getId());
|
actionCollection.setApplicationId(importedApplication.getId());
|
||||||
|
|
||||||
|
if (importedApplication.getGitApplicationMetadata() != null) {
|
||||||
|
final String defaultApplicationId = importedApplication
|
||||||
|
.getGitApplicationMetadata()
|
||||||
|
.getDefaultApplicationId();
|
||||||
|
if (actionsCollectionsInBranches.containsKey(actionCollection.getGitSyncId())) {
|
||||||
|
ActionCollection branchedActionCollection =
|
||||||
|
getExistingCollectionForImportedCollection(
|
||||||
|
mappedImportableResourcesDTO,
|
||||||
|
actionsCollectionsInBranches,
|
||||||
|
actionCollection);
|
||||||
|
defaultResourcesService.setFromOtherBranch(
|
||||||
|
actionCollection,
|
||||||
|
branchedActionCollection,
|
||||||
|
importingMetaDTO.getBranchName());
|
||||||
|
dtoDefaultResourcesService.setFromOtherBranch(
|
||||||
|
actionCollection.getUnpublishedCollection(),
|
||||||
|
branchedActionCollection.getUnpublishedCollection(),
|
||||||
|
importingMetaDTO.getBranchName());
|
||||||
|
} else {
|
||||||
|
defaultResourcesService.initialize(
|
||||||
|
actionCollection, importingMetaDTO.getBranchName(), false);
|
||||||
|
actionCollection
|
||||||
|
.getDefaultResources()
|
||||||
|
.setApplicationId(defaultApplicationId);
|
||||||
|
dtoDefaultResourcesService.initialize(
|
||||||
|
actionCollection.getUnpublishedCollection(),
|
||||||
|
importingMetaDTO.getBranchName(),
|
||||||
|
false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Check if the action has gitSyncId and if it's already in DB
|
// Check if the action has gitSyncId and if it's already in DB
|
||||||
if (existingAppContainsCollection(
|
if (existingAppContainsCollection(
|
||||||
actionsCollectionsInCurrentApp, actionCollection)) {
|
actionsCollectionsInCurrentApp, actionCollection)) {
|
||||||
|
|
@ -226,26 +260,12 @@ public class ActionCollectionImportableServiceCEImpl implements ImportableServic
|
||||||
actionsCollectionsInCurrentApp,
|
actionsCollectionsInCurrentApp,
|
||||||
actionCollection);
|
actionCollection);
|
||||||
|
|
||||||
Set<Policy> existingPolicy = existingActionCollection.getPolicies();
|
updateExistingCollection(
|
||||||
copyNestedNonNullProperties(actionCollection, existingActionCollection);
|
importingMetaDTO,
|
||||||
|
mappedImportableResourcesDTO,
|
||||||
|
actionCollection,
|
||||||
|
existingActionCollection);
|
||||||
|
|
||||||
populateDomainMappedReferences(
|
|
||||||
mappedImportableResourcesDTO, existingActionCollection);
|
|
||||||
|
|
||||||
// Update branchName
|
|
||||||
existingActionCollection
|
|
||||||
.getDefaultResources()
|
|
||||||
.setBranchName(importingMetaDTO.getBranchName());
|
|
||||||
// Recover the deleted state present in DB from imported actionCollection
|
|
||||||
existingActionCollection
|
|
||||||
.getUnpublishedCollection()
|
|
||||||
.setDeletedAt(actionCollection
|
|
||||||
.getUnpublishedCollection()
|
|
||||||
.getDeletedAt());
|
|
||||||
existingActionCollection.setDeletedAt(actionCollection.getDeletedAt());
|
|
||||||
existingActionCollection.setPolicies(existingPolicy);
|
|
||||||
|
|
||||||
existingActionCollection.updateForBulkWriteOperation();
|
|
||||||
existingActionCollections.add(existingActionCollection);
|
existingActionCollections.add(existingActionCollection);
|
||||||
resultDTO.getSavedActionCollectionIds().add(existingActionCollection.getId());
|
resultDTO.getSavedActionCollectionIds().add(existingActionCollection.getId());
|
||||||
resultDTO
|
resultDTO
|
||||||
|
|
@ -261,29 +281,6 @@ public class ActionCollectionImportableServiceCEImpl implements ImportableServic
|
||||||
parentPage.getId());
|
parentPage.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (importedApplication.getGitApplicationMetadata() != null) {
|
|
||||||
final String defaultApplicationId = importedApplication
|
|
||||||
.getGitApplicationMetadata()
|
|
||||||
.getDefaultApplicationId();
|
|
||||||
if (actionsCollectionsInBranches.containsKey(
|
|
||||||
actionCollection.getGitSyncId())) {
|
|
||||||
ActionCollection branchedActionCollection =
|
|
||||||
getExistingCollectionForImportedCollection(
|
|
||||||
mappedImportableResourcesDTO,
|
|
||||||
actionsCollectionsInBranches,
|
|
||||||
actionCollection);
|
|
||||||
actionCollectionService.populateDefaultResources(
|
|
||||||
actionCollection,
|
|
||||||
branchedActionCollection,
|
|
||||||
importingMetaDTO.getBranchName());
|
|
||||||
} else {
|
|
||||||
DefaultResources defaultResources = new DefaultResources();
|
|
||||||
defaultResources.setApplicationId(defaultApplicationId);
|
|
||||||
defaultResources.setBranchName(importingMetaDTO.getBranchName());
|
|
||||||
actionCollection.setDefaultResources(defaultResources);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// this will generate the id and other auto generated fields e.g. createdAt
|
// this will generate the id and other auto generated fields e.g. createdAt
|
||||||
actionCollection.updateForBulkWriteOperation();
|
actionCollection.updateForBulkWriteOperation();
|
||||||
actionCollectionService.generateAndSetPolicies(parentPage, actionCollection);
|
actionCollectionService.generateAndSetPolicies(parentPage, actionCollection);
|
||||||
|
|
@ -323,6 +320,44 @@ public class ActionCollectionImportableServiceCEImpl implements ImportableServic
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected Flux<ActionCollection> getCollectionsInCurrentAppFlux(Application importedApplication) {
|
||||||
|
return repository.findByApplicationId(importedApplication.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Flux<ActionCollection> getCollectionsInOtherBranchesFlux(String defaultApplicationId) {
|
||||||
|
return repository.findByDefaultApplicationId(defaultApplicationId, Optional.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateExistingCollection(
|
||||||
|
ImportingMetaDTO importingMetaDTO,
|
||||||
|
MappedImportableResourcesDTO mappedImportableResourcesDTO,
|
||||||
|
ActionCollection actionCollection,
|
||||||
|
ActionCollection existingActionCollection) {
|
||||||
|
Set<Policy> existingPolicy = existingActionCollection.getPolicies();
|
||||||
|
|
||||||
|
updateImportableCollectionFromExistingCollection(existingActionCollection, actionCollection);
|
||||||
|
|
||||||
|
copyNestedNonNullProperties(actionCollection, existingActionCollection);
|
||||||
|
|
||||||
|
populateDomainMappedReferences(mappedImportableResourcesDTO, existingActionCollection);
|
||||||
|
|
||||||
|
// Update branchName
|
||||||
|
existingActionCollection.getDefaultResources().setBranchName(importingMetaDTO.getBranchName());
|
||||||
|
// Recover the deleted state present in DB from imported actionCollection
|
||||||
|
existingActionCollection
|
||||||
|
.getUnpublishedCollection()
|
||||||
|
.setDeletedAt(actionCollection.getUnpublishedCollection().getDeletedAt());
|
||||||
|
existingActionCollection.setDeletedAt(actionCollection.getDeletedAt());
|
||||||
|
existingActionCollection.setPolicies(existingPolicy);
|
||||||
|
|
||||||
|
existingActionCollection.updateForBulkWriteOperation();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void updateImportableCollectionFromExistingCollection(
|
||||||
|
ActionCollection existingActionCollection, ActionCollection actionCollection) {
|
||||||
|
// Nothing to update from the existing action collection
|
||||||
|
}
|
||||||
|
|
||||||
protected ActionCollection getExistingCollectionForImportedCollection(
|
protected ActionCollection getExistingCollectionForImportedCollection(
|
||||||
MappedImportableResourcesDTO mappedImportableResourcesDTO,
|
MappedImportableResourcesDTO mappedImportableResourcesDTO,
|
||||||
Map<String, ActionCollection> actionsCollectionsInCurrentApp,
|
Map<String, ActionCollection> actionsCollectionsInCurrentApp,
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
package com.appsmith.server.actioncollections.imports;
|
package com.appsmith.server.actioncollections.imports;
|
||||||
|
|
||||||
import com.appsmith.server.actioncollections.base.ActionCollectionService;
|
import com.appsmith.server.actioncollections.base.ActionCollectionService;
|
||||||
|
import com.appsmith.server.defaultresources.DefaultResourcesService;
|
||||||
import com.appsmith.server.domains.ActionCollection;
|
import com.appsmith.server.domains.ActionCollection;
|
||||||
|
import com.appsmith.server.dtos.ActionCollectionDTO;
|
||||||
import com.appsmith.server.imports.importable.ImportableService;
|
import com.appsmith.server.imports.importable.ImportableService;
|
||||||
import com.appsmith.server.repositories.ActionCollectionRepository;
|
import com.appsmith.server.repositories.ActionCollectionRepository;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
@ -10,7 +12,10 @@ import org.springframework.stereotype.Service;
|
||||||
public class ActionCollectionImportableServiceImpl extends ActionCollectionImportableServiceCEImpl
|
public class ActionCollectionImportableServiceImpl extends ActionCollectionImportableServiceCEImpl
|
||||||
implements ImportableService<ActionCollection> {
|
implements ImportableService<ActionCollection> {
|
||||||
public ActionCollectionImportableServiceImpl(
|
public ActionCollectionImportableServiceImpl(
|
||||||
ActionCollectionService actionCollectionService, ActionCollectionRepository repository) {
|
ActionCollectionService actionCollectionService,
|
||||||
super(actionCollectionService, repository);
|
ActionCollectionRepository repository,
|
||||||
|
DefaultResourcesService<ActionCollection> defaultResourcesService,
|
||||||
|
DefaultResourcesService<ActionCollectionDTO> dtoDefaultResourcesService) {
|
||||||
|
super(actionCollectionService, repository, defaultResourcesService, dtoDefaultResourcesService);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
package com.appsmith.server.defaultresources;
|
||||||
|
|
||||||
|
public interface DefaultResourcesService<T> extends DefaultResourcesServiceCE<T> {}
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
package com.appsmith.server.defaultresources;
|
||||||
|
|
||||||
|
public interface DefaultResourcesServiceCE<T> {
|
||||||
|
|
||||||
|
T initialize(T domainObject, String branchName, boolean resetExistingValues);
|
||||||
|
|
||||||
|
T setFromOtherBranch(T domainObject, T defaultDomainObject, String branchName);
|
||||||
|
}
|
||||||
|
|
@ -139,14 +139,32 @@ public class ActionCollectionCE_DTO {
|
||||||
this.setApplicationId(actionCollection.getApplicationId());
|
this.setApplicationId(actionCollection.getApplicationId());
|
||||||
this.setWorkspaceId(actionCollection.getWorkspaceId());
|
this.setWorkspaceId(actionCollection.getWorkspaceId());
|
||||||
this.setUserPermissions(actionCollection.userPermissions);
|
this.setUserPermissions(actionCollection.userPermissions);
|
||||||
copyNewFieldValuesIntoOldObject(actionCollection.getDefaultResources(), this.getDefaultResources());
|
if (this.getDefaultResources() == null) {
|
||||||
|
this.setDefaultResources(actionCollection.getDefaultResources());
|
||||||
|
} else {
|
||||||
|
copyNewFieldValuesIntoOldObject(actionCollection.getDefaultResources(), this.getDefaultResources());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sanitiseForExport() {
|
public void sanitiseForExport() {
|
||||||
|
this.resetTransientFields();
|
||||||
this.setDefaultResources(null);
|
this.setDefaultResources(null);
|
||||||
this.setDefaultToBranchedActionIdsMap(null);
|
this.setDefaultToBranchedActionIdsMap(null);
|
||||||
this.setDefaultToBranchedArchivedActionIdsMap(null);
|
this.setDefaultToBranchedArchivedActionIdsMap(null);
|
||||||
this.setActionIds(null);
|
this.setActionIds(null);
|
||||||
this.setArchivedActionIds(null);
|
this.setArchivedActionIds(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getUserExecutableName() {
|
||||||
|
return this.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void resetTransientFields() {
|
||||||
|
this.setId(null);
|
||||||
|
this.setWorkspaceId(null);
|
||||||
|
this.setApplicationId(null);
|
||||||
|
this.setErrorReports(null);
|
||||||
|
this.setActions(List.of());
|
||||||
|
this.setArchivedActions(List.of());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,659 +1,28 @@
|
||||||
package com.appsmith.server.helpers;
|
package com.appsmith.server.helpers;
|
||||||
|
|
||||||
import com.appsmith.external.constants.AnalyticsEvents;
|
|
||||||
import com.appsmith.external.git.FileInterface;
|
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.helpers.FileUtilsImpl;
|
import com.appsmith.git.helpers.FileUtilsImpl;
|
||||||
import com.appsmith.server.constants.FieldName;
|
import com.appsmith.server.helpers.ce.GitFileUtilsCE;
|
||||||
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.services.AnalyticsService;
|
import com.appsmith.server.services.AnalyticsService;
|
||||||
import com.appsmith.server.services.SessionUserService;
|
import com.appsmith.server.services.SessionUserService;
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
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.context.annotation.Import;
|
||||||
import org.springframework.stereotype.Component;
|
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.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
|
@Slf4j
|
||||||
@RequiredArgsConstructor
|
|
||||||
@Component
|
@Component
|
||||||
@Import({FileUtilsImpl.class})
|
@Import({FileUtilsImpl.class})
|
||||||
public class GitFileUtils {
|
public class GitFileUtils extends GitFileUtilsCE {
|
||||||
|
|
||||||
private final FileInterface fileUtils;
|
|
||||||
private final AnalyticsService analyticsService;
|
|
||||||
private final SessionUserService sessionUserService;
|
|
||||||
|
|
||||||
private final Gson gson;
|
private final Gson gson;
|
||||||
|
|
||||||
// Number of seconds after lock file is stale
|
public GitFileUtils(
|
||||||
@Value("${appsmith.index.lock.file.time}")
|
FileInterface fileUtils,
|
||||||
public final int INDEX_LOCK_FILE_STALE_TIME = 300;
|
AnalyticsService analyticsService,
|
||||||
|
SessionUserService sessionUserService,
|
||||||
// Only include the application helper fields in metadata object
|
Gson gson) {
|
||||||
private static final Set<String> blockedMetadataFields = Set.of(
|
super(fileUtils, analyticsService, sessionUserService, gson);
|
||||||
EXPORTED_APPLICATION,
|
this.gson = gson;
|
||||||
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<Path> 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<Path> 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<Path> 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<String, Object> 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<Path> 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();
|
|
||||||
|
|
||||||
Application application = applicationJson.getExportedApplication();
|
|
||||||
removeUnwantedFieldsFromApplication(application);
|
|
||||||
// Pass application reference
|
|
||||||
applicationReference.setApplication(applicationJson.getExportedApplication());
|
|
||||||
|
|
||||||
// No need to commit publish mode theme as it leads to conflict resolution at both the places if any
|
|
||||||
applicationJson.setPublishedTheme(null);
|
|
||||||
|
|
||||||
// Pass metadata
|
|
||||||
Iterable<String> keys = getAllFields(applicationJson)
|
|
||||||
.map(Field::getName)
|
|
||||||
.filter(name -> !blockedMetadataFields.contains(name))
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
|
|
||||||
ApplicationJson applicationMetadata = new ApplicationJson();
|
|
||||||
Map<String, Set<String>> updatedResources = applicationJson.getUpdatedResources();
|
|
||||||
applicationJson.setUpdatedResources(null);
|
|
||||||
copyProperties(applicationJson, applicationMetadata, keys);
|
|
||||||
applicationReference.setMetadata(applicationMetadata);
|
|
||||||
|
|
||||||
// Remove internal fields from the themes
|
|
||||||
removeUnwantedFieldsFromBaseDomain(applicationJson.getEditModeTheme());
|
|
||||||
|
|
||||||
applicationReference.setTheme(applicationJson.getEditModeTheme());
|
|
||||||
|
|
||||||
// Insert only active pages which will then be committed to repo as individual file
|
|
||||||
Map<String, Object> resourceMap = new HashMap<>();
|
|
||||||
Map<String, String> resourceMapBody = new HashMap<>();
|
|
||||||
Map<String, String> 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(new HashMap<>(resourceMap));
|
|
||||||
applicationReference.setPageDsl(new HashMap<>(dslBody));
|
|
||||||
resourceMap.clear();
|
|
||||||
resourceMapBody.clear();
|
|
||||||
|
|
||||||
// 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)
|
|
||||||
.forEach(newAction -> {
|
|
||||||
String prefix = newAction.getUnpublishedAction() != null
|
|
||||||
? newAction.getUnpublishedAction().getValidName()
|
|
||||||
+ NAME_SEPARATOR
|
|
||||||
+ newAction.getUnpublishedAction().getPageId()
|
|
||||||
: newAction.getPublishedAction().getValidName()
|
|
||||||
+ NAME_SEPARATOR
|
|
||||||
+ newAction.getPublishedAction().getPageId();
|
|
||||||
removeUnwantedFieldFromAction(newAction);
|
|
||||||
String body = 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 (newAction.getPluginType().toString().equals("REMOTE")
|
|
||||||
&& 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 (newAction.getPluginType().equals(PluginType.JS)) {
|
|
||||||
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(new HashMap<>(resourceMap));
|
|
||||||
applicationReference.setActionBody(new HashMap<>(resourceMapBody));
|
|
||||||
resourceMap.clear();
|
|
||||||
resourceMapBody.clear();
|
|
||||||
|
|
||||||
// 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
|
|
||||||
Map<String, String> resourceMapActionCollectionBody = new HashMap<>();
|
|
||||||
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)
|
|
||||||
.forEach(actionCollection -> {
|
|
||||||
String prefix = actionCollection.getUnpublishedCollection() != null
|
|
||||||
? actionCollection.getUnpublishedCollection().getName()
|
|
||||||
+ NAME_SEPARATOR
|
|
||||||
+ actionCollection
|
|
||||||
.getUnpublishedCollection()
|
|
||||||
.getPageId()
|
|
||||||
: actionCollection.getPublishedCollection().getName()
|
|
||||||
+ NAME_SEPARATOR
|
|
||||||
+ actionCollection.getPublishedCollection().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(new HashMap<>(resourceMap));
|
|
||||||
applicationReference.setActionCollectionBody(new HashMap<>(resourceMapActionCollectionBody));
|
|
||||||
applicationReference.setUpdatedResources(updatedResources);
|
|
||||||
resourceMap.clear();
|
|
||||||
resourceMapActionCollectionBody.clear();
|
|
||||||
|
|
||||||
// Send datasources
|
|
||||||
applicationJson.getDatasourceList().forEach(datasource -> {
|
|
||||||
removeUnwantedFieldsFromDatasource(datasource);
|
|
||||||
resourceMap.put(datasource.getName(), datasource);
|
|
||||||
});
|
|
||||||
applicationReference.setDatasources(new HashMap<>(resourceMap));
|
|
||||||
resourceMap.clear();
|
|
||||||
|
|
||||||
applicationJson.getCustomJSLibList().forEach(jsLib -> {
|
|
||||||
removeUnwantedFieldsFromBaseDomain(jsLib);
|
|
||||||
resourceMap.put(jsLib.getUidString(), jsLib);
|
|
||||||
});
|
|
||||||
applicationReference.setJsLibraries(new HashMap<>(resourceMap));
|
|
||||||
resourceMap.clear();
|
|
||||||
|
|
||||||
return applicationReference;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Stream<Field> getAllFields(ApplicationJson applicationJson) {
|
|
||||||
Class<?> currentType = applicationJson.getClass();
|
|
||||||
|
|
||||||
Set<Class<?>> 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<ApplicationJson> 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<String, Object> 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<ApplicationJson> reconstructApplicationJsonFromGitRepo(
|
|
||||||
String workspaceId, String defaultApplicationId, String repoName, String branchName) {
|
|
||||||
|
|
||||||
Mono<ApplicationGitReference> 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;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private <T> List<T> getApplicationResource(Map<String, Object> resources, Type type) {
|
|
||||||
|
|
||||||
List<T> deserializedResources = new ArrayList<>();
|
|
||||||
if (!CollectionUtils.isNullOrEmpty(resources)) {
|
|
||||||
for (Map.Entry<String, Object> resource : resources.entrySet()) {
|
|
||||||
deserializedResources.add(getApplicationResource(resource.getValue(), type));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return deserializedResources;
|
|
||||||
}
|
|
||||||
|
|
||||||
public <T> 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<Path> 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<Boolean> deleteLocalRepo(Path baseRepoSuffix) {
|
|
||||||
return fileUtils.deleteLocalRepo(baseRepoSuffix);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Mono<Boolean> checkIfDirectoryIsEmpty(Path baseRepoSuffix) throws IOException {
|
|
||||||
return fileUtils
|
|
||||||
.checkIfDirectoryIsEmpty(baseRepoSuffix)
|
|
||||||
.onErrorResume(e -> Mono.error(new AppsmithException(AppsmithError.GIT_FILE_SYSTEM_ERROR, e)));
|
|
||||||
}
|
|
||||||
|
|
||||||
private 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);
|
|
||||||
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);
|
|
||||||
removeUnwantedFieldsFromBaseDomain(actionCollection);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ApplicationJson getApplicationJsonFromGitReference(ApplicationGitReference applicationReference) {
|
|
||||||
ApplicationJson applicationJson = new ApplicationJson();
|
|
||||||
// Extract application data from the json
|
|
||||||
Application application = getApplicationResource(applicationReference.getApplication(), Application.class);
|
|
||||||
applicationJson.setExportedApplication(application);
|
|
||||||
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());
|
|
||||||
|
|
||||||
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<ApplicationPage> applicationPages =
|
|
||||||
new ArrayList<>(application.getPages().size());
|
|
||||||
application
|
|
||||||
.getPages()
|
|
||||||
.forEach(applicationPage ->
|
|
||||||
applicationPages.add(gson.fromJson(gson.toJson(applicationPage), ApplicationPage.class)));
|
|
||||||
application.setPublishedPages(applicationPages);
|
|
||||||
}
|
|
||||||
|
|
||||||
List<CustomJSLib> customJSLibList =
|
|
||||||
getApplicationResource(applicationReference.getJsLibraries(), CustomJSLib.class);
|
|
||||||
|
|
||||||
// remove the duplicate js libraries if there is any
|
|
||||||
List<CustomJSLib> customJSLibListWithoutDuplicates = new ArrayList<>(new HashSet<>(customJSLibList));
|
|
||||||
|
|
||||||
applicationJson.setCustomJSLibList(customJSLibListWithoutDuplicates);
|
|
||||||
|
|
||||||
// Extract pages
|
|
||||||
List<NewPage> 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<String, String> 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);
|
|
||||||
|
|
||||||
// Extract actions
|
|
||||||
if (CollectionUtils.isNullOrEmpty(applicationReference.getActions())) {
|
|
||||||
applicationJson.setActionList(new ArrayList<>());
|
|
||||||
} else {
|
|
||||||
Map<String, String> actionBody = applicationReference.getActionBody();
|
|
||||||
List<NewAction> 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<String, Object> 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract actionCollection
|
|
||||||
if (CollectionUtils.isNullOrEmpty(applicationReference.getActionCollections())) {
|
|
||||||
applicationJson.setActionCollectionList(new ArrayList<>());
|
|
||||||
} else {
|
|
||||||
Map<String, String> actionCollectionBody = applicationReference.getActionCollectionBody();
|
|
||||||
List<ActionCollection> 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract datasources
|
|
||||||
applicationJson.setDatasourceList(
|
|
||||||
getApplicationResource(applicationReference.getDatasources(), DatasourceStorage.class));
|
|
||||||
|
|
||||||
return applicationJson;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Mono<Long> deleteIndexLockFile(Path path) {
|
|
||||||
return fileUtils.deleteIndexLockFile(path, INDEX_LOCK_FILE_STALE_TIME);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,345 +1,9 @@
|
||||||
package com.appsmith.server.helpers;
|
package com.appsmith.server.helpers;
|
||||||
|
|
||||||
import com.appsmith.external.models.ActionDTO;
|
import com.appsmith.server.helpers.ce.ResponseUtilsCE;
|
||||||
import com.appsmith.external.models.DefaultResources;
|
|
||||||
import com.appsmith.server.domains.ActionCollection;
|
|
||||||
import com.appsmith.server.domains.Application;
|
|
||||||
import com.appsmith.server.domains.Layout;
|
|
||||||
import com.appsmith.server.domains.NewAction;
|
|
||||||
import com.appsmith.server.domains.NewPage;
|
|
||||||
import com.appsmith.server.dtos.ActionCollectionDTO;
|
|
||||||
import com.appsmith.server.dtos.ActionCollectionViewDTO;
|
|
||||||
import com.appsmith.server.dtos.ActionViewDTO;
|
|
||||||
import com.appsmith.server.dtos.ApplicationPagesDTO;
|
|
||||||
import com.appsmith.server.dtos.LayoutDTO;
|
|
||||||
import com.appsmith.server.dtos.PageDTO;
|
|
||||||
import com.appsmith.server.dtos.PageNameIdDTO;
|
|
||||||
import com.appsmith.server.exceptions.AppsmithError;
|
|
||||||
import com.appsmith.server.exceptions.AppsmithException;
|
|
||||||
import com.appsmith.server.migrations.JsonSchemaVersions;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.lang.StringUtils;
|
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.util.CollectionUtils;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Component
|
@Component
|
||||||
public class ResponseUtils {
|
public class ResponseUtils extends ResponseUtilsCE {}
|
||||||
|
|
||||||
public PageDTO updatePageDTOWithDefaultResources(PageDTO page) {
|
|
||||||
DefaultResources defaultResourceIds = page.getDefaultResources();
|
|
||||||
if (defaultResourceIds == null
|
|
||||||
|| StringUtils.isEmpty(defaultResourceIds.getApplicationId())
|
|
||||||
|| StringUtils.isEmpty(defaultResourceIds.getPageId())) {
|
|
||||||
|
|
||||||
if (defaultResourceIds == null) {
|
|
||||||
return page;
|
|
||||||
}
|
|
||||||
if (StringUtils.isEmpty(defaultResourceIds.getApplicationId())) {
|
|
||||||
defaultResourceIds.setApplicationId(page.getApplicationId());
|
|
||||||
}
|
|
||||||
if (StringUtils.isEmpty(defaultResourceIds.getPageId())) {
|
|
||||||
defaultResourceIds.setPageId(page.getId());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
page.setApplicationId(defaultResourceIds.getApplicationId());
|
|
||||||
page.setId(defaultResourceIds.getPageId());
|
|
||||||
|
|
||||||
page.getLayouts().stream()
|
|
||||||
.filter(layout -> !CollectionUtils.isEmpty(layout.getLayoutOnLoadActions()))
|
|
||||||
.forEach(layout -> this.updateLayoutWithDefaultResources(layout));
|
|
||||||
return page;
|
|
||||||
}
|
|
||||||
|
|
||||||
public NewPage updateNewPageWithDefaultResources(NewPage newPage) {
|
|
||||||
DefaultResources defaultResourceIds = newPage.getDefaultResources();
|
|
||||||
if (defaultResourceIds == null
|
|
||||||
|| StringUtils.isEmpty(defaultResourceIds.getApplicationId())
|
|
||||||
|| StringUtils.isEmpty(defaultResourceIds.getPageId())) {
|
|
||||||
log.error(
|
|
||||||
"Unable to find default ids for page: {}",
|
|
||||||
newPage.getId(),
|
|
||||||
new AppsmithException(AppsmithError.DEFAULT_RESOURCES_UNAVAILABLE, "page", newPage.getId()));
|
|
||||||
|
|
||||||
if (defaultResourceIds == null) {
|
|
||||||
return newPage;
|
|
||||||
}
|
|
||||||
if (StringUtils.isEmpty(defaultResourceIds.getApplicationId())) {
|
|
||||||
defaultResourceIds.setApplicationId(newPage.getApplicationId());
|
|
||||||
}
|
|
||||||
if (StringUtils.isEmpty(defaultResourceIds.getPageId())) {
|
|
||||||
defaultResourceIds.setPageId(newPage.getId());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
newPage.setId(defaultResourceIds.getPageId());
|
|
||||||
newPage.setApplicationId(defaultResourceIds.getApplicationId());
|
|
||||||
if (newPage.getUnpublishedPage() != null) {
|
|
||||||
newPage.setUnpublishedPage(this.updatePageDTOWithDefaultResources(newPage.getUnpublishedPage()));
|
|
||||||
}
|
|
||||||
if (newPage.getPublishedPage() != null) {
|
|
||||||
newPage.setPublishedPage(this.updatePageDTOWithDefaultResources(newPage.getPublishedPage()));
|
|
||||||
}
|
|
||||||
return newPage;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ApplicationPagesDTO updateApplicationPagesDTOWithDefaultResources(ApplicationPagesDTO applicationPages) {
|
|
||||||
List<PageNameIdDTO> pageNameIdList = applicationPages.getPages();
|
|
||||||
for (PageNameIdDTO page : pageNameIdList) {
|
|
||||||
if (StringUtils.isEmpty(page.getDefaultPageId())) {
|
|
||||||
log.error(
|
|
||||||
"Unable to find default pageId for applicationPage: {}",
|
|
||||||
page.getId(),
|
|
||||||
new AppsmithException(
|
|
||||||
AppsmithError.DEFAULT_RESOURCES_UNAVAILABLE, "applicationPage", page.getId()));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
page.setId(page.getDefaultPageId());
|
|
||||||
}
|
|
||||||
// need to update the application also if it's present
|
|
||||||
if (applicationPages.getApplication() != null) {
|
|
||||||
applicationPages.setApplication(updateApplicationWithDefaultResources(applicationPages.getApplication()));
|
|
||||||
}
|
|
||||||
return applicationPages;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ActionDTO updateActionDTOWithDefaultResources(ActionDTO action) {
|
|
||||||
DefaultResources defaultResourceIds = action.getDefaultResources();
|
|
||||||
if (defaultResourceIds == null
|
|
||||||
|| StringUtils.isEmpty(defaultResourceIds.getApplicationId())
|
|
||||||
|| StringUtils.isEmpty(defaultResourceIds.getPageId())
|
|
||||||
|| StringUtils.isEmpty(defaultResourceIds.getActionId())) {
|
|
||||||
|
|
||||||
if (defaultResourceIds == null) {
|
|
||||||
return action;
|
|
||||||
}
|
|
||||||
if (StringUtils.isEmpty(defaultResourceIds.getApplicationId())) {
|
|
||||||
defaultResourceIds.setApplicationId(action.getApplicationId());
|
|
||||||
}
|
|
||||||
if (StringUtils.isEmpty(defaultResourceIds.getPageId())) {
|
|
||||||
defaultResourceIds.setPageId(action.getPageId());
|
|
||||||
}
|
|
||||||
if (StringUtils.isEmpty(defaultResourceIds.getActionId())) {
|
|
||||||
defaultResourceIds.setActionId(action.getId());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
action.setApplicationId(defaultResourceIds.getApplicationId());
|
|
||||||
action.setPageId(defaultResourceIds.getPageId());
|
|
||||||
action.setId(defaultResourceIds.getActionId());
|
|
||||||
if (!StringUtils.isEmpty(defaultResourceIds.getCollectionId())) {
|
|
||||||
action.setCollectionId(defaultResourceIds.getCollectionId());
|
|
||||||
}
|
|
||||||
return action;
|
|
||||||
}
|
|
||||||
|
|
||||||
public LayoutDTO updateLayoutDTOWithDefaultResources(LayoutDTO layout) {
|
|
||||||
if (!CollectionUtils.isEmpty(layout.getActionUpdates())) {
|
|
||||||
layout.getActionUpdates()
|
|
||||||
.forEach(updateLayoutAction -> updateLayoutAction.setId(updateLayoutAction.getDefaultActionId()));
|
|
||||||
}
|
|
||||||
if (!CollectionUtils.isEmpty(layout.getLayoutOnLoadActions())) {
|
|
||||||
layout.getLayoutOnLoadActions()
|
|
||||||
.forEach(layoutOnLoadAction -> layoutOnLoadAction.forEach(onLoadAction -> {
|
|
||||||
if (!StringUtils.isEmpty(onLoadAction.getDefaultActionId())) {
|
|
||||||
onLoadAction.setId(onLoadAction.getDefaultActionId());
|
|
||||||
}
|
|
||||||
if (!StringUtils.isEmpty(onLoadAction.getDefaultCollectionId())) {
|
|
||||||
onLoadAction.setCollectionId(onLoadAction.getDefaultCollectionId());
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
return layout;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Layout updateLayoutWithDefaultResources(Layout layout) {
|
|
||||||
if (!CollectionUtils.isEmpty(layout.getLayoutOnLoadActions())) {
|
|
||||||
layout.getLayoutOnLoadActions()
|
|
||||||
.forEach(layoutOnLoadAction -> layoutOnLoadAction.forEach(onLoadAction -> {
|
|
||||||
if (!StringUtils.isEmpty(onLoadAction.getDefaultActionId())) {
|
|
||||||
onLoadAction.setId(onLoadAction.getDefaultActionId());
|
|
||||||
}
|
|
||||||
if (!StringUtils.isEmpty(onLoadAction.getDefaultCollectionId())) {
|
|
||||||
onLoadAction.setCollectionId(onLoadAction.getDefaultCollectionId());
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
return layout;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ActionViewDTO updateActionViewDTOWithDefaultResources(ActionViewDTO viewDTO) {
|
|
||||||
DefaultResources defaultResourceIds = viewDTO.getDefaultResources();
|
|
||||||
if (defaultResourceIds == null
|
|
||||||
|| StringUtils.isEmpty(defaultResourceIds.getPageId())
|
|
||||||
|| StringUtils.isEmpty(defaultResourceIds.getActionId())) {
|
|
||||||
|
|
||||||
if (defaultResourceIds == null) {
|
|
||||||
return viewDTO;
|
|
||||||
}
|
|
||||||
if (StringUtils.isEmpty(defaultResourceIds.getPageId())) {
|
|
||||||
defaultResourceIds.setPageId(viewDTO.getPageId());
|
|
||||||
}
|
|
||||||
if (StringUtils.isEmpty(defaultResourceIds.getActionId())) {
|
|
||||||
defaultResourceIds.setActionId(viewDTO.getId());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
viewDTO.setId(defaultResourceIds.getActionId());
|
|
||||||
viewDTO.setPageId(defaultResourceIds.getPageId());
|
|
||||||
return viewDTO;
|
|
||||||
}
|
|
||||||
|
|
||||||
public NewAction updateNewActionWithDefaultResources(NewAction newAction) {
|
|
||||||
DefaultResources defaultResourceIds = newAction.getDefaultResources();
|
|
||||||
if (defaultResourceIds == null
|
|
||||||
|| StringUtils.isEmpty(defaultResourceIds.getApplicationId())
|
|
||||||
|| StringUtils.isEmpty(defaultResourceIds.getActionId())) {
|
|
||||||
log.error(
|
|
||||||
"Unable to find default ids for newAction: {}",
|
|
||||||
newAction.getId(),
|
|
||||||
new AppsmithException(AppsmithError.DEFAULT_RESOURCES_UNAVAILABLE, "newAction", newAction.getId()));
|
|
||||||
|
|
||||||
if (defaultResourceIds == null) {
|
|
||||||
return newAction;
|
|
||||||
}
|
|
||||||
if (StringUtils.isEmpty(defaultResourceIds.getApplicationId())) {
|
|
||||||
defaultResourceIds.setApplicationId(newAction.getApplicationId());
|
|
||||||
}
|
|
||||||
if (StringUtils.isEmpty(defaultResourceIds.getActionId())) {
|
|
||||||
defaultResourceIds.setActionId(newAction.getId());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
newAction.setId(defaultResourceIds.getActionId());
|
|
||||||
newAction.setApplicationId(defaultResourceIds.getApplicationId());
|
|
||||||
if (newAction.getUnpublishedAction() != null) {
|
|
||||||
newAction.setUnpublishedAction(this.updateActionDTOWithDefaultResources(newAction.getUnpublishedAction()));
|
|
||||||
}
|
|
||||||
if (newAction.getPublishedAction() != null) {
|
|
||||||
newAction.setPublishedAction(this.updateActionDTOWithDefaultResources(newAction.getPublishedAction()));
|
|
||||||
}
|
|
||||||
return newAction;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ActionCollection updateActionCollectionWithDefaultResources(ActionCollection actionCollection) {
|
|
||||||
DefaultResources defaultResourceIds = actionCollection.getDefaultResources();
|
|
||||||
if (defaultResourceIds == null
|
|
||||||
|| StringUtils.isEmpty(defaultResourceIds.getApplicationId())
|
|
||||||
|| StringUtils.isEmpty(defaultResourceIds.getCollectionId())) {
|
|
||||||
log.error(
|
|
||||||
"Unable to find default ids for actionCollection: {}",
|
|
||||||
actionCollection.getId(),
|
|
||||||
new AppsmithException(
|
|
||||||
AppsmithError.DEFAULT_RESOURCES_UNAVAILABLE, "actionCollection", actionCollection.getId()));
|
|
||||||
|
|
||||||
if (defaultResourceIds == null) {
|
|
||||||
return actionCollection;
|
|
||||||
}
|
|
||||||
if (StringUtils.isEmpty(defaultResourceIds.getApplicationId())) {
|
|
||||||
defaultResourceIds.setApplicationId(actionCollection.getApplicationId());
|
|
||||||
}
|
|
||||||
if (StringUtils.isEmpty(defaultResourceIds.getCollectionId())) {
|
|
||||||
defaultResourceIds.setCollectionId(actionCollection.getId());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
actionCollection.setId(defaultResourceIds.getCollectionId());
|
|
||||||
actionCollection.setApplicationId(defaultResourceIds.getApplicationId());
|
|
||||||
if (actionCollection.getUnpublishedCollection() != null) {
|
|
||||||
actionCollection.setUnpublishedCollection(
|
|
||||||
this.updateCollectionDTOWithDefaultResources(actionCollection.getUnpublishedCollection()));
|
|
||||||
}
|
|
||||||
if (actionCollection.getPublishedCollection() != null) {
|
|
||||||
actionCollection.setPublishedCollection(
|
|
||||||
this.updateCollectionDTOWithDefaultResources(actionCollection.getPublishedCollection()));
|
|
||||||
}
|
|
||||||
return actionCollection;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ActionCollectionDTO updateCollectionDTOWithDefaultResources(ActionCollectionDTO collection) {
|
|
||||||
DefaultResources defaultResourceIds = collection.getDefaultResources();
|
|
||||||
if (defaultResourceIds == null
|
|
||||||
|| StringUtils.isEmpty(defaultResourceIds.getApplicationId())
|
|
||||||
|| StringUtils.isEmpty(defaultResourceIds.getPageId())
|
|
||||||
|| StringUtils.isEmpty(defaultResourceIds.getCollectionId())) {
|
|
||||||
|
|
||||||
if (defaultResourceIds == null) {
|
|
||||||
return collection;
|
|
||||||
}
|
|
||||||
if (StringUtils.isEmpty(defaultResourceIds.getApplicationId())) {
|
|
||||||
defaultResourceIds.setApplicationId(collection.getApplicationId());
|
|
||||||
}
|
|
||||||
if (StringUtils.isEmpty(defaultResourceIds.getPageId())) {
|
|
||||||
defaultResourceIds.setPageId(collection.getPageId());
|
|
||||||
}
|
|
||||||
if (StringUtils.isEmpty(defaultResourceIds.getCollectionId())) {
|
|
||||||
defaultResourceIds.setCollectionId(collection.getId());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
collection.setApplicationId(defaultResourceIds.getApplicationId());
|
|
||||||
collection.setPageId(defaultResourceIds.getPageId());
|
|
||||||
collection.setId(defaultResourceIds.getCollectionId());
|
|
||||||
|
|
||||||
// Update actions within the collection
|
|
||||||
collection.getActions().forEach(this::updateActionDTOWithDefaultResources);
|
|
||||||
collection.getArchivedActions().forEach(this::updateActionDTOWithDefaultResources);
|
|
||||||
|
|
||||||
return collection;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ActionCollectionViewDTO updateActionCollectionViewDTOWithDefaultResources(ActionCollectionViewDTO viewDTO) {
|
|
||||||
DefaultResources defaultResourceIds = viewDTO.getDefaultResources();
|
|
||||||
if (defaultResourceIds == null
|
|
||||||
|| StringUtils.isEmpty(defaultResourceIds.getPageId())
|
|
||||||
|| StringUtils.isEmpty(defaultResourceIds.getApplicationId())
|
|
||||||
|| StringUtils.isEmpty(defaultResourceIds.getCollectionId())) {
|
|
||||||
|
|
||||||
if (defaultResourceIds == null) {
|
|
||||||
return viewDTO;
|
|
||||||
}
|
|
||||||
if (StringUtils.isEmpty(defaultResourceIds.getApplicationId())) {
|
|
||||||
defaultResourceIds.setApplicationId(viewDTO.getApplicationId());
|
|
||||||
}
|
|
||||||
if (StringUtils.isEmpty(defaultResourceIds.getPageId())) {
|
|
||||||
defaultResourceIds.setPageId(viewDTO.getPageId());
|
|
||||||
}
|
|
||||||
if (StringUtils.isEmpty(defaultResourceIds.getCollectionId())) {
|
|
||||||
defaultResourceIds.setCollectionId(viewDTO.getId());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
viewDTO.setId(defaultResourceIds.getCollectionId());
|
|
||||||
viewDTO.setApplicationId(defaultResourceIds.getApplicationId());
|
|
||||||
viewDTO.setPageId(defaultResourceIds.getPageId());
|
|
||||||
viewDTO.getActions().forEach(this::updateActionDTOWithDefaultResources);
|
|
||||||
return viewDTO;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Application updateApplicationWithDefaultResources(Application application) {
|
|
||||||
if (application.getGitApplicationMetadata() != null
|
|
||||||
&& !StringUtils.isEmpty(application.getGitApplicationMetadata().getDefaultApplicationId())) {
|
|
||||||
application.setId(application.getGitApplicationMetadata().getDefaultApplicationId());
|
|
||||||
}
|
|
||||||
if (!CollectionUtils.isEmpty(application.getPages())) {
|
|
||||||
application.getPages().forEach(page -> {
|
|
||||||
if (!StringUtils.isEmpty(page.getDefaultPageId())) {
|
|
||||||
page.setId(page.getDefaultPageId());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (!CollectionUtils.isEmpty(application.getPublishedPages())) {
|
|
||||||
application.getPublishedPages().forEach(page -> {
|
|
||||||
if (!StringUtils.isEmpty(page.getDefaultPageId())) {
|
|
||||||
page.setId(page.getDefaultPageId());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (application.getClientSchemaVersion() == null
|
|
||||||
|| application.getServerSchemaVersion() == null
|
|
||||||
|| (JsonSchemaVersions.clientVersion.equals(application.getClientSchemaVersion())
|
|
||||||
&& JsonSchemaVersions.serverVersion.equals(application.getServerSchemaVersion()))) {
|
|
||||||
application.setIsAutoUpdate(false);
|
|
||||||
} else {
|
|
||||||
application.setIsAutoUpdate(true);
|
|
||||||
}
|
|
||||||
return application;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,737 @@
|
||||||
|
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.helpers.FileUtilsImpl;
|
||||||
|
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.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.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 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<String> 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<Path> 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<Path> 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<Path> 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<String, Object> 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<Path> 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.setUpdatedResources(applicationJson.getUpdatedResources());
|
||||||
|
|
||||||
|
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<String> keys = getAllFields(applicationJson)
|
||||||
|
.map(Field::getName)
|
||||||
|
.filter(name -> !getBlockedMetadataFields().contains(name))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
ApplicationJson applicationMetadata = new ApplicationJson();
|
||||||
|
applicationJson.setUpdatedResources(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<String, Object> 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<String, Object> 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<String, Object> resourceMap = new HashMap<>();
|
||||||
|
Map<String, String> 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)
|
||||||
|
.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<String, Object> resourceMap = new HashMap<>();
|
||||||
|
Map<String, String> 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<String, Object> resourceMap = new HashMap<>();
|
||||||
|
Map<String, String> 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)
|
||||||
|
.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<Field> getAllFields(ApplicationJson applicationJson) {
|
||||||
|
Class<?> currentType = applicationJson.getClass();
|
||||||
|
|
||||||
|
Set<Class<?>> 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<ApplicationJson> 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<String, Object> 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<ApplicationJson> reconstructApplicationJsonFromGitRepo(
|
||||||
|
String workspaceId, String defaultApplicationId, String repoName, String branchName) {
|
||||||
|
|
||||||
|
Mono<ApplicationGitReference> 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 <T> List<T> getApplicationResource(Map<String, Object> resources, Type type) {
|
||||||
|
|
||||||
|
List<T> deserializedResources = new ArrayList<>();
|
||||||
|
if (!CollectionUtils.isNullOrEmpty(resources)) {
|
||||||
|
for (Map.Entry<String, Object> resource : resources.entrySet()) {
|
||||||
|
deserializedResources.add(getApplicationResource(resource.getValue(), type));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return deserializedResources;
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> 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<Path> 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<Boolean> deleteLocalRepo(Path baseRepoSuffix) {
|
||||||
|
return fileUtils.deleteLocalRepo(baseRepoSuffix);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Mono<Boolean> 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<ApplicationPage> 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<String, String> actionCollectionBody = applicationReference.getActionCollectionBody();
|
||||||
|
List<ActionCollection> 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<String, String> actionBody = applicationReference.getActionBody();
|
||||||
|
List<NewAction> 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<String, Object> 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<CustomJSLib> customJSLibList =
|
||||||
|
getApplicationResource(applicationReference.getJsLibraries(), CustomJSLib.class);
|
||||||
|
|
||||||
|
// remove the duplicate js libraries if there is any
|
||||||
|
List<CustomJSLib> customJSLibListWithoutDuplicates = new ArrayList<>(new HashSet<>(customJSLibList));
|
||||||
|
|
||||||
|
applicationJson.setCustomJSLibList(customJSLibListWithoutDuplicates);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setNewPagesInApplicationJson(
|
||||||
|
ApplicationGitReference applicationReference, ApplicationJson applicationJson) {
|
||||||
|
// Extract pages
|
||||||
|
List<NewPage> 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<String, String> 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<Long> deleteIndexLockFile(Path path) {
|
||||||
|
return fileUtils.deleteIndexLockFile(path, INDEX_LOCK_FILE_STALE_TIME);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,321 @@
|
||||||
|
package com.appsmith.server.helpers.ce;
|
||||||
|
|
||||||
|
import com.appsmith.external.models.ActionDTO;
|
||||||
|
import com.appsmith.external.models.DefaultResources;
|
||||||
|
import com.appsmith.server.domains.ActionCollection;
|
||||||
|
import com.appsmith.server.domains.Application;
|
||||||
|
import com.appsmith.server.domains.Layout;
|
||||||
|
import com.appsmith.server.domains.NewAction;
|
||||||
|
import com.appsmith.server.domains.NewPage;
|
||||||
|
import com.appsmith.server.dtos.ActionCollectionDTO;
|
||||||
|
import com.appsmith.server.dtos.ActionCollectionViewDTO;
|
||||||
|
import com.appsmith.server.dtos.ActionViewDTO;
|
||||||
|
import com.appsmith.server.dtos.ApplicationPagesDTO;
|
||||||
|
import com.appsmith.server.dtos.LayoutDTO;
|
||||||
|
import com.appsmith.server.dtos.PageDTO;
|
||||||
|
import com.appsmith.server.dtos.PageNameIdDTO;
|
||||||
|
import com.appsmith.server.exceptions.AppsmithError;
|
||||||
|
import com.appsmith.server.exceptions.AppsmithException;
|
||||||
|
import com.appsmith.server.migrations.JsonSchemaVersions;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.util.CollectionUtils;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
public class ResponseUtilsCE {
|
||||||
|
|
||||||
|
public PageDTO updatePageDTOWithDefaultResources(PageDTO page) {
|
||||||
|
DefaultResources defaultResourceIds = page.getDefaultResources();
|
||||||
|
if (defaultResourceIds == null
|
||||||
|
|| StringUtils.isEmpty(defaultResourceIds.getApplicationId())
|
||||||
|
|| StringUtils.isEmpty(defaultResourceIds.getPageId())) {
|
||||||
|
|
||||||
|
if (defaultResourceIds == null) {
|
||||||
|
return page;
|
||||||
|
}
|
||||||
|
if (StringUtils.isEmpty(defaultResourceIds.getApplicationId())) {
|
||||||
|
defaultResourceIds.setApplicationId(page.getApplicationId());
|
||||||
|
}
|
||||||
|
if (StringUtils.isEmpty(defaultResourceIds.getPageId())) {
|
||||||
|
defaultResourceIds.setPageId(page.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
page.setApplicationId(defaultResourceIds.getApplicationId());
|
||||||
|
page.setId(defaultResourceIds.getPageId());
|
||||||
|
|
||||||
|
page.getLayouts().stream()
|
||||||
|
.filter(layout -> !CollectionUtils.isEmpty(layout.getLayoutOnLoadActions()))
|
||||||
|
.forEach(layout -> this.updateLayoutWithDefaultResources(layout));
|
||||||
|
return page;
|
||||||
|
}
|
||||||
|
|
||||||
|
public NewPage updateNewPageWithDefaultResources(NewPage newPage) {
|
||||||
|
DefaultResources defaultResourceIds = newPage.getDefaultResources();
|
||||||
|
if (defaultResourceIds == null
|
||||||
|
|| StringUtils.isEmpty(defaultResourceIds.getApplicationId())
|
||||||
|
|| StringUtils.isEmpty(defaultResourceIds.getPageId())) {
|
||||||
|
log.error(
|
||||||
|
"Unable to find default ids for page: {}",
|
||||||
|
newPage.getId(),
|
||||||
|
new AppsmithException(AppsmithError.DEFAULT_RESOURCES_UNAVAILABLE, "page", newPage.getId()));
|
||||||
|
|
||||||
|
if (defaultResourceIds == null) {
|
||||||
|
return newPage;
|
||||||
|
}
|
||||||
|
if (StringUtils.isEmpty(defaultResourceIds.getApplicationId())) {
|
||||||
|
defaultResourceIds.setApplicationId(newPage.getApplicationId());
|
||||||
|
}
|
||||||
|
if (StringUtils.isEmpty(defaultResourceIds.getPageId())) {
|
||||||
|
defaultResourceIds.setPageId(newPage.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
newPage.setId(defaultResourceIds.getPageId());
|
||||||
|
newPage.setApplicationId(defaultResourceIds.getApplicationId());
|
||||||
|
if (newPage.getUnpublishedPage() != null) {
|
||||||
|
newPage.setUnpublishedPage(this.updatePageDTOWithDefaultResources(newPage.getUnpublishedPage()));
|
||||||
|
}
|
||||||
|
if (newPage.getPublishedPage() != null) {
|
||||||
|
newPage.setPublishedPage(this.updatePageDTOWithDefaultResources(newPage.getPublishedPage()));
|
||||||
|
}
|
||||||
|
return newPage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ApplicationPagesDTO updateApplicationPagesDTOWithDefaultResources(ApplicationPagesDTO applicationPages) {
|
||||||
|
List<PageNameIdDTO> pageNameIdList = applicationPages.getPages();
|
||||||
|
for (PageNameIdDTO page : pageNameIdList) {
|
||||||
|
if (StringUtils.isEmpty(page.getDefaultPageId())) {
|
||||||
|
log.error(
|
||||||
|
"Unable to find default pageId for applicationPage: {}",
|
||||||
|
page.getId(),
|
||||||
|
new AppsmithException(
|
||||||
|
AppsmithError.DEFAULT_RESOURCES_UNAVAILABLE, "applicationPage", page.getId()));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
page.setId(page.getDefaultPageId());
|
||||||
|
}
|
||||||
|
// need to update the application also if it's present
|
||||||
|
if (applicationPages.getApplication() != null) {
|
||||||
|
applicationPages.setApplication(updateApplicationWithDefaultResources(applicationPages.getApplication()));
|
||||||
|
}
|
||||||
|
return applicationPages;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ActionDTO updateActionDTOWithDefaultResources(ActionDTO action) {
|
||||||
|
DefaultResources defaultResourceIds = action.getDefaultResources();
|
||||||
|
if (defaultResourceIds == null) {
|
||||||
|
return action;
|
||||||
|
}
|
||||||
|
if (StringUtils.isEmpty(defaultResourceIds.getApplicationId())) {
|
||||||
|
defaultResourceIds.setApplicationId(action.getApplicationId());
|
||||||
|
}
|
||||||
|
if (StringUtils.isEmpty(defaultResourceIds.getPageId())) {
|
||||||
|
defaultResourceIds.setPageId(action.getPageId());
|
||||||
|
}
|
||||||
|
if (StringUtils.isEmpty(defaultResourceIds.getActionId())) {
|
||||||
|
defaultResourceIds.setActionId(action.getId());
|
||||||
|
}
|
||||||
|
action.setApplicationId(defaultResourceIds.getApplicationId());
|
||||||
|
action.setPageId(defaultResourceIds.getPageId());
|
||||||
|
action.setId(defaultResourceIds.getActionId());
|
||||||
|
if (!StringUtils.isEmpty(defaultResourceIds.getCollectionId())) {
|
||||||
|
action.setCollectionId(defaultResourceIds.getCollectionId());
|
||||||
|
}
|
||||||
|
return action;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LayoutDTO updateLayoutDTOWithDefaultResources(LayoutDTO layout) {
|
||||||
|
if (!CollectionUtils.isEmpty(layout.getActionUpdates())) {
|
||||||
|
layout.getActionUpdates()
|
||||||
|
.forEach(updateLayoutAction -> updateLayoutAction.setId(updateLayoutAction.getDefaultActionId()));
|
||||||
|
}
|
||||||
|
if (!CollectionUtils.isEmpty(layout.getLayoutOnLoadActions())) {
|
||||||
|
layout.getLayoutOnLoadActions()
|
||||||
|
.forEach(layoutOnLoadAction -> layoutOnLoadAction.forEach(onLoadAction -> {
|
||||||
|
if (!StringUtils.isEmpty(onLoadAction.getDefaultActionId())) {
|
||||||
|
onLoadAction.setId(onLoadAction.getDefaultActionId());
|
||||||
|
}
|
||||||
|
if (!StringUtils.isEmpty(onLoadAction.getDefaultCollectionId())) {
|
||||||
|
onLoadAction.setCollectionId(onLoadAction.getDefaultCollectionId());
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
return layout;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Layout updateLayoutWithDefaultResources(Layout layout) {
|
||||||
|
if (!CollectionUtils.isEmpty(layout.getLayoutOnLoadActions())) {
|
||||||
|
layout.getLayoutOnLoadActions()
|
||||||
|
.forEach(layoutOnLoadAction -> layoutOnLoadAction.forEach(onLoadAction -> {
|
||||||
|
if (!StringUtils.isEmpty(onLoadAction.getDefaultActionId())) {
|
||||||
|
onLoadAction.setId(onLoadAction.getDefaultActionId());
|
||||||
|
}
|
||||||
|
if (!StringUtils.isEmpty(onLoadAction.getDefaultCollectionId())) {
|
||||||
|
onLoadAction.setCollectionId(onLoadAction.getDefaultCollectionId());
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
return layout;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ActionViewDTO updateActionViewDTOWithDefaultResources(ActionViewDTO viewDTO) {
|
||||||
|
DefaultResources defaultResourceIds = viewDTO.getDefaultResources();
|
||||||
|
|
||||||
|
if (defaultResourceIds == null) {
|
||||||
|
return viewDTO;
|
||||||
|
}
|
||||||
|
if (StringUtils.isEmpty(defaultResourceIds.getPageId())) {
|
||||||
|
defaultResourceIds.setPageId(viewDTO.getPageId());
|
||||||
|
}
|
||||||
|
if (StringUtils.isEmpty(defaultResourceIds.getActionId())) {
|
||||||
|
defaultResourceIds.setActionId(viewDTO.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
viewDTO.setId(defaultResourceIds.getActionId());
|
||||||
|
viewDTO.setPageId(defaultResourceIds.getPageId());
|
||||||
|
return viewDTO;
|
||||||
|
}
|
||||||
|
|
||||||
|
public NewAction updateNewActionWithDefaultResources(NewAction newAction) {
|
||||||
|
DefaultResources defaultResourceIds = newAction.getDefaultResources();
|
||||||
|
if (defaultResourceIds == null
|
||||||
|
|| StringUtils.isEmpty(defaultResourceIds.getApplicationId())
|
||||||
|
|| StringUtils.isEmpty(defaultResourceIds.getActionId())) {
|
||||||
|
log.error(
|
||||||
|
"Unable to find default ids for newAction: {}",
|
||||||
|
newAction.getId(),
|
||||||
|
new AppsmithException(AppsmithError.DEFAULT_RESOURCES_UNAVAILABLE, "newAction", newAction.getId()));
|
||||||
|
|
||||||
|
if (defaultResourceIds == null) {
|
||||||
|
return newAction;
|
||||||
|
}
|
||||||
|
if (StringUtils.isEmpty(defaultResourceIds.getApplicationId())) {
|
||||||
|
defaultResourceIds.setApplicationId(newAction.getApplicationId());
|
||||||
|
}
|
||||||
|
if (StringUtils.isEmpty(defaultResourceIds.getActionId())) {
|
||||||
|
defaultResourceIds.setActionId(newAction.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
newAction.setId(defaultResourceIds.getActionId());
|
||||||
|
newAction.setApplicationId(defaultResourceIds.getApplicationId());
|
||||||
|
if (newAction.getUnpublishedAction() != null) {
|
||||||
|
newAction.setUnpublishedAction(this.updateActionDTOWithDefaultResources(newAction.getUnpublishedAction()));
|
||||||
|
}
|
||||||
|
if (newAction.getPublishedAction() != null) {
|
||||||
|
newAction.setPublishedAction(this.updateActionDTOWithDefaultResources(newAction.getPublishedAction()));
|
||||||
|
}
|
||||||
|
return newAction;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ActionCollection updateActionCollectionWithDefaultResources(ActionCollection actionCollection) {
|
||||||
|
DefaultResources defaultResourceIds = actionCollection.getDefaultResources();
|
||||||
|
|
||||||
|
if (defaultResourceIds == null) {
|
||||||
|
return actionCollection;
|
||||||
|
}
|
||||||
|
if (StringUtils.isEmpty(defaultResourceIds.getApplicationId())) {
|
||||||
|
defaultResourceIds.setApplicationId(actionCollection.getApplicationId());
|
||||||
|
}
|
||||||
|
if (StringUtils.isEmpty(defaultResourceIds.getCollectionId())) {
|
||||||
|
defaultResourceIds.setCollectionId(actionCollection.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
actionCollection.setId(defaultResourceIds.getCollectionId());
|
||||||
|
actionCollection.setApplicationId(defaultResourceIds.getApplicationId());
|
||||||
|
if (actionCollection.getUnpublishedCollection() != null) {
|
||||||
|
actionCollection.setUnpublishedCollection(
|
||||||
|
this.updateCollectionDTOWithDefaultResources(actionCollection.getUnpublishedCollection()));
|
||||||
|
}
|
||||||
|
if (actionCollection.getPublishedCollection() != null) {
|
||||||
|
actionCollection.setPublishedCollection(
|
||||||
|
this.updateCollectionDTOWithDefaultResources(actionCollection.getPublishedCollection()));
|
||||||
|
}
|
||||||
|
return actionCollection;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ActionCollectionDTO updateCollectionDTOWithDefaultResources(ActionCollectionDTO collection) {
|
||||||
|
DefaultResources defaultResourceIds = collection.getDefaultResources();
|
||||||
|
|
||||||
|
if (defaultResourceIds == null) {
|
||||||
|
return collection;
|
||||||
|
}
|
||||||
|
if (StringUtils.isEmpty(defaultResourceIds.getApplicationId())) {
|
||||||
|
defaultResourceIds.setApplicationId(collection.getApplicationId());
|
||||||
|
}
|
||||||
|
if (StringUtils.isEmpty(defaultResourceIds.getPageId())) {
|
||||||
|
defaultResourceIds.setPageId(collection.getPageId());
|
||||||
|
}
|
||||||
|
if (StringUtils.isEmpty(defaultResourceIds.getCollectionId())) {
|
||||||
|
defaultResourceIds.setCollectionId(collection.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
collection.setApplicationId(defaultResourceIds.getApplicationId());
|
||||||
|
collection.setPageId(defaultResourceIds.getPageId());
|
||||||
|
collection.setId(defaultResourceIds.getCollectionId());
|
||||||
|
|
||||||
|
// Update actions within the collection
|
||||||
|
collection.getActions().forEach(this::updateActionDTOWithDefaultResources);
|
||||||
|
collection.getArchivedActions().forEach(this::updateActionDTOWithDefaultResources);
|
||||||
|
|
||||||
|
return collection;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ActionCollectionViewDTO updateActionCollectionViewDTOWithDefaultResources(ActionCollectionViewDTO viewDTO) {
|
||||||
|
DefaultResources defaultResourceIds = viewDTO.getDefaultResources();
|
||||||
|
|
||||||
|
if (defaultResourceIds == null) {
|
||||||
|
return viewDTO;
|
||||||
|
}
|
||||||
|
if (StringUtils.isEmpty(defaultResourceIds.getApplicationId())) {
|
||||||
|
defaultResourceIds.setApplicationId(viewDTO.getApplicationId());
|
||||||
|
}
|
||||||
|
if (StringUtils.isEmpty(defaultResourceIds.getPageId())) {
|
||||||
|
defaultResourceIds.setPageId(viewDTO.getPageId());
|
||||||
|
}
|
||||||
|
if (StringUtils.isEmpty(defaultResourceIds.getCollectionId())) {
|
||||||
|
defaultResourceIds.setCollectionId(viewDTO.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
viewDTO.setId(defaultResourceIds.getCollectionId());
|
||||||
|
viewDTO.setApplicationId(defaultResourceIds.getApplicationId());
|
||||||
|
viewDTO.setPageId(defaultResourceIds.getPageId());
|
||||||
|
|
||||||
|
viewDTO.getActions().forEach(this::updateActionDTOWithDefaultResources);
|
||||||
|
return viewDTO;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Application updateApplicationWithDefaultResources(Application application) {
|
||||||
|
if (application.getGitApplicationMetadata() != null
|
||||||
|
&& !StringUtils.isEmpty(application.getGitApplicationMetadata().getDefaultApplicationId())) {
|
||||||
|
application.setId(application.getGitApplicationMetadata().getDefaultApplicationId());
|
||||||
|
}
|
||||||
|
if (!CollectionUtils.isEmpty(application.getPages())) {
|
||||||
|
application.getPages().forEach(page -> {
|
||||||
|
if (!StringUtils.isEmpty(page.getDefaultPageId())) {
|
||||||
|
page.setId(page.getDefaultPageId());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (!CollectionUtils.isEmpty(application.getPublishedPages())) {
|
||||||
|
application.getPublishedPages().forEach(page -> {
|
||||||
|
if (!StringUtils.isEmpty(page.getDefaultPageId())) {
|
||||||
|
page.setId(page.getDefaultPageId());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (application.getClientSchemaVersion() == null
|
||||||
|
|| application.getServerSchemaVersion() == null
|
||||||
|
|| (JsonSchemaVersions.clientVersion.equals(application.getClientSchemaVersion())
|
||||||
|
&& JsonSchemaVersions.serverVersion.equals(application.getServerSchemaVersion()))) {
|
||||||
|
application.setIsAutoUpdate(false);
|
||||||
|
} else {
|
||||||
|
application.setIsAutoUpdate(true);
|
||||||
|
}
|
||||||
|
return application;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -133,8 +133,6 @@ public interface NewActionServiceCE extends CrudService<NewAction, String> {
|
||||||
|
|
||||||
Map<String, Object> getAnalyticsProperties(NewAction savedAction);
|
Map<String, Object> getAnalyticsProperties(NewAction savedAction);
|
||||||
|
|
||||||
void populateDefaultResources(NewAction newAction, NewAction branchedAction, String branchName);
|
|
||||||
|
|
||||||
Mono<ImportedActionAndCollectionMapsDTO> updateActionsWithImportedCollectionIds(
|
Mono<ImportedActionAndCollectionMapsDTO> updateActionsWithImportedCollectionIds(
|
||||||
ImportActionCollectionResultDTO importActionCollectionResultDTO,
|
ImportActionCollectionResultDTO importActionCollectionResultDTO,
|
||||||
ImportActionResultDTO importActionResultDTO);
|
ImportActionResultDTO importActionResultDTO);
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ import com.appsmith.server.acl.PolicyGenerator;
|
||||||
import com.appsmith.server.applications.base.ApplicationService;
|
import com.appsmith.server.applications.base.ApplicationService;
|
||||||
import com.appsmith.server.constants.FieldName;
|
import com.appsmith.server.constants.FieldName;
|
||||||
import com.appsmith.server.datasources.base.DatasourceService;
|
import com.appsmith.server.datasources.base.DatasourceService;
|
||||||
|
import com.appsmith.server.defaultresources.DefaultResourcesService;
|
||||||
import com.appsmith.server.domains.Action;
|
import com.appsmith.server.domains.Action;
|
||||||
import com.appsmith.server.domains.ActionCollection;
|
import com.appsmith.server.domains.ActionCollection;
|
||||||
import com.appsmith.server.domains.Application;
|
import com.appsmith.server.domains.Application;
|
||||||
|
|
@ -136,6 +137,8 @@ public class NewActionServiceCEImpl extends BaseService<NewActionRepository, New
|
||||||
private final ObservationRegistry observationRegistry;
|
private final ObservationRegistry observationRegistry;
|
||||||
private final Map<String, Plugin> defaultPluginMap = new HashMap<>();
|
private final Map<String, Plugin> defaultPluginMap = new HashMap<>();
|
||||||
private final AtomicReference<Plugin> jsTypePluginReference = new AtomicReference<>();
|
private final AtomicReference<Plugin> jsTypePluginReference = new AtomicReference<>();
|
||||||
|
private final DefaultResourcesService<NewAction> defaultResourcesService;
|
||||||
|
private final DefaultResourcesService<ActionDTO> dtoDefaultResourcesService;
|
||||||
|
|
||||||
public NewActionServiceCEImpl(
|
public NewActionServiceCEImpl(
|
||||||
Scheduler scheduler,
|
Scheduler scheduler,
|
||||||
|
|
@ -161,7 +164,9 @@ public class NewActionServiceCEImpl extends BaseService<NewActionRepository, New
|
||||||
PagePermission pagePermission,
|
PagePermission pagePermission,
|
||||||
ActionPermission actionPermission,
|
ActionPermission actionPermission,
|
||||||
EntityValidationService entityValidationService,
|
EntityValidationService entityValidationService,
|
||||||
ObservationRegistry observationRegistry) {
|
ObservationRegistry observationRegistry,
|
||||||
|
DefaultResourcesService<NewAction> defaultResourcesService,
|
||||||
|
DefaultResourcesService<ActionDTO> dtoDefaultResourcesService) {
|
||||||
|
|
||||||
super(scheduler, validator, mongoConverter, reactiveMongoTemplate, repository, analyticsService);
|
super(scheduler, validator, mongoConverter, reactiveMongoTemplate, repository, analyticsService);
|
||||||
this.repository = repository;
|
this.repository = repository;
|
||||||
|
|
@ -183,6 +188,8 @@ public class NewActionServiceCEImpl extends BaseService<NewActionRepository, New
|
||||||
this.applicationPermission = applicationPermission;
|
this.applicationPermission = applicationPermission;
|
||||||
this.pagePermission = pagePermission;
|
this.pagePermission = pagePermission;
|
||||||
this.actionPermission = actionPermission;
|
this.actionPermission = actionPermission;
|
||||||
|
this.defaultResourcesService = defaultResourcesService;
|
||||||
|
this.dtoDefaultResourcesService = dtoDefaultResourcesService;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void setCommonFieldsFromNewActionIntoAction(NewAction newAction, ActionDTO action) {
|
protected void setCommonFieldsFromNewActionIntoAction(NewAction newAction, ActionDTO action) {
|
||||||
|
|
@ -268,6 +275,7 @@ public class NewActionServiceCEImpl extends BaseService<NewActionRepository, New
|
||||||
* Whenever we save an action into the repository using this method, we expect that the action has all its required fields populated,
|
* Whenever we save an action into the repository using this method, we expect that the action has all its required fields populated,
|
||||||
* and that this is not a partial update. As a result, all validations can be performed, and values can be reset if they do not fit
|
* and that this is not a partial update. As a result, all validations can be performed, and values can be reset if they do not fit
|
||||||
* our validations.
|
* our validations.
|
||||||
|
*
|
||||||
* @param newAction
|
* @param newAction
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
|
|
@ -294,19 +302,13 @@ public class NewActionServiceCEImpl extends BaseService<NewActionRepository, New
|
||||||
|
|
||||||
ActionDTO action = newAction.getUnpublishedAction();
|
ActionDTO action = newAction.getUnpublishedAction();
|
||||||
|
|
||||||
|
defaultResourcesService.initialize(
|
||||||
|
newAction, newAction.getDefaultResources().getBranchName(), false);
|
||||||
|
dtoDefaultResourcesService.initialize(
|
||||||
|
action, newAction.getDefaultResources().getBranchName(), false);
|
||||||
|
|
||||||
setCommonFieldsFromNewActionIntoAction(newAction, action);
|
setCommonFieldsFromNewActionIntoAction(newAction, action);
|
||||||
|
|
||||||
if (action.getDefaultResources() == null) {
|
|
||||||
return Mono.error(
|
|
||||||
new AppsmithException(AppsmithError.DEFAULT_RESOURCES_UNAVAILABLE, "action", action.getName()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove default appId, branchName and actionId to avoid duplication these resources will be present in
|
|
||||||
// NewAction level default resource
|
|
||||||
action.getDefaultResources().setActionId(null);
|
|
||||||
action.getDefaultResources().setBranchName(null);
|
|
||||||
action.getDefaultResources().setApplicationId(null);
|
|
||||||
|
|
||||||
// Default the validity to true and invalids to be an empty set.
|
// Default the validity to true and invalids to be an empty set.
|
||||||
Set<String> invalids = new HashSet<>();
|
Set<String> invalids = new HashSet<>();
|
||||||
|
|
||||||
|
|
@ -639,18 +641,15 @@ public class NewActionServiceCEImpl extends BaseService<NewActionRepository, New
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates an unpublished action in the database without sending an analytics event.
|
* Updates an unpublished action in the database without sending an analytics event.
|
||||||
*
|
* <p>
|
||||||
* This method performs an update of an unpublished action in the database without triggering an analytics event.
|
* This method performs an update of an unpublished action in the database without triggering an analytics event.
|
||||||
*
|
*
|
||||||
* @param id The unique identifier of the unpublished action to be updated.
|
* @param id The unique identifier of the unpublished action to be updated.
|
||||||
* @param action The updated action object.
|
* @param action The updated action object.
|
||||||
* @param permission An optional permission parameter for access control.
|
* @param permission An optional permission parameter for access control.
|
||||||
* @return A Mono emitting a Tuple containing the updated ActionDTO and NewAction after modification.
|
* @return A Mono emitting a Tuple containing the updated ActionDTO and NewAction after modification.
|
||||||
*
|
|
||||||
* @throws AppsmithException if the provided ID is invalid or if the action is not found.
|
* @throws AppsmithException if the provided ID is invalid or if the action is not found.
|
||||||
*
|
* @implNote This method is used by {#updateUnpublishedAction(String, ActionDTO)}, but it does not send an analytics event. If analytics event tracking is not required for the update, this method can be used to improve performance and reduce overhead.
|
||||||
* @implNote
|
|
||||||
* This method is used by {#updateUnpublishedAction(String, ActionDTO)}, but it does not send an analytics event. If analytics event tracking is not required for the update, this method can be used to improve performance and reduce overhead.
|
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Mono<Tuple2<ActionDTO, NewAction>> updateUnpublishedActionWithoutAnalytics(
|
public Mono<Tuple2<ActionDTO, NewAction>> updateUnpublishedActionWithoutAnalytics(
|
||||||
|
|
@ -1646,33 +1645,6 @@ public class NewActionServiceCEImpl extends BaseService<NewActionRepository, New
|
||||||
return analyticsProperties;
|
return analyticsProperties;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void populateDefaultResources(NewAction newAction, NewAction branchedAction, String branchName) {
|
|
||||||
DefaultResources defaultResources = branchedAction.getDefaultResources();
|
|
||||||
// Create new action but keep defaultApplicationId and defaultActionId same for both the actions
|
|
||||||
defaultResources.setBranchName(branchName);
|
|
||||||
newAction.setDefaultResources(defaultResources);
|
|
||||||
|
|
||||||
String defaultPageId = branchedAction.getUnpublishedAction() != null
|
|
||||||
? branchedAction.getUnpublishedAction().getDefaultResources().getPageId()
|
|
||||||
: branchedAction.getPublishedAction().getDefaultResources().getPageId();
|
|
||||||
DefaultResources defaultsDTO = new DefaultResources();
|
|
||||||
defaultsDTO.setPageId(defaultPageId);
|
|
||||||
if (newAction.getUnpublishedAction() != null) {
|
|
||||||
newAction.getUnpublishedAction().setDefaultResources(defaultsDTO);
|
|
||||||
}
|
|
||||||
if (newAction.getPublishedAction() != null) {
|
|
||||||
newAction.getPublishedAction().setDefaultResources(defaultsDTO);
|
|
||||||
}
|
|
||||||
|
|
||||||
newAction
|
|
||||||
.getUnpublishedAction()
|
|
||||||
.setDeletedAt(branchedAction.getUnpublishedAction().getDeletedAt());
|
|
||||||
newAction.setDeletedAt(branchedAction.getDeletedAt());
|
|
||||||
// Set policies from existing branch object
|
|
||||||
newAction.setPolicies(branchedAction.getPolicies());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Mono<ImportedActionAndCollectionMapsDTO> updateActionsWithImportedCollectionIds(
|
public Mono<ImportedActionAndCollectionMapsDTO> updateActionsWithImportedCollectionIds(
|
||||||
ImportActionCollectionResultDTO importActionCollectionResultDTO,
|
ImportActionCollectionResultDTO importActionCollectionResultDTO,
|
||||||
|
|
@ -1722,7 +1694,7 @@ public class NewActionServiceCEImpl extends BaseService<NewActionRepository, New
|
||||||
.get(newAction.getId())
|
.get(newAction.getId())
|
||||||
.get(0));
|
.get(0));
|
||||||
if (unpublishedAction.getDefaultResources() != null
|
if (unpublishedAction.getDefaultResources() != null
|
||||||
&& org.apache.commons.lang3.StringUtils.isEmpty(
|
&& !StringUtils.hasText(
|
||||||
unpublishedAction.getDefaultResources().getCollectionId())) {
|
unpublishedAction.getDefaultResources().getCollectionId())) {
|
||||||
|
|
||||||
unpublishedAction
|
unpublishedAction
|
||||||
|
|
@ -1788,7 +1760,10 @@ public class NewActionServiceCEImpl extends BaseService<NewActionRepository, New
|
||||||
@Override
|
@Override
|
||||||
public Flux<NewAction> findByPageIdsForExport(
|
public Flux<NewAction> findByPageIdsForExport(
|
||||||
List<String> unpublishedPages, Optional<AclPermission> optionalPermission) {
|
List<String> unpublishedPages, Optional<AclPermission> optionalPermission) {
|
||||||
return repository.findByPageIds(unpublishedPages, optionalPermission);
|
return repository.findByPageIds(unpublishedPages, optionalPermission).doOnNext(newAction -> {
|
||||||
|
this.setCommonFieldsFromNewActionIntoAction(newAction, newAction.getUnpublishedAction());
|
||||||
|
this.setCommonFieldsFromNewActionIntoAction(newAction, newAction.getPublishedAction());
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -1857,8 +1832,6 @@ public class NewActionServiceCEImpl extends BaseService<NewActionRepository, New
|
||||||
defaults.setApplicationId(newAction.getApplicationId());
|
defaults.setApplicationId(newAction.getApplicationId());
|
||||||
}
|
}
|
||||||
newAction.setDefaultResources(defaults);
|
newAction.setDefaultResources(defaults);
|
||||||
|
|
||||||
newAction.setUnpublishedAction(action);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,11 @@
|
||||||
package com.appsmith.server.newactions.base;
|
package com.appsmith.server.newactions.base;
|
||||||
|
|
||||||
|
import com.appsmith.external.models.ActionDTO;
|
||||||
import com.appsmith.server.acl.PolicyGenerator;
|
import com.appsmith.server.acl.PolicyGenerator;
|
||||||
import com.appsmith.server.applications.base.ApplicationService;
|
import com.appsmith.server.applications.base.ApplicationService;
|
||||||
import com.appsmith.server.datasources.base.DatasourceService;
|
import com.appsmith.server.datasources.base.DatasourceService;
|
||||||
|
import com.appsmith.server.defaultresources.DefaultResourcesService;
|
||||||
|
import com.appsmith.server.domains.NewAction;
|
||||||
import com.appsmith.server.helpers.PluginExecutorHelper;
|
import com.appsmith.server.helpers.PluginExecutorHelper;
|
||||||
import com.appsmith.server.helpers.ResponseUtils;
|
import com.appsmith.server.helpers.ResponseUtils;
|
||||||
import com.appsmith.server.newactions.helpers.NewActionHelper;
|
import com.appsmith.server.newactions.helpers.NewActionHelper;
|
||||||
|
|
@ -55,8 +58,9 @@ public class NewActionServiceImpl extends NewActionServiceCEImpl implements NewA
|
||||||
PagePermission pagePermission,
|
PagePermission pagePermission,
|
||||||
ActionPermission actionPermission,
|
ActionPermission actionPermission,
|
||||||
EntityValidationService entityValidationService,
|
EntityValidationService entityValidationService,
|
||||||
ObservationRegistry observationRegistry) {
|
ObservationRegistry observationRegistry,
|
||||||
|
DefaultResourcesService<NewAction> defaultResourcesService,
|
||||||
|
DefaultResourcesService<ActionDTO> dtoDefaultResourcesService) {
|
||||||
super(
|
super(
|
||||||
scheduler,
|
scheduler,
|
||||||
validator,
|
validator,
|
||||||
|
|
@ -81,6 +85,8 @@ public class NewActionServiceImpl extends NewActionServiceCEImpl implements NewA
|
||||||
pagePermission,
|
pagePermission,
|
||||||
actionPermission,
|
actionPermission,
|
||||||
entityValidationService,
|
entityValidationService,
|
||||||
observationRegistry);
|
observationRegistry,
|
||||||
|
defaultResourcesService,
|
||||||
|
dtoDefaultResourcesService);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
package com.appsmith.server.newactions.defaultresources;
|
||||||
|
|
||||||
|
import com.appsmith.external.models.ActionDTO;
|
||||||
|
import com.appsmith.external.models.DefaultResources;
|
||||||
|
import com.appsmith.server.defaultresources.DefaultResourcesServiceCE;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class ActionDTODefaultResourcesServiceCEImpl implements DefaultResourcesServiceCE<ActionDTO> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ActionDTO initialize(ActionDTO domainObject, String branchName, boolean resetExistingValues) {
|
||||||
|
DefaultResources existingDefaultResources = domainObject.getDefaultResources();
|
||||||
|
DefaultResources defaultResources = new DefaultResources();
|
||||||
|
|
||||||
|
String defaultPageId = domainObject.getPageId();
|
||||||
|
|
||||||
|
if (existingDefaultResources != null && !resetExistingValues) {
|
||||||
|
// Check if there are properties to be copied over from existing
|
||||||
|
if (StringUtils.hasText(existingDefaultResources.getPageId())) {
|
||||||
|
defaultPageId = existingDefaultResources.getPageId();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultResources.setPageId(defaultPageId);
|
||||||
|
|
||||||
|
domainObject.setDefaultResources(defaultResources);
|
||||||
|
return domainObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ActionDTO setFromOtherBranch(ActionDTO domainObject, ActionDTO defaultDomainObject, String branchName) {
|
||||||
|
DefaultResources defaultResources = new DefaultResources();
|
||||||
|
|
||||||
|
defaultResources.setPageId(defaultDomainObject.getDefaultResources().getPageId());
|
||||||
|
|
||||||
|
domainObject.setDefaultResources(defaultResources);
|
||||||
|
return domainObject;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
package com.appsmith.server.newactions.defaultresources;
|
||||||
|
|
||||||
|
import com.appsmith.external.models.ActionDTO;
|
||||||
|
import com.appsmith.server.defaultresources.DefaultResourcesService;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class ActionDTODefaultResourcesServiceImpl extends ActionDTODefaultResourcesServiceCEImpl
|
||||||
|
implements DefaultResourcesService<ActionDTO> {}
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
package com.appsmith.server.newactions.defaultresources;
|
||||||
|
|
||||||
|
import com.appsmith.external.models.DefaultResources;
|
||||||
|
import com.appsmith.server.defaultresources.DefaultResourcesServiceCE;
|
||||||
|
import com.appsmith.server.domains.NewAction;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class NewActionDefaultResourcesServiceCEImpl implements DefaultResourcesServiceCE<NewAction> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NewAction initialize(NewAction domainObject, String branchName, boolean resetExistingValues) {
|
||||||
|
DefaultResources existingDefaultResources = domainObject.getDefaultResources();
|
||||||
|
DefaultResources defaultResources = new DefaultResources();
|
||||||
|
|
||||||
|
String defaultApplicationId = domainObject.getApplicationId();
|
||||||
|
String defaultActionId = domainObject.getId();
|
||||||
|
|
||||||
|
if (existingDefaultResources != null && !resetExistingValues) {
|
||||||
|
// Check if there are properties to be copied over from existing
|
||||||
|
if (StringUtils.hasText(existingDefaultResources.getApplicationId())) {
|
||||||
|
defaultApplicationId = existingDefaultResources.getApplicationId();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StringUtils.hasText(existingDefaultResources.getActionId())) {
|
||||||
|
defaultActionId = existingDefaultResources.getActionId();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultResources.setActionId(defaultActionId);
|
||||||
|
defaultResources.setApplicationId(defaultApplicationId);
|
||||||
|
defaultResources.setBranchName(branchName);
|
||||||
|
|
||||||
|
domainObject.setDefaultResources(defaultResources);
|
||||||
|
return domainObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NewAction setFromOtherBranch(NewAction domainObject, NewAction defaultDomainObject, String branchName) {
|
||||||
|
DefaultResources defaultResources = new DefaultResources();
|
||||||
|
|
||||||
|
DefaultResources otherDefaultResources = defaultDomainObject.getDefaultResources();
|
||||||
|
|
||||||
|
defaultResources.setActionId(otherDefaultResources.getActionId());
|
||||||
|
defaultResources.setApplicationId(otherDefaultResources.getApplicationId());
|
||||||
|
defaultResources.setBranchName(branchName);
|
||||||
|
|
||||||
|
domainObject.setDefaultResources(defaultResources);
|
||||||
|
return domainObject;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
package com.appsmith.server.newactions.defaultresources;
|
||||||
|
|
||||||
|
import com.appsmith.server.defaultresources.DefaultResourcesService;
|
||||||
|
import com.appsmith.server.domains.NewAction;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class NewActionDefaultResourcesServiceImpl extends NewActionDefaultResourcesServiceCEImpl
|
||||||
|
implements DefaultResourcesService<NewAction> {}
|
||||||
|
|
@ -5,6 +5,7 @@ import com.appsmith.external.models.DefaultResources;
|
||||||
import com.appsmith.external.models.Policy;
|
import com.appsmith.external.models.Policy;
|
||||||
import com.appsmith.server.actioncollections.base.ActionCollectionService;
|
import com.appsmith.server.actioncollections.base.ActionCollectionService;
|
||||||
import com.appsmith.server.constants.FieldName;
|
import com.appsmith.server.constants.FieldName;
|
||||||
|
import com.appsmith.server.defaultresources.DefaultResourcesService;
|
||||||
import com.appsmith.server.domains.ActionCollection;
|
import com.appsmith.server.domains.ActionCollection;
|
||||||
import com.appsmith.server.domains.Application;
|
import com.appsmith.server.domains.Application;
|
||||||
import com.appsmith.server.domains.NewAction;
|
import com.appsmith.server.domains.NewAction;
|
||||||
|
|
@ -49,6 +50,8 @@ public class NewActionImportableServiceCEImpl implements ImportableServiceCE<New
|
||||||
private final NewActionService newActionService;
|
private final NewActionService newActionService;
|
||||||
private final NewActionRepository repository;
|
private final NewActionRepository repository;
|
||||||
private final ActionCollectionService actionCollectionService;
|
private final ActionCollectionService actionCollectionService;
|
||||||
|
private final DefaultResourcesService<NewAction> defaultResourcesService;
|
||||||
|
private final DefaultResourcesService<ActionDTO> dtoDefaultResourcesService;
|
||||||
|
|
||||||
// Requires pageNameMap, pageNameToOldNameMap, pluginMap and datasourceNameToIdMap, to be present in importable
|
// Requires pageNameMap, pageNameToOldNameMap, pluginMap and datasourceNameToIdMap, to be present in importable
|
||||||
// resources.
|
// resources.
|
||||||
|
|
@ -229,8 +232,7 @@ public class NewActionImportableServiceCEImpl implements ImportableServiceCE<New
|
||||||
if (importedApplication.getGitApplicationMetadata() != null) {
|
if (importedApplication.getGitApplicationMetadata() != null) {
|
||||||
final String defaultApplicationId =
|
final String defaultApplicationId =
|
||||||
importedApplication.getGitApplicationMetadata().getDefaultApplicationId();
|
importedApplication.getGitApplicationMetadata().getDefaultApplicationId();
|
||||||
actionsInOtherBranchesMono = repository
|
actionsInOtherBranchesMono = getActionInOtherBranchesMono(defaultApplicationId)
|
||||||
.findByDefaultApplicationId(defaultApplicationId, Optional.empty())
|
|
||||||
.filter(newAction -> newAction.getGitSyncId() != null)
|
.filter(newAction -> newAction.getGitSyncId() != null)
|
||||||
.collectMap(NewAction::getGitSyncId);
|
.collectMap(NewAction::getGitSyncId);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -307,6 +309,38 @@ public class NewActionImportableServiceCEImpl implements ImportableServiceCE<New
|
||||||
.get(newAction.getPluginId()));
|
.get(newAction.getPluginId()));
|
||||||
newActionService.generateAndSetActionPolicies(parentPage, newAction);
|
newActionService.generateAndSetActionPolicies(parentPage, newAction);
|
||||||
|
|
||||||
|
if (importedApplication.getGitApplicationMetadata() != null) {
|
||||||
|
// application is git connected, check if the action is already present in
|
||||||
|
// any other branch
|
||||||
|
if (actionsInOtherBranches.containsKey(newAction.getGitSyncId())) {
|
||||||
|
// action found in other branch, copy the default resources from that
|
||||||
|
// action
|
||||||
|
NewAction branchedAction = getExistingActionForImportedAction(
|
||||||
|
mappedImportableResourcesDTO, actionsInOtherBranches, newAction);
|
||||||
|
defaultResourcesService.setFromOtherBranch(
|
||||||
|
newAction, branchedAction, importingMetaDTO.getBranchName());
|
||||||
|
dtoDefaultResourcesService.setFromOtherBranch(
|
||||||
|
newAction.getUnpublishedAction(),
|
||||||
|
branchedAction.getUnpublishedAction(),
|
||||||
|
importingMetaDTO.getBranchName());
|
||||||
|
} else {
|
||||||
|
// This is the first action we are saving with given gitSyncId in this
|
||||||
|
// instance
|
||||||
|
DefaultResources defaultResources = new DefaultResources();
|
||||||
|
defaultResources.setApplicationId(importedApplication
|
||||||
|
.getGitApplicationMetadata()
|
||||||
|
.getDefaultApplicationId());
|
||||||
|
defaultResources.setActionId(newAction.getId());
|
||||||
|
defaultResources.setBranchName(importingMetaDTO.getBranchName());
|
||||||
|
newAction.setDefaultResources(defaultResources);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
DefaultResources defaultResources = new DefaultResources();
|
||||||
|
defaultResources.setApplicationId(importedApplication.getId());
|
||||||
|
defaultResources.setActionId(newAction.getId());
|
||||||
|
newAction.setDefaultResources(defaultResources);
|
||||||
|
}
|
||||||
|
|
||||||
// Check if the action has gitSyncId and if it's already in DB
|
// Check if the action has gitSyncId and if it's already in DB
|
||||||
if (existingAppContainsAction(actionsInCurrentApp, newAction)) {
|
if (existingAppContainsAction(actionsInCurrentApp, newAction)) {
|
||||||
|
|
||||||
|
|
@ -339,46 +373,13 @@ public class NewActionImportableServiceCEImpl implements ImportableServiceCE<New
|
||||||
parentPage.getId()));
|
parentPage.getId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
populateDomainMappedReferences(mappedImportableResourcesDTO, newAction);
|
// Add all the properties that will be needed for a new object
|
||||||
|
populateNewAction(
|
||||||
// this will generate the id and other auto generated fields e.g. createdAt
|
importingMetaDTO,
|
||||||
newAction.updateForBulkWriteOperation();
|
mappedImportableResourcesDTO,
|
||||||
|
importedApplication,
|
||||||
// set gitSyncId, if it doesn't exist
|
actionsInOtherBranches,
|
||||||
if (newAction.getGitSyncId() == null) {
|
newAction);
|
||||||
newAction.setGitSyncId(newAction.getApplicationId() + "_"
|
|
||||||
+ Instant.now().toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (importedApplication.getGitApplicationMetadata() != null) {
|
|
||||||
// application is git connected, check if the action is already present in
|
|
||||||
// any other branch
|
|
||||||
if (actionsInOtherBranches.containsKey(newAction.getGitSyncId())) {
|
|
||||||
// action found in other branch, copy the default resources from that
|
|
||||||
// action
|
|
||||||
NewAction branchedAction = getExistingActionForImportedAction(
|
|
||||||
mappedImportableResourcesDTO,
|
|
||||||
actionsInOtherBranches,
|
|
||||||
newAction);
|
|
||||||
newActionService.populateDefaultResources(
|
|
||||||
newAction, branchedAction, importingMetaDTO.getBranchName());
|
|
||||||
} else {
|
|
||||||
// This is the first action we are saving with given gitSyncId in this
|
|
||||||
// instance
|
|
||||||
DefaultResources defaultResources = new DefaultResources();
|
|
||||||
defaultResources.setApplicationId(importedApplication
|
|
||||||
.getGitApplicationMetadata()
|
|
||||||
.getDefaultApplicationId());
|
|
||||||
defaultResources.setActionId(newAction.getId());
|
|
||||||
defaultResources.setBranchName(importingMetaDTO.getBranchName());
|
|
||||||
newAction.setDefaultResources(defaultResources);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
DefaultResources defaultResources = new DefaultResources();
|
|
||||||
defaultResources.setApplicationId(importedApplication.getId());
|
|
||||||
defaultResources.setActionId(newAction.getId());
|
|
||||||
newAction.setDefaultResources(defaultResources);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add it to actions list that'll be inserted or updated in bulk
|
// Add it to actions list that'll be inserted or updated in bulk
|
||||||
newNewActionList.add(newAction);
|
newNewActionList.add(newAction);
|
||||||
|
|
@ -418,6 +419,24 @@ public class NewActionImportableServiceCEImpl implements ImportableServiceCE<New
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void populateNewAction(
|
||||||
|
ImportingMetaDTO importingMetaDTO,
|
||||||
|
MappedImportableResourcesDTO mappedImportableResourcesDTO,
|
||||||
|
Application importedApplication,
|
||||||
|
Map<String, NewAction> actionsInOtherBranches,
|
||||||
|
NewAction newAction) {
|
||||||
|
// this will generate the id and other auto generated fields e.g. createdAt
|
||||||
|
newAction.updateForBulkWriteOperation();
|
||||||
|
|
||||||
|
populateDomainMappedReferences(mappedImportableResourcesDTO, newAction);
|
||||||
|
|
||||||
|
// set gitSyncId, if it doesn't exist
|
||||||
|
if (newAction.getGitSyncId() == null) {
|
||||||
|
newAction.setGitSyncId(
|
||||||
|
newAction.getApplicationId() + "_" + Instant.now().toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected NewAction getExistingActionForImportedAction(
|
protected NewAction getExistingActionForImportedAction(
|
||||||
MappedImportableResourcesDTO mappedImportableResourcesDTO,
|
MappedImportableResourcesDTO mappedImportableResourcesDTO,
|
||||||
Map<String, NewAction> actionsInCurrentApp,
|
Map<String, NewAction> actionsInCurrentApp,
|
||||||
|
|
@ -440,6 +459,10 @@ public class NewActionImportableServiceCEImpl implements ImportableServiceCE<New
|
||||||
.filter(newAction -> newAction.getGitSyncId() != null);
|
.filter(newAction -> newAction.getGitSyncId() != null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected Flux<NewAction> getActionInOtherBranchesMono(String defaultApplicationId) {
|
||||||
|
return repository.findByDefaultApplicationId(defaultApplicationId, Optional.empty());
|
||||||
|
}
|
||||||
|
|
||||||
private NewPage updatePageInAction(
|
private NewPage updatePageInAction(
|
||||||
ActionDTO action, Map<String, NewPage> pageNameMap, Map<String, String> actionIdMap) {
|
ActionDTO action, Map<String, NewPage> pageNameMap, Map<String, String> actionIdMap) {
|
||||||
NewPage parentPage = pageNameMap.get(action.getPageId());
|
NewPage parentPage = pageNameMap.get(action.getPageId());
|
||||||
|
|
@ -488,6 +511,13 @@ public class NewActionImportableServiceCEImpl implements ImportableServiceCE<New
|
||||||
|
|
||||||
private void putActionIdInMap(NewAction newAction, ImportActionResultDTO importActionResultDTO) {
|
private void putActionIdInMap(NewAction newAction, ImportActionResultDTO importActionResultDTO) {
|
||||||
// Populate actionIdsMap to associate the appropriate actions to run on page load
|
// Populate actionIdsMap to associate the appropriate actions to run on page load
|
||||||
|
String defaultResourcesActionId = newAction.getDefaultResources().getActionId();
|
||||||
|
|
||||||
|
if (defaultResourcesActionId == null) {
|
||||||
|
defaultResourcesActionId = newAction.getId();
|
||||||
|
newAction.getDefaultResources().setActionId(newAction.getId());
|
||||||
|
}
|
||||||
|
|
||||||
if (newAction.getUnpublishedAction() != null) {
|
if (newAction.getUnpublishedAction() != null) {
|
||||||
ActionDTO unpublishedAction = newAction.getUnpublishedAction();
|
ActionDTO unpublishedAction = newAction.getUnpublishedAction();
|
||||||
importActionResultDTO
|
importActionResultDTO
|
||||||
|
|
@ -505,7 +535,8 @@ public class NewActionImportableServiceCEImpl implements ImportableServiceCE<New
|
||||||
final Map<String, String> actionIds = importActionResultDTO
|
final Map<String, String> actionIds = importActionResultDTO
|
||||||
.getUnpublishedCollectionIdToActionIdsMap()
|
.getUnpublishedCollectionIdToActionIdsMap()
|
||||||
.get(unpublishedAction.getCollectionId());
|
.get(unpublishedAction.getCollectionId());
|
||||||
actionIds.put(newAction.getDefaultResources().getActionId(), newAction.getId());
|
|
||||||
|
actionIds.put(defaultResourcesActionId, newAction.getId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (newAction.getPublishedAction() != null) {
|
if (newAction.getPublishedAction() != null) {
|
||||||
|
|
@ -525,7 +556,7 @@ public class NewActionImportableServiceCEImpl implements ImportableServiceCE<New
|
||||||
final Map<String, String> actionIds = importActionResultDTO
|
final Map<String, String> actionIds = importActionResultDTO
|
||||||
.getPublishedCollectionIdToActionIdsMap()
|
.getPublishedCollectionIdToActionIdsMap()
|
||||||
.get(publishedAction.getCollectionId());
|
.get(publishedAction.getCollectionId());
|
||||||
actionIds.put(newAction.getDefaultResources().getActionId(), newAction.getId());
|
actionIds.put(defaultResourcesActionId, newAction.getId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
package com.appsmith.server.newactions.imports;
|
package com.appsmith.server.newactions.imports;
|
||||||
|
|
||||||
|
import com.appsmith.external.models.ActionDTO;
|
||||||
import com.appsmith.server.actioncollections.base.ActionCollectionService;
|
import com.appsmith.server.actioncollections.base.ActionCollectionService;
|
||||||
|
import com.appsmith.server.defaultresources.DefaultResourcesService;
|
||||||
import com.appsmith.server.domains.NewAction;
|
import com.appsmith.server.domains.NewAction;
|
||||||
import com.appsmith.server.imports.importable.ImportableService;
|
import com.appsmith.server.imports.importable.ImportableService;
|
||||||
import com.appsmith.server.newactions.base.NewActionService;
|
import com.appsmith.server.newactions.base.NewActionService;
|
||||||
|
|
@ -13,7 +15,14 @@ public class NewActionImportableServiceImpl extends NewActionImportableServiceCE
|
||||||
public NewActionImportableServiceImpl(
|
public NewActionImportableServiceImpl(
|
||||||
NewActionService newActionService,
|
NewActionService newActionService,
|
||||||
NewActionRepository repository,
|
NewActionRepository repository,
|
||||||
ActionCollectionService actionCollectionService) {
|
ActionCollectionService actionCollectionService,
|
||||||
super(newActionService, repository, actionCollectionService);
|
DefaultResourcesService<NewAction> defaultResourcesService,
|
||||||
|
DefaultResourcesService<ActionDTO> dtoDefaultResourcesService) {
|
||||||
|
super(
|
||||||
|
newActionService,
|
||||||
|
repository,
|
||||||
|
actionCollectionService,
|
||||||
|
defaultResourcesService,
|
||||||
|
dtoDefaultResourcesService);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,50 @@
|
||||||
|
package com.appsmith.server.newpages.defaultresources;
|
||||||
|
|
||||||
|
import com.appsmith.external.models.DefaultResources;
|
||||||
|
import com.appsmith.server.defaultresources.DefaultResourcesServiceCE;
|
||||||
|
import com.appsmith.server.domains.NewPage;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class NewPageDefaultResourcesServiceCEImpl implements DefaultResourcesServiceCE<NewPage> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NewPage initialize(NewPage domainObject, String branchName, boolean resetExistingValues) {
|
||||||
|
DefaultResources existingDefaultResources = domainObject.getDefaultResources();
|
||||||
|
DefaultResources defaultResources = new DefaultResources();
|
||||||
|
|
||||||
|
String defaultApplicationId = domainObject.getApplicationId();
|
||||||
|
String defaultPageId = domainObject.getId();
|
||||||
|
|
||||||
|
if (existingDefaultResources != null && !resetExistingValues) {
|
||||||
|
// Check if there are properties to be copied over from existing
|
||||||
|
if (StringUtils.hasText(existingDefaultResources.getApplicationId())) {
|
||||||
|
defaultApplicationId = existingDefaultResources.getApplicationId();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StringUtils.hasText(existingDefaultResources.getPageId())) {
|
||||||
|
defaultPageId = existingDefaultResources.getPageId();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultResources.setPageId(defaultPageId);
|
||||||
|
defaultResources.setApplicationId(defaultApplicationId);
|
||||||
|
defaultResources.setBranchName(branchName);
|
||||||
|
|
||||||
|
domainObject.setDefaultResources(defaultResources);
|
||||||
|
return domainObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NewPage setFromOtherBranch(NewPage domainObject, NewPage defaultDomainObject, String branchName) {
|
||||||
|
DefaultResources defaultResources = new DefaultResources();
|
||||||
|
|
||||||
|
defaultResources.setPageId(defaultDomainObject.getId());
|
||||||
|
defaultResources.setApplicationId(defaultDomainObject.getApplicationId());
|
||||||
|
defaultResources.setBranchName(branchName);
|
||||||
|
|
||||||
|
domainObject.setDefaultResources(defaultResources);
|
||||||
|
return domainObject;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
package com.appsmith.server.newpages.defaultresources;
|
||||||
|
|
||||||
|
import com.appsmith.server.defaultresources.DefaultResourcesService;
|
||||||
|
import com.appsmith.server.domains.NewPage;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class NewPageDefaultResourcesServiceImpl extends NewPageDefaultResourcesServiceCEImpl
|
||||||
|
implements DefaultResourcesService<NewPage> {}
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package com.appsmith.server.repositories;
|
package com.appsmith.server.repositories;
|
||||||
|
|
||||||
import com.appsmith.server.acl.AclPermission;
|
import com.appsmith.server.acl.AclPermission;
|
||||||
|
import com.mongodb.bulk.BulkWriteResult;
|
||||||
import com.mongodb.client.result.InsertManyResult;
|
import com.mongodb.client.result.InsertManyResult;
|
||||||
import org.springframework.data.domain.Sort;
|
import org.springframework.data.domain.Sort;
|
||||||
import org.springframework.data.mongodb.core.query.Criteria;
|
import org.springframework.data.mongodb.core.query.Criteria;
|
||||||
|
|
@ -48,4 +49,6 @@ public interface AppsmithRepository<T> {
|
||||||
* @return List of actions that were passed in the method
|
* @return List of actions that were passed in the method
|
||||||
*/
|
*/
|
||||||
Mono<List<InsertManyResult>> bulkInsert(List<T> domainList);
|
Mono<List<InsertManyResult>> bulkInsert(List<T> domainList);
|
||||||
|
|
||||||
|
Mono<List<BulkWriteResult>> bulkUpdate(List<T> domainList);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,11 +11,15 @@ import com.appsmith.server.exceptions.AppsmithException;
|
||||||
import com.appsmith.server.repositories.CacheableRepositoryHelper;
|
import com.appsmith.server.repositories.CacheableRepositoryHelper;
|
||||||
import com.mongodb.BasicDBObject;
|
import com.mongodb.BasicDBObject;
|
||||||
import com.mongodb.DBObject;
|
import com.mongodb.DBObject;
|
||||||
|
import com.mongodb.bulk.BulkWriteResult;
|
||||||
|
import com.mongodb.client.model.UpdateOneModel;
|
||||||
|
import com.mongodb.client.model.WriteModel;
|
||||||
import com.mongodb.client.result.InsertManyResult;
|
import com.mongodb.client.result.InsertManyResult;
|
||||||
import com.mongodb.client.result.UpdateResult;
|
import com.mongodb.client.result.UpdateResult;
|
||||||
import com.querydsl.core.types.Path;
|
import com.querydsl.core.types.Path;
|
||||||
import jakarta.validation.constraints.NotNull;
|
import jakarta.validation.constraints.NotNull;
|
||||||
import org.bson.Document;
|
import org.bson.Document;
|
||||||
|
import org.bson.types.ObjectId;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.core.GenericTypeResolver;
|
import org.springframework.core.GenericTypeResolver;
|
||||||
import org.springframework.data.domain.Sort;
|
import org.springframework.data.domain.Sort;
|
||||||
|
|
@ -743,4 +747,28 @@ public abstract class BaseAppsmithRepositoryCEImpl<T extends BaseDomain> {
|
||||||
.flatMapMany(documentMongoCollection -> documentMongoCollection.insertMany(dbObjects))
|
.flatMapMany(documentMongoCollection -> documentMongoCollection.insertMany(dbObjects))
|
||||||
.collectList();
|
.collectList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Mono<List<BulkWriteResult>> bulkUpdate(List<T> domainObjects) {
|
||||||
|
if (CollectionUtils.isEmpty(domainObjects)) {
|
||||||
|
return Mono.just(Collections.emptyList());
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert the list of new actions to a list of DBObjects
|
||||||
|
List<WriteModel<Document>> dbObjects = domainObjects.stream()
|
||||||
|
.map(actionCollection -> {
|
||||||
|
assert actionCollection.getId() != null;
|
||||||
|
Document document = new Document();
|
||||||
|
mongoOperations.getConverter().write(actionCollection, document);
|
||||||
|
document.remove("_id");
|
||||||
|
return (WriteModel<Document>) new UpdateOneModel<Document>(
|
||||||
|
new Document("_id", new ObjectId(actionCollection.getId())),
|
||||||
|
new Document("$set", document));
|
||||||
|
})
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
return mongoOperations
|
||||||
|
.getCollection(mongoOperations.getCollectionName(genericDomain))
|
||||||
|
.flatMapMany(documentMongoCollection -> documentMongoCollection.bulkWrite(dbObjects))
|
||||||
|
.collectList();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ import com.appsmith.external.models.CreatorContextType;
|
||||||
import com.appsmith.server.acl.AclPermission;
|
import com.appsmith.server.acl.AclPermission;
|
||||||
import com.appsmith.server.domains.ActionCollection;
|
import com.appsmith.server.domains.ActionCollection;
|
||||||
import com.appsmith.server.repositories.AppsmithRepository;
|
import com.appsmith.server.repositories.AppsmithRepository;
|
||||||
import com.mongodb.bulk.BulkWriteResult;
|
|
||||||
import org.springframework.data.domain.Sort;
|
import org.springframework.data.domain.Sort;
|
||||||
import reactor.core.publisher.Flux;
|
import reactor.core.publisher.Flux;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
|
|
@ -49,8 +48,6 @@ public interface CustomActionCollectionRepositoryCE extends AppsmithRepository<A
|
||||||
|
|
||||||
Flux<ActionCollection> findByPageIds(List<String> pageIds, Optional<AclPermission> permission);
|
Flux<ActionCollection> findByPageIds(List<String> pageIds, Optional<AclPermission> permission);
|
||||||
|
|
||||||
Mono<List<BulkWriteResult>> bulkUpdate(List<ActionCollection> actionCollections);
|
|
||||||
|
|
||||||
Flux<ActionCollection> findAllByApplicationIds(List<String> applicationIds, List<String> includeFields);
|
Flux<ActionCollection> findAllByApplicationIds(List<String> applicationIds, List<String> includeFields);
|
||||||
|
|
||||||
Flux<ActionCollection> findAllUnpublishedActionCollectionsByContextIdAndContextType(
|
Flux<ActionCollection> findAllUnpublishedActionCollectionsByContextIdAndContextType(
|
||||||
|
|
|
||||||
|
|
@ -8,25 +8,17 @@ import com.appsmith.server.domains.ActionCollection;
|
||||||
import com.appsmith.server.domains.QActionCollection;
|
import com.appsmith.server.domains.QActionCollection;
|
||||||
import com.appsmith.server.repositories.BaseAppsmithRepositoryImpl;
|
import com.appsmith.server.repositories.BaseAppsmithRepositoryImpl;
|
||||||
import com.appsmith.server.repositories.CacheableRepositoryHelper;
|
import com.appsmith.server.repositories.CacheableRepositoryHelper;
|
||||||
import com.mongodb.bulk.BulkWriteResult;
|
|
||||||
import com.mongodb.client.model.UpdateOneModel;
|
|
||||||
import com.mongodb.client.model.WriteModel;
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.bson.Document;
|
|
||||||
import org.bson.types.ObjectId;
|
|
||||||
import org.springframework.data.domain.Sort;
|
import org.springframework.data.domain.Sort;
|
||||||
import org.springframework.data.mongodb.core.ReactiveMongoOperations;
|
import org.springframework.data.mongodb.core.ReactiveMongoOperations;
|
||||||
import org.springframework.data.mongodb.core.convert.MongoConverter;
|
import org.springframework.data.mongodb.core.convert.MongoConverter;
|
||||||
import org.springframework.data.mongodb.core.query.Criteria;
|
import org.springframework.data.mongodb.core.query.Criteria;
|
||||||
import org.springframework.util.CollectionUtils;
|
|
||||||
import reactor.core.publisher.Flux;
|
import reactor.core.publisher.Flux;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import static org.springframework.data.mongodb.core.query.Criteria.where;
|
import static org.springframework.data.mongodb.core.query.Criteria.where;
|
||||||
|
|
||||||
|
|
@ -236,31 +228,6 @@ public class CustomActionCollectionRepositoryCEImpl extends BaseAppsmithReposito
|
||||||
return queryAll(List.of(pageIdCriteria), permission);
|
return queryAll(List.of(pageIdCriteria), permission);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Mono<List<BulkWriteResult>> bulkUpdate(List<ActionCollection> actionCollections) {
|
|
||||||
if (CollectionUtils.isEmpty(actionCollections)) {
|
|
||||||
return Mono.just(Collections.emptyList());
|
|
||||||
}
|
|
||||||
|
|
||||||
// convert the list of new actions to a list of DBObjects
|
|
||||||
List<WriteModel<Document>> dbObjects = actionCollections.stream()
|
|
||||||
.map(actionCollection -> {
|
|
||||||
assert actionCollection.getId() != null;
|
|
||||||
Document document = new Document();
|
|
||||||
mongoOperations.getConverter().write(actionCollection, document);
|
|
||||||
document.remove("_id");
|
|
||||||
return (WriteModel<Document>) new UpdateOneModel<Document>(
|
|
||||||
new Document("_id", new ObjectId(actionCollection.getId())),
|
|
||||||
new Document("$set", document));
|
|
||||||
})
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
|
|
||||||
return mongoOperations
|
|
||||||
.getCollection(mongoOperations.getCollectionName(ActionCollection.class))
|
|
||||||
.flatMapMany(documentMongoCollection -> documentMongoCollection.bulkWrite(dbObjects))
|
|
||||||
.collectList();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Flux<ActionCollection> findAllByApplicationIds(List<String> applicationIds, List<String> includeFields) {
|
public Flux<ActionCollection> findAllByApplicationIds(List<String> applicationIds, List<String> includeFields) {
|
||||||
Criteria applicationCriteria = Criteria.where(FieldName.APPLICATION_ID).in(applicationIds);
|
Criteria applicationCriteria = Criteria.where(FieldName.APPLICATION_ID).in(applicationIds);
|
||||||
|
|
|
||||||
|
|
@ -73,8 +73,6 @@ public interface CustomNewActionRepositoryCE extends AppsmithRepository<NewActio
|
||||||
Flux<NewAction> findAllNonJsActionsByNameAndPageIdsAndViewMode(
|
Flux<NewAction> findAllNonJsActionsByNameAndPageIdsAndViewMode(
|
||||||
String name, List<String> pageIds, Boolean viewMode, AclPermission aclPermission, Sort sort);
|
String name, List<String> pageIds, Boolean viewMode, AclPermission aclPermission, Sort sort);
|
||||||
|
|
||||||
Mono<List<BulkWriteResult>> bulkUpdate(List<NewAction> newActions);
|
|
||||||
|
|
||||||
Mono<List<BulkWriteResult>> publishActions(String applicationId, AclPermission permission);
|
Mono<List<BulkWriteResult>> publishActions(String applicationId, AclPermission permission);
|
||||||
|
|
||||||
Mono<UpdateResult> archiveDeletedUnpublishedActions(String applicationId, AclPermission permission);
|
Mono<UpdateResult> archiveDeletedUnpublishedActions(String applicationId, AclPermission permission);
|
||||||
|
|
|
||||||
|
|
@ -12,11 +12,8 @@ import com.appsmith.server.dtos.PluginTypeAndCountDTO;
|
||||||
import com.appsmith.server.repositories.BaseAppsmithRepositoryImpl;
|
import com.appsmith.server.repositories.BaseAppsmithRepositoryImpl;
|
||||||
import com.appsmith.server.repositories.CacheableRepositoryHelper;
|
import com.appsmith.server.repositories.CacheableRepositoryHelper;
|
||||||
import com.mongodb.bulk.BulkWriteResult;
|
import com.mongodb.bulk.BulkWriteResult;
|
||||||
import com.mongodb.client.model.UpdateOneModel;
|
|
||||||
import com.mongodb.client.model.WriteModel;
|
|
||||||
import com.mongodb.client.result.UpdateResult;
|
import com.mongodb.client.result.UpdateResult;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.bson.Document;
|
|
||||||
import org.bson.types.ObjectId;
|
import org.bson.types.ObjectId;
|
||||||
import org.springframework.data.domain.Sort;
|
import org.springframework.data.domain.Sort;
|
||||||
import org.springframework.data.mongodb.core.MongoTemplate;
|
import org.springframework.data.mongodb.core.MongoTemplate;
|
||||||
|
|
@ -31,18 +28,15 @@ import org.springframework.data.mongodb.core.convert.MongoConverter;
|
||||||
import org.springframework.data.mongodb.core.query.Criteria;
|
import org.springframework.data.mongodb.core.query.Criteria;
|
||||||
import org.springframework.data.mongodb.core.query.Query;
|
import org.springframework.data.mongodb.core.query.Query;
|
||||||
import org.springframework.data.mongodb.core.query.Update;
|
import org.springframework.data.mongodb.core.query.Update;
|
||||||
import org.springframework.util.CollectionUtils;
|
|
||||||
import reactor.core.publisher.Flux;
|
import reactor.core.publisher.Flux;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
import reactor.core.scheduler.Schedulers;
|
import reactor.core.scheduler.Schedulers;
|
||||||
|
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import static org.springframework.data.mongodb.core.aggregation.Aggregation.group;
|
import static org.springframework.data.mongodb.core.aggregation.Aggregation.group;
|
||||||
import static org.springframework.data.mongodb.core.aggregation.Aggregation.match;
|
import static org.springframework.data.mongodb.core.aggregation.Aggregation.match;
|
||||||
|
|
@ -537,30 +531,6 @@ public class CustomNewActionRepositoryCEImpl extends BaseAppsmithRepositoryImpl<
|
||||||
return criteriaList;
|
return criteriaList;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Mono<List<BulkWriteResult>> bulkUpdate(List<NewAction> newActions) {
|
|
||||||
if (CollectionUtils.isEmpty(newActions)) {
|
|
||||||
return Mono.just(Collections.emptyList());
|
|
||||||
}
|
|
||||||
|
|
||||||
// convert the list of new actions to a list of DBObjects
|
|
||||||
List<WriteModel<Document>> dbObjects = newActions.stream()
|
|
||||||
.map(newAction -> {
|
|
||||||
assert newAction.getId() != null;
|
|
||||||
Document document = new Document();
|
|
||||||
mongoOperations.getConverter().write(newAction, document);
|
|
||||||
document.remove("_id");
|
|
||||||
return (WriteModel<Document>) new UpdateOneModel<Document>(
|
|
||||||
new Document("_id", new ObjectId(newAction.getId())), new Document("$set", document));
|
|
||||||
})
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
|
|
||||||
return mongoOperations
|
|
||||||
.getCollection(mongoOperations.getCollectionName(NewAction.class))
|
|
||||||
.flatMapMany(documentMongoCollection -> documentMongoCollection.bulkWrite(dbObjects))
|
|
||||||
.collectList();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Flux<NewAction> findByDefaultApplicationId(String defaultApplicationId, Optional<AclPermission> permission) {
|
public Flux<NewAction> findByDefaultApplicationId(String defaultApplicationId, Optional<AclPermission> permission) {
|
||||||
final String defaultResources = fieldName(QBranchAwareDomain.branchAwareDomain.defaultResources);
|
final String defaultResources = fieldName(QBranchAwareDomain.branchAwareDomain.defaultResources);
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,5 @@ public interface CustomNewPageRepositoryCE extends AppsmithRepository<NewPage> {
|
||||||
|
|
||||||
Mono<List<BulkWriteResult>> publishPages(Collection<String> pageIds, AclPermission permission);
|
Mono<List<BulkWriteResult>> publishPages(Collection<String> pageIds, AclPermission permission);
|
||||||
|
|
||||||
Mono<List<BulkWriteResult>> bulkUpdate(List<NewPage> newPages);
|
|
||||||
|
|
||||||
Flux<NewPage> findAllByApplicationIdsWithoutPermission(List<String> applicationIds, List<String> includeFields);
|
Flux<NewPage> findAllByApplicationIdsWithoutPermission(List<String> applicationIds, List<String> includeFields);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,11 +10,7 @@ import com.appsmith.server.dtos.PageDTO;
|
||||||
import com.appsmith.server.repositories.BaseAppsmithRepositoryImpl;
|
import com.appsmith.server.repositories.BaseAppsmithRepositoryImpl;
|
||||||
import com.appsmith.server.repositories.CacheableRepositoryHelper;
|
import com.appsmith.server.repositories.CacheableRepositoryHelper;
|
||||||
import com.mongodb.bulk.BulkWriteResult;
|
import com.mongodb.bulk.BulkWriteResult;
|
||||||
import com.mongodb.client.model.UpdateOneModel;
|
|
||||||
import com.mongodb.client.model.WriteModel;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.bson.Document;
|
|
||||||
import org.bson.types.ObjectId;
|
|
||||||
import org.springframework.data.mongodb.core.MongoTemplate;
|
import org.springframework.data.mongodb.core.MongoTemplate;
|
||||||
import org.springframework.data.mongodb.core.ReactiveMongoOperations;
|
import org.springframework.data.mongodb.core.ReactiveMongoOperations;
|
||||||
import org.springframework.data.mongodb.core.aggregation.Aggregation;
|
import org.springframework.data.mongodb.core.aggregation.Aggregation;
|
||||||
|
|
@ -23,18 +19,15 @@ import org.springframework.data.mongodb.core.aggregation.Fields;
|
||||||
import org.springframework.data.mongodb.core.convert.MongoConverter;
|
import org.springframework.data.mongodb.core.convert.MongoConverter;
|
||||||
import org.springframework.data.mongodb.core.query.Criteria;
|
import org.springframework.data.mongodb.core.query.Criteria;
|
||||||
import org.springframework.data.mongodb.core.query.Query;
|
import org.springframework.data.mongodb.core.query.Query;
|
||||||
import org.springframework.util.CollectionUtils;
|
|
||||||
import reactor.core.publisher.Flux;
|
import reactor.core.publisher.Flux;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
import reactor.core.scheduler.Schedulers;
|
import reactor.core.scheduler.Schedulers;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import static org.springframework.data.mongodb.core.query.Criteria.where;
|
import static org.springframework.data.mongodb.core.query.Criteria.where;
|
||||||
|
|
||||||
|
|
@ -298,30 +291,6 @@ public class CustomNewPageRepositoryCEImpl extends BaseAppsmithRepositoryImpl<Ne
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Mono<List<BulkWriteResult>> bulkUpdate(List<NewPage> newPages) {
|
|
||||||
if (CollectionUtils.isEmpty(newPages)) {
|
|
||||||
return Mono.just(Collections.emptyList());
|
|
||||||
}
|
|
||||||
|
|
||||||
// convert the list of new pages to a list of DBObjects
|
|
||||||
List<WriteModel<Document>> dbObjects = newPages.stream()
|
|
||||||
.map(newPage -> {
|
|
||||||
assert newPage.getId() != null;
|
|
||||||
Document document = new Document();
|
|
||||||
mongoOperations.getConverter().write(newPage, document);
|
|
||||||
document.remove("_id");
|
|
||||||
return (WriteModel<Document>) new UpdateOneModel<Document>(
|
|
||||||
new Document("_id", new ObjectId(newPage.getId())), new Document("$set", document));
|
|
||||||
})
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
|
|
||||||
return mongoOperations
|
|
||||||
.getCollection(mongoOperations.getCollectionName(NewPage.class))
|
|
||||||
.flatMapMany(documentMongoCollection -> documentMongoCollection.bulkWrite(dbObjects))
|
|
||||||
.collectList();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Flux<NewPage> findAllByApplicationIdsWithoutPermission(
|
public Flux<NewPage> findAllByApplicationIdsWithoutPermission(
|
||||||
List<String> applicationIds, List<String> includeFields) {
|
List<String> applicationIds, List<String> includeFields) {
|
||||||
|
|
|
||||||
|
|
@ -160,7 +160,6 @@ public class LayoutCollectionServiceCEImpl implements LayoutCollectionServiceCE
|
||||||
defaultResources.setBranchName(branchName);
|
defaultResources.setBranchName(branchName);
|
||||||
collectionDTO.setDefaultResources(defaultResources);
|
collectionDTO.setDefaultResources(defaultResources);
|
||||||
actionCollection.setDefaultResources(defaultResources);
|
actionCollection.setDefaultResources(defaultResources);
|
||||||
actionCollection.setUnpublishedCollection(collectionDTO);
|
|
||||||
actionCollectionService.generateAndSetPolicies(newPage, actionCollection);
|
actionCollectionService.generateAndSetPolicies(newPage, actionCollection);
|
||||||
actionCollection.setUnpublishedCollection(collectionDTO);
|
actionCollection.setUnpublishedCollection(collectionDTO);
|
||||||
return Mono.zip(
|
return Mono.zip(
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import com.appsmith.server.actioncollections.base.ActionCollectionService;
|
||||||
import com.appsmith.server.actioncollections.base.ActionCollectionServiceImpl;
|
import com.appsmith.server.actioncollections.base.ActionCollectionServiceImpl;
|
||||||
import com.appsmith.server.applications.base.ApplicationService;
|
import com.appsmith.server.applications.base.ApplicationService;
|
||||||
import com.appsmith.server.constants.FieldName;
|
import com.appsmith.server.constants.FieldName;
|
||||||
|
import com.appsmith.server.defaultresources.DefaultResourcesService;
|
||||||
import com.appsmith.server.domains.ActionCollection;
|
import com.appsmith.server.domains.ActionCollection;
|
||||||
import com.appsmith.server.domains.Layout;
|
import com.appsmith.server.domains.Layout;
|
||||||
import com.appsmith.server.domains.NewAction;
|
import com.appsmith.server.domains.NewAction;
|
||||||
|
|
@ -124,6 +125,9 @@ public class ActionCollectionServiceImplTest {
|
||||||
@MockBean
|
@MockBean
|
||||||
private PolicyGenerator policyGenerator;
|
private PolicyGenerator policyGenerator;
|
||||||
|
|
||||||
|
@MockBean
|
||||||
|
private DefaultResourcesService<ActionCollection> actionCollectionDefaultResourcesService;
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
applicationPermission = new ApplicationPermissionImpl();
|
applicationPermission = new ApplicationPermissionImpl();
|
||||||
|
|
@ -141,7 +145,8 @@ public class ActionCollectionServiceImplTest {
|
||||||
applicationService,
|
applicationService,
|
||||||
responseUtils,
|
responseUtils,
|
||||||
applicationPermission,
|
applicationPermission,
|
||||||
actionPermission);
|
actionPermission,
|
||||||
|
actionCollectionDefaultResourcesService);
|
||||||
|
|
||||||
layoutCollectionService = new LayoutCollectionServiceImpl(
|
layoutCollectionService = new LayoutCollectionServiceImpl(
|
||||||
newPageService,
|
newPageService,
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import com.appsmith.external.models.PluginType;
|
||||||
import com.appsmith.server.acl.PolicyGenerator;
|
import com.appsmith.server.acl.PolicyGenerator;
|
||||||
import com.appsmith.server.applications.base.ApplicationService;
|
import com.appsmith.server.applications.base.ApplicationService;
|
||||||
import com.appsmith.server.datasources.base.DatasourceService;
|
import com.appsmith.server.datasources.base.DatasourceService;
|
||||||
|
import com.appsmith.server.defaultresources.DefaultResourcesService;
|
||||||
import com.appsmith.server.domains.NewAction;
|
import com.appsmith.server.domains.NewAction;
|
||||||
import com.appsmith.server.domains.Plugin;
|
import com.appsmith.server.domains.Plugin;
|
||||||
import com.appsmith.server.helpers.PluginExecutorHelper;
|
import com.appsmith.server.helpers.PluginExecutorHelper;
|
||||||
|
|
@ -123,6 +124,12 @@ public class NewActionServiceUnitTest {
|
||||||
@MockBean
|
@MockBean
|
||||||
ObservationRegistry observationRegistry;
|
ObservationRegistry observationRegistry;
|
||||||
|
|
||||||
|
@MockBean
|
||||||
|
DefaultResourcesService<NewAction> defaultResourcesService;
|
||||||
|
|
||||||
|
@MockBean
|
||||||
|
DefaultResourcesService<ActionDTO> dtoDefaultResourcesService;
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
public void setup() {
|
public void setup() {
|
||||||
newActionService = new NewActionServiceCEImpl(
|
newActionService = new NewActionServiceCEImpl(
|
||||||
|
|
@ -149,7 +156,9 @@ public class NewActionServiceUnitTest {
|
||||||
pagePermission,
|
pagePermission,
|
||||||
actionPermission,
|
actionPermission,
|
||||||
entityValidationService,
|
entityValidationService,
|
||||||
observationRegistry);
|
observationRegistry,
|
||||||
|
defaultResourcesService,
|
||||||
|
dtoDefaultResourcesService);
|
||||||
|
|
||||||
ObservationRegistry.ObservationConfig mockObservationConfig =
|
ObservationRegistry.ObservationConfig mockObservationConfig =
|
||||||
Mockito.mock(ObservationRegistry.ObservationConfig.class);
|
Mockito.mock(ObservationRegistry.ObservationConfig.class);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user