chore: added import override CE (#40462)
## Description - Adds override feature when importing artifacts EE PR https://github.com/appsmithorg/appsmith-ee/pull/7251 ## Automation /ok-to-test tags="@tag.Git" ### 🔍 Cypress test results <!-- This is an auto-generated comment: Cypress test results --> > [!TIP] > 🟢 🟢 🟢 All cypress tests have passed! 🎉 🎉 🎉 > Workflow run: <https://github.com/appsmithorg/appsmith/actions/runs/14727350382> > Commit: 79ceda02e6aee9a078548572f27dcb0ea9b58b52 > <a href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=14727350382&attempt=1" target="_blank">Cypress dashboard</a>. > Tags: `@tag.Git` > Spec: > <hr>Tue, 29 Apr 2025 09:54:34 UTC <!-- end of auto-generated comment: Cypress test results --> ## Communication Should the DevRel and Marketing teams inform users about this change? - [ ] Yes - [ ] No <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Introduced an import override modal for handling duplicate artifact imports from Git, allowing users to override existing artifacts with new ones. - Enhanced user messaging with detailed prompts and localized strings for import override scenarios. - **Bug Fixes** - Improved error handling during artifact import, specifically distinguishing duplicate key errors and providing clearer feedback. - **Improvements** - Streamlined artifact import flow, including better state management and more robust error reporting. - Refined backend service interfaces and reduced unnecessary dependencies for improved maintainability. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: sondermanish <manish@appsmith.com>
This commit is contained in:
parent
76540c3171
commit
61d48018e1
|
|
@ -135,6 +135,7 @@ import { useGitModEnabled } from "pages/Editor/gitSync/hooks/modHooks";
|
|||
import {
|
||||
GitRepoLimitErrorModal as NewGitRepoLimitErrorModal,
|
||||
GitImportModal as NewGitImportModal,
|
||||
GitImportOverrideModal,
|
||||
} from "git";
|
||||
import OldRepoLimitExceededErrorModal from "pages/Editor/gitSync/RepoLimitExceededErrorModal";
|
||||
|
||||
|
|
@ -145,6 +146,7 @@ function GitModals() {
|
|||
<>
|
||||
<NewGitImportModal />
|
||||
<NewGitRepoLimitErrorModal />
|
||||
<GitImportOverrideModal />
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
|
|
|
|||
|
|
@ -4,6 +4,14 @@ export const IMPORT_GIT = {
|
|||
WAIT_TEXT: "Please wait while we import via Git..",
|
||||
};
|
||||
|
||||
export const IMPORT_OVERRIDE_MODAL = {
|
||||
TITLE: "Override existing {{artifactType}}?",
|
||||
DESCRIPTION:
|
||||
"{{newArtifactName}} already exists in this workspace as {{oldArtifactName}}. Do you want to override it with the imported {{artifactType}}?",
|
||||
CANCEL_BTN: "Cancel",
|
||||
OVERRIDE_BTN: "Override",
|
||||
};
|
||||
|
||||
export const CONNECT_GIT = {
|
||||
MODAL_TITLE: "Configure Git",
|
||||
CHOOSE_PROVIDER_CTA: "Configure Git",
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ function ImportModal() {
|
|||
gitImportError,
|
||||
isGitImportLoading,
|
||||
isImportModalOpen,
|
||||
resetGitImport,
|
||||
toggleImportModal,
|
||||
} = useImport();
|
||||
const {
|
||||
|
|
@ -24,11 +25,16 @@ function ImportModal() {
|
|||
|
||||
const onSubmit = useCallback(
|
||||
(params: GitImportRequestParams) => {
|
||||
gitImport(params);
|
||||
gitImport({ ...params, override: false });
|
||||
},
|
||||
[gitImport],
|
||||
);
|
||||
|
||||
const resetConnectState = useCallback(() => {
|
||||
resetGlobalSSHKey();
|
||||
resetGitImport();
|
||||
}, [resetGitImport, resetGlobalSSHKey]);
|
||||
|
||||
return (
|
||||
<ConnectModalView
|
||||
artifactType="artifact"
|
||||
|
|
@ -41,7 +47,7 @@ function ImportModal() {
|
|||
onGenerateSSHKey={fetchGlobalSSHKey}
|
||||
onOpenImport={null}
|
||||
onSubmit={onSubmit}
|
||||
resetConnectState={resetGlobalSSHKey}
|
||||
resetConnectState={resetConnectState}
|
||||
sshPublicKey={sshPublicKey}
|
||||
toggleModalOpen={toggleImportModal}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,86 @@
|
|||
import {
|
||||
Button,
|
||||
Modal,
|
||||
ModalBody,
|
||||
ModalContent,
|
||||
ModalFooter,
|
||||
ModalHeader,
|
||||
Text,
|
||||
} from "@appsmith/ads";
|
||||
import { IMPORT_OVERRIDE_MODAL } from "git/ee/constants/messages";
|
||||
import useMessage from "git/hooks/useMessage";
|
||||
import noop from "lodash/noop";
|
||||
import React, { useCallback } from "react";
|
||||
import styled from "styled-components";
|
||||
|
||||
const StyledModalContent = styled(ModalContent)`
|
||||
width: 640px;
|
||||
`;
|
||||
|
||||
interface ImportOverrideModalViewProps {
|
||||
artifactType?: string;
|
||||
isImportLoading: boolean;
|
||||
isOpen: boolean;
|
||||
newArtifactName: string | null;
|
||||
oldArtifactName: string | null;
|
||||
onImport: () => void;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
}
|
||||
|
||||
function ImportOverrideModalView({
|
||||
artifactType = "artifact",
|
||||
isImportLoading = false,
|
||||
isOpen = false,
|
||||
newArtifactName = null,
|
||||
oldArtifactName = null,
|
||||
onImport = noop,
|
||||
onOpenChange = noop,
|
||||
}: ImportOverrideModalViewProps) {
|
||||
const modalTitle = useMessage(IMPORT_OVERRIDE_MODAL.TITLE, { artifactType });
|
||||
const modalDescription = useMessage(IMPORT_OVERRIDE_MODAL.DESCRIPTION, {
|
||||
newArtifactName: newArtifactName ?? "",
|
||||
oldArtifactName: oldArtifactName ?? "",
|
||||
artifactType,
|
||||
});
|
||||
const ctaBtnText = useMessage(IMPORT_OVERRIDE_MODAL.OVERRIDE_BTN, {
|
||||
artifactType,
|
||||
});
|
||||
|
||||
const handleCancel = useCallback(() => {
|
||||
onOpenChange(false);
|
||||
}, [onOpenChange]);
|
||||
|
||||
const handleImport = useCallback(() => {
|
||||
if (!isImportLoading) {
|
||||
onImport();
|
||||
}
|
||||
}, [isImportLoading, onImport]);
|
||||
|
||||
return (
|
||||
<Modal onOpenChange={onOpenChange} open={isOpen}>
|
||||
<StyledModalContent>
|
||||
<ModalHeader>{modalTitle}</ModalHeader>
|
||||
<ModalBody>
|
||||
<Text kind="body-m" renderAs="p">
|
||||
{modalDescription}
|
||||
</Text>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button kind="secondary" onClick={handleCancel} size="md">
|
||||
{IMPORT_OVERRIDE_MODAL.CANCEL_BTN}
|
||||
</Button>
|
||||
<Button
|
||||
isLoading={isImportLoading}
|
||||
onClick={handleImport}
|
||||
size="md"
|
||||
type="submit"
|
||||
>
|
||||
{ctaBtnText}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</StyledModalContent>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
export default ImportOverrideModalView;
|
||||
46
app/client/src/git/components/ImportOverrideModal/index.tsx
Normal file
46
app/client/src/git/components/ImportOverrideModal/index.tsx
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
import React, { useCallback } from "react";
|
||||
import ImportOverrideModalView from "./ImportOverrideModalView";
|
||||
import useImport from "git/hooks/useImport";
|
||||
|
||||
function ImportOverrideModal() {
|
||||
const {
|
||||
gitImport,
|
||||
importOverrideDetails,
|
||||
isGitImportLoading,
|
||||
isImportOverrideModalOpen,
|
||||
resetGitImport,
|
||||
resetImportOverrideDetails,
|
||||
} = useImport();
|
||||
|
||||
const handleOpenChange = useCallback(
|
||||
(open: boolean) => {
|
||||
if (!open && !isGitImportLoading) {
|
||||
resetImportOverrideDetails();
|
||||
resetGitImport();
|
||||
}
|
||||
},
|
||||
[isGitImportLoading, resetGitImport, resetImportOverrideDetails],
|
||||
);
|
||||
|
||||
const handleImport = useCallback(() => {
|
||||
if (importOverrideDetails) {
|
||||
const params = { ...importOverrideDetails.params, override: true };
|
||||
|
||||
gitImport(params);
|
||||
}
|
||||
}, [gitImport, importOverrideDetails]);
|
||||
|
||||
return (
|
||||
<ImportOverrideModalView
|
||||
artifactType={"package"}
|
||||
isImportLoading={isGitImportLoading}
|
||||
isOpen={isImportOverrideModalOpen}
|
||||
newArtifactName={importOverrideDetails?.newArtifactName ?? null}
|
||||
oldArtifactName={importOverrideDetails?.oldArtifactName ?? null}
|
||||
onImport={handleImport}
|
||||
onOpenChange={handleOpenChange}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default ImportOverrideModal;
|
||||
|
|
@ -37,4 +37,5 @@ export enum GitErrorCodes {
|
|||
REPO_NOT_EMPTY = "AE-GIT-4033",
|
||||
REPO_LIMIT_REACHED = "AE-GIT-4043",
|
||||
PUSH_FAILED_REMOTE_COUNTERPART_IS_AHEAD = "AE-GIT-4048",
|
||||
DUPLICATE_ARTIFACT_OVERRIDE = "AE-GIT-5004",
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,9 +3,12 @@ import { useDispatch, useSelector } from "react-redux";
|
|||
import {
|
||||
selectGitImportState,
|
||||
selectImportModalOpen,
|
||||
selectImportOverrideDetails,
|
||||
selectImportOverrideModalOpen,
|
||||
} from "git/store/selectors/gitGlobalSelectors";
|
||||
import { gitGlobalActions } from "git/store/gitGlobalSlice";
|
||||
import type { GitImportRequestParams } from "git/requests/gitImportRequest.types";
|
||||
import type { SetImportOverrideDetailsPayload } from "git/store/actions/uiActions";
|
||||
|
||||
export default function useImport() {
|
||||
const dispatch = useDispatch();
|
||||
|
|
@ -19,6 +22,10 @@ export default function useImport() {
|
|||
[dispatch],
|
||||
);
|
||||
|
||||
const resetGitImport = useCallback(() => {
|
||||
dispatch(gitGlobalActions.resetGitImport());
|
||||
}, [dispatch]);
|
||||
|
||||
const isImportModalOpen = useSelector(selectImportModalOpen);
|
||||
|
||||
const toggleImportModal = useCallback(
|
||||
|
|
@ -28,11 +35,31 @@ export default function useImport() {
|
|||
[dispatch],
|
||||
);
|
||||
|
||||
const isImportOverrideModalOpen = useSelector(selectImportOverrideModalOpen);
|
||||
|
||||
const importOverrideDetails = useSelector(selectImportOverrideDetails);
|
||||
|
||||
const setImportOverrideDetails = useCallback(
|
||||
(details: SetImportOverrideDetailsPayload) => {
|
||||
dispatch(gitGlobalActions.setImportOverrideDetails(details));
|
||||
},
|
||||
[dispatch],
|
||||
);
|
||||
|
||||
const resetImportOverrideDetails = useCallback(() => {
|
||||
dispatch(gitGlobalActions.resetImportOverrideDetails());
|
||||
}, [dispatch]);
|
||||
|
||||
return {
|
||||
isGitImportLoading: gitImportState?.loading ?? false,
|
||||
gitImportError: gitImportState?.error ?? null,
|
||||
gitImport,
|
||||
resetGitImport,
|
||||
isImportModalOpen: isImportModalOpen ?? false,
|
||||
toggleImportModal,
|
||||
isImportOverrideModalOpen: isImportOverrideModalOpen ?? false,
|
||||
importOverrideDetails,
|
||||
setImportOverrideDetails,
|
||||
resetImportOverrideDetails,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
12
app/client/src/git/hooks/useMessage.ts
Normal file
12
app/client/src/git/hooks/useMessage.ts
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
import { useMemo } from "react";
|
||||
|
||||
export default function useMessage(msg: string, args: Record<string, string>) {
|
||||
return useMemo(() => {
|
||||
const msgWithArgs = msg.replace(/\{\{([^}]+)\}\}/g, (match, p1) => {
|
||||
// p1 is the key from {{key}} in the message
|
||||
return args[p1] || match;
|
||||
});
|
||||
|
||||
return msgWithArgs;
|
||||
}, [msg, args]);
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@ export { useHotKeys } from "./components/HotKeys";
|
|||
export { default as GitContextProvider } from "./components/GitContextProvider";
|
||||
export { default as GitModals } from "./ee/components/GitModals";
|
||||
export { default as GitImportModal } from "./components/ImportModal";
|
||||
export { default as GitImportOverrideModal } from "./components/ImportOverrideModal";
|
||||
export { default as GitRepoLimitErrorModal } from "./components/RepoLimitErrorModal";
|
||||
export { default as GitQuickActions } from "./components/QuickActions";
|
||||
export { default as GitProtectedBranchCallout } from "./components/ProtectedBranchCallout";
|
||||
|
|
|
|||
|
|
@ -17,7 +17,10 @@ async function gitImportRequestNew(
|
|||
workspaceId: string,
|
||||
params: GitImportRequestParams,
|
||||
): AxiosPromise<GitImportResponse> {
|
||||
return Api.post(`${GIT_BASE_URL}/artifacts/import`, params, { workspaceId });
|
||||
const { override = false, ...restParams } = params;
|
||||
const body = { override, ...restParams };
|
||||
|
||||
return Api.post(`${GIT_BASE_URL}/artifacts/import`, body, { workspaceId });
|
||||
}
|
||||
|
||||
export default async function gitImportRequest(
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ export interface GitImportRequestParams {
|
|||
authorEmail: string;
|
||||
useDefaultProfile?: boolean;
|
||||
};
|
||||
override?: boolean;
|
||||
}
|
||||
|
||||
export interface GitImportResponseData {
|
||||
|
|
|
|||
|
|
@ -2,13 +2,17 @@ import { call, put, select } from "redux-saga/effects";
|
|||
import { validateResponse } from "sagas/ErrorSagas";
|
||||
import type { PayloadAction } from "@reduxjs/toolkit";
|
||||
import gitImportRequest from "git/requests/gitImportRequest";
|
||||
import type { GitImportResponse } from "git/requests/gitImportRequest.types";
|
||||
import type {
|
||||
GitImportRequestParams,
|
||||
GitImportResponse,
|
||||
} from "git/requests/gitImportRequest.types";
|
||||
import type { GitImportInitPayload } from "git/store/actions/gitImportActions";
|
||||
import { gitGlobalActions } from "git/store/gitGlobalSlice";
|
||||
import { getWorkspaceIdForImport } from "ee/selectors/applicationSelectors";
|
||||
import { GitErrorCodes } from "git/constants/enums";
|
||||
import { selectGitApiContractsEnabled } from "git/store/selectors/gitFeatureFlagSelectors";
|
||||
import handleApiErrors from "./helpers/handleApiErrors";
|
||||
import type { GitApiError } from "git/store/types";
|
||||
|
||||
export default function* gitImportSaga(
|
||||
action: PayloadAction<GitImportInitPayload>,
|
||||
|
|
@ -36,6 +40,7 @@ export default function* gitImportSaga(
|
|||
gitGlobalActions.gitImportSuccess({ responseData: response.data }),
|
||||
);
|
||||
yield put(gitGlobalActions.toggleImportModal({ open: false }));
|
||||
yield put(gitGlobalActions.resetImportOverrideDetails());
|
||||
}
|
||||
} catch (e) {
|
||||
const error = handleApiErrors(e as Error, response);
|
||||
|
|
@ -47,6 +52,38 @@ export default function* gitImportSaga(
|
|||
yield put(gitGlobalActions.toggleImportModal({ open: false }));
|
||||
yield put(gitGlobalActions.toggleRepoLimitErrorModal({ open: true }));
|
||||
}
|
||||
|
||||
if (GitErrorCodes.DUPLICATE_ARTIFACT_OVERRIDE === error.code) {
|
||||
yield call(handleDuplicateArtifactOverride, error, params);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function* handleDuplicateArtifactOverride(
|
||||
error: GitApiError,
|
||||
params: GitImportRequestParams,
|
||||
) {
|
||||
yield put(gitGlobalActions.toggleImportModal({ open: false }));
|
||||
|
||||
let artifactNames = { newArtifactName: null, oldArtifactName: null };
|
||||
|
||||
if (error?.message) {
|
||||
const jsonMatch = error.message.match(/\{.*\}/);
|
||||
const jsonStr = jsonMatch ? jsonMatch[0] : null;
|
||||
|
||||
if (jsonStr) {
|
||||
try {
|
||||
artifactNames = JSON.parse(jsonStr);
|
||||
} catch {}
|
||||
}
|
||||
}
|
||||
|
||||
yield put(
|
||||
gitGlobalActions.setImportOverrideDetails({
|
||||
params,
|
||||
oldArtifactName: artifactNames.oldArtifactName ?? "",
|
||||
newArtifactName: artifactNames.newArtifactName ?? "",
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,3 +48,10 @@ export const gitImportErrorAction = (
|
|||
|
||||
return state;
|
||||
};
|
||||
|
||||
export const resetGitImportAction = (state: GitGlobalReduxState) => {
|
||||
state.gitImport.loading = false;
|
||||
state.gitImport.error = null;
|
||||
|
||||
return state;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { createArtifactAction } from "../helpers/createArtifactAction";
|
|||
import type { GitGlobalReduxState } from "../types";
|
||||
import type { PayloadAction } from "@reduxjs/toolkit";
|
||||
import type { GitArtifactDef } from "git/types";
|
||||
import type { GitImportRequestParams } from "git/requests/gitImportRequest.types";
|
||||
|
||||
// connect modal
|
||||
export interface ToggleConnectModalPayload {
|
||||
|
|
@ -31,6 +32,7 @@ export const toggleConnectSuccessModalAction =
|
|||
return state;
|
||||
});
|
||||
|
||||
// import
|
||||
export interface ToggleImportModalPayload {
|
||||
open: boolean;
|
||||
}
|
||||
|
|
@ -46,6 +48,29 @@ export const toggleImportModalAction = (
|
|||
return state;
|
||||
};
|
||||
|
||||
export const resetImportOverrideDetailsAction = (
|
||||
state: GitGlobalReduxState,
|
||||
) => {
|
||||
state.importOverrideDetails = null;
|
||||
|
||||
return state;
|
||||
};
|
||||
|
||||
export interface SetImportOverrideDetailsPayload {
|
||||
params: GitImportRequestParams;
|
||||
oldArtifactName: string;
|
||||
newArtifactName: string;
|
||||
}
|
||||
|
||||
export const setImportOverrideDetailsAction = (
|
||||
state: GitGlobalReduxState,
|
||||
action: PayloadAction<SetImportOverrideDetailsPayload>,
|
||||
) => {
|
||||
state.importOverrideDetails = { ...action.payload };
|
||||
|
||||
return state;
|
||||
};
|
||||
|
||||
// disconnect modal
|
||||
export interface OpenDisconnectModalPayload {
|
||||
targetArtifactDef: GitArtifactDef;
|
||||
|
|
|
|||
|
|
@ -10,11 +10,16 @@ import {
|
|||
updateGlobalProfileSuccessAction,
|
||||
} from "./actions/updateGlobalProfileActions";
|
||||
import { gitGlobalInitialState } from "./helpers/initialState";
|
||||
import { toggleImportModalAction } from "./actions/uiActions";
|
||||
import {
|
||||
resetImportOverrideDetailsAction,
|
||||
setImportOverrideDetailsAction,
|
||||
toggleImportModalAction,
|
||||
} from "./actions/uiActions";
|
||||
import {
|
||||
gitImportErrorAction,
|
||||
gitImportInitAction,
|
||||
gitImportSuccessAction,
|
||||
resetGitImportAction,
|
||||
} from "./actions/gitImportActions";
|
||||
import {
|
||||
fetchGlobalSSHKeyErrorAction,
|
||||
|
|
@ -41,7 +46,10 @@ export const gitGlobalSlice = createSlice({
|
|||
gitImportInit: gitImportInitAction,
|
||||
gitImportSuccess: gitImportSuccessAction,
|
||||
gitImportError: gitImportErrorAction,
|
||||
resetGitImport: resetGitImportAction,
|
||||
toggleImportModal: toggleImportModalAction,
|
||||
resetImportOverrideDetails: resetImportOverrideDetailsAction,
|
||||
setImportOverrideDetails: setImportOverrideDetailsAction,
|
||||
toggleRepoLimitErrorModal: toggleRepoLimitErrorModalAction,
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -168,5 +168,6 @@ export const gitGlobalInitialState: GitGlobalReduxState = {
|
|||
error: null,
|
||||
},
|
||||
isImportModalOpen: false,
|
||||
importOverrideDetails: null,
|
||||
repoLimitErrorModalOpen: false,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -14,6 +14,12 @@ export const selectUpdateGlobalProfileState = (state: GitRootState) =>
|
|||
export const selectImportModalOpen = (state: GitRootState) =>
|
||||
selectGitGlobal(state).isImportModalOpen;
|
||||
|
||||
export const selectImportOverrideModalOpen = (state: GitRootState) =>
|
||||
!!selectGitGlobal(state).importOverrideDetails;
|
||||
|
||||
export const selectImportOverrideDetails = (state: GitRootState) =>
|
||||
selectGitGlobal(state).importOverrideDetails ?? null;
|
||||
|
||||
export const selectGitImportState = (state: GitRootState) =>
|
||||
selectGitGlobal(state).gitImport;
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import type { FetchGlobalSSHKeyResponseData } from "git/requests/fetchGlobalSSHK
|
|||
import type { FetchRefsResponseData } from "git/requests/fetchRefsRequest.types";
|
||||
import type { GitArtifactDef } from "git/types";
|
||||
import type { PretagResponseData } from "git/requests/pretagRequest.types";
|
||||
import type { GitImportRequestParams } from "git/requests/gitImportRequest.types";
|
||||
|
||||
export interface GitApiError extends ApiResponseError {
|
||||
errorType?: string;
|
||||
|
|
@ -98,6 +99,11 @@ export interface GitGlobalReduxState {
|
|||
globalSSHKey: GitAsyncState<FetchGlobalSSHKeyResponseData>;
|
||||
// ui
|
||||
isImportModalOpen: boolean;
|
||||
importOverrideDetails: {
|
||||
params: GitImportRequestParams;
|
||||
oldArtifactName: string;
|
||||
newArtifactName: string;
|
||||
} | null;
|
||||
repoLimitErrorModalOpen: boolean;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ import static com.appsmith.git.constants.ce.CommonConstantsCE.DELIMITER_PATH;
|
|||
@Import({GitServiceConfig.class})
|
||||
public class FileUtilsCEImpl implements FileInterface {
|
||||
|
||||
private final GitServiceConfig gitServiceConfig;
|
||||
protected final GitServiceConfig gitServiceConfig;
|
||||
protected final FSGitHandler fsGitHandler;
|
||||
private final GitExecutor gitExecutor;
|
||||
protected final FileOperations fileOperations;
|
||||
|
|
@ -98,7 +98,7 @@ public class FileUtilsCEImpl implements FileInterface {
|
|||
private static final Pattern ALLOWED_FILE_EXTENSION_PATTERN =
|
||||
Pattern.compile("(.*?)\\.(md|MD|git|gitignore|github|yml|yaml)$");
|
||||
|
||||
private final Scheduler scheduler = Schedulers.boundedElastic();
|
||||
protected final Scheduler scheduler = Schedulers.boundedElastic();
|
||||
|
||||
private static final String CANVAS_WIDGET = "(Canvas)[0-9]*.";
|
||||
|
||||
|
|
@ -1250,6 +1250,12 @@ public class FileUtilsCEImpl implements FileInterface {
|
|||
return metadataMono.subscribeOn(scheduler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Object> reconstructPackageJsonFromGitRepository(Path repoSuffix) {
|
||||
return Mono.error(
|
||||
new AppsmithPluginException(AppsmithPluginError.PLUGIN_UNSUPPORTED_OPERATION, "package json creation"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Object> reconstructMetadataFromGitRepository(Path repoSuffix) {
|
||||
Mono<Object> metadataMono = Mono.fromCallable(() -> {
|
||||
|
|
|
|||
|
|
@ -77,6 +77,8 @@ public interface FileInterface {
|
|||
|
||||
Mono<Object> reconstructMetadataFromGitRepository(Path repoSuffix);
|
||||
|
||||
Mono<Object> reconstructPackageJsonFromGitRepository(Path repoSuffix);
|
||||
|
||||
Mono<Object> reconstructPageFromGitRepo(
|
||||
String pageName, String branchName, Path repoSuffixPath, Boolean checkoutRequired);
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ public enum ErrorType {
|
|||
BAD_REQUEST,
|
||||
INTERNAL_ERROR,
|
||||
ACTION_CONFIGURATION_ERROR,
|
||||
ARTIFACT_IMPORT_ERROR,
|
||||
GIT_CONFIGURATION_ERROR,
|
||||
GIT_ACTION_EXECUTION_ERROR,
|
||||
GIT_UPSTREAM_CHANGES_PUSH_EXECUTION_ERROR,
|
||||
|
|
|
|||
|
|
@ -11,4 +11,13 @@ public class GitConnectDTO {
|
|||
String remoteUrl;
|
||||
|
||||
GitProfile gitProfile;
|
||||
|
||||
/**
|
||||
* This attribute has been placed specifically for packages,
|
||||
* In multi instance setup, the packages in PROD are present in non git connected state.
|
||||
* Once the DEV package connects to git, the prod would also require the git connected package
|
||||
* however importing a new package in PROD workspace would cause problems, and we can't delete existing
|
||||
* consumed packages, hence we would override prod package with same UUID.
|
||||
*/
|
||||
Boolean override;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -876,6 +876,14 @@ public enum AppsmithError {
|
|||
"Duplicate Configuration",
|
||||
ErrorType.BAD_REQUEST,
|
||||
null),
|
||||
ARTIFACT_IMPORT_DUPLICATE_KEY_WRITE_ERROR(
|
||||
500,
|
||||
AppsmithErrorCode.ARTIFACT_IMPORT_DUPLICATE_KEY_WRITE_ERROR.getCode(),
|
||||
"Duplicate key detected while importing artifact into the workspace during a write operation to the server.. Details: {0}",
|
||||
AppsmithErrorAction.DEFAULT,
|
||||
"Import operation failed because of a duplicate key error.",
|
||||
ErrorType.ARTIFACT_IMPORT_ERROR,
|
||||
null),
|
||||
INVALID_PROPERTIES_CONFIGURATION(
|
||||
500,
|
||||
AppsmithErrorCode.INVALID_PROPERTIES_CONFIGURATION.getCode(),
|
||||
|
|
|
|||
|
|
@ -103,6 +103,8 @@ public enum AppsmithErrorCode {
|
|||
JSON_PROCESSING_ERROR("AE-JSN-4001", "Json processing error"),
|
||||
INCOMPATIBLE_IMPORTED_JSON("AE-JSN-4045", "Incompatible imported json"),
|
||||
GENERIC_JSON_IMPORT_ERROR("AE-JSN-4049", "Generic json import error"),
|
||||
ARTIFACT_IMPORT_DUPLICATE_KEY_WRITE_ERROR(
|
||||
"AE-JSN-5001", "Artifact import failed due to a duplicate key conflict during write operation"),
|
||||
INVALID_LOGIN_METHOD("AE-LGN-4000", "Invalid login method"),
|
||||
PLUGIN_NOT_INSTALLED("AE-PLG-4001", "Plugin not installed"),
|
||||
PLUGIN_ID_NOT_GIVEN("AE-PLG-4002", "Plugin id not given"),
|
||||
|
|
|
|||
|
|
@ -21,9 +21,6 @@ import java.util.List;
|
|||
|
||||
public interface CentralGitServiceCE {
|
||||
|
||||
Mono<? extends ArtifactImportDTO> importArtifactFromGit(
|
||||
String workspaceId, GitConnectDTO gitConnectDTO, ArtifactType artifactType, GitType gitType);
|
||||
|
||||
Mono<? extends ArtifactImportDTO> importArtifactFromGit(
|
||||
String workspaceId, GitConnectDTO gitConnectDTO, GitType gitType);
|
||||
|
||||
|
|
|
|||
|
|
@ -161,11 +161,11 @@ public class CentralGitServiceCEImpl implements CentralGitServiceCE {
|
|||
final String repoName = gitHandlingService.getRepoName(gitConnectDTO);
|
||||
|
||||
// since at this point in the import flow, there is no context about the artifact type
|
||||
// it needs to be retrieved from the fetched repository itself. however, in order to retrieve
|
||||
// the artifact type from repository, the repository needs to be saved.
|
||||
// for saving the repo an identifier is required (which usually is the artifact id);
|
||||
// it needs to be retrieved from the fetched repository itself. however, to retrieve
|
||||
// the artifact type from the repository, the repository needs to be saved.
|
||||
// for saving the repository, an identifier is required (which usually is the artifact id);
|
||||
// however, the artifact could only be generated after the artifact type is known.
|
||||
// hence this is a temporary placeholder to hold the repository and it's components
|
||||
// hence this is a temporary placeholder to hold the repository and its components
|
||||
String placeholder = "temp" + UUID.randomUUID();
|
||||
ArtifactJsonTransformationDTO tempJsonTransformationDTO =
|
||||
new ArtifactJsonTransformationDTO(workspaceId, placeholder, repoName);
|
||||
|
|
@ -203,7 +203,7 @@ public class CentralGitServiceCEImpl implements CentralGitServiceCE {
|
|||
.flatMap(defaultBranch -> {
|
||||
return Mono.zip(
|
||||
Mono.just(defaultBranch),
|
||||
gitHandlingService.obtainArtifactTypeFromGitRepository(
|
||||
gitHandlingService.obtainArtifactTypeAndIdentifierFromGitRepository(
|
||||
tempJsonTransformationDTO),
|
||||
isRepositoryPrivateMonoCached,
|
||||
Mono.just(gitAuth));
|
||||
|
|
@ -211,7 +211,8 @@ public class CentralGitServiceCEImpl implements CentralGitServiceCE {
|
|||
})
|
||||
.flatMap(tuple4 -> {
|
||||
String defaultBranch = tuple4.getT1();
|
||||
ArtifactType artifactType = tuple4.getT2();
|
||||
ArtifactType artifactType = tuple4.getT2().getT1();
|
||||
String uniqueIdentifier = tuple4.getT2().getT2();
|
||||
Boolean isRepoPrivate = tuple4.getT3();
|
||||
GitAuth gitAuth = tuple4.getT4();
|
||||
|
||||
|
|
@ -224,7 +225,8 @@ public class CentralGitServiceCEImpl implements CentralGitServiceCE {
|
|||
AppsmithError.NO_RESOURCE_FOUND, FieldName.WORKSPACE, workspaceId)));
|
||||
|
||||
return workspaceMono
|
||||
.flatMap(workspace -> contextHelper.createArtifactForImport(workspaceId, repoName))
|
||||
.flatMap(workspace -> createArtifactForGitConnect(
|
||||
gitConnectDTO, artifactType, workspaceId, repoName, uniqueIdentifier))
|
||||
.map(baseArtifact -> {
|
||||
GitArtifactMetadata gitArtifactMetadata = new GitArtifactMetadata();
|
||||
gitArtifactMetadata.setGitAuth(gitAuth);
|
||||
|
|
@ -240,6 +242,12 @@ public class CentralGitServiceCEImpl implements CentralGitServiceCE {
|
|||
baseArtifact.setGitArtifactMetadata(gitArtifactMetadata);
|
||||
return baseArtifact;
|
||||
});
|
||||
})
|
||||
.onErrorResume(error -> {
|
||||
Mono<Boolean> removeRepositoryMono =
|
||||
gitHandlingService.removeRepository(tempJsonTransformationDTO, TRUE);
|
||||
|
||||
return removeRepositoryMono.then(Mono.error(error));
|
||||
});
|
||||
|
||||
Mono<? extends Artifact> containerArtifactForImport = Mono.usingWhen(
|
||||
|
|
@ -267,7 +275,7 @@ public class CentralGitServiceCEImpl implements CentralGitServiceCE {
|
|||
GitArtifactHelper<?> gitArtifactHelper =
|
||||
gitArtifactHelperResolver.getArtifactHelper(baseArtifact.getArtifactType());
|
||||
|
||||
Mono<List<Datasource>> datasourceMono = datasourceService
|
||||
Mono<List<Datasource>> existingWorkspaceDatasourceMono = datasourceService
|
||||
.getAllByWorkspaceIdWithStorages(workspaceId, datasourcePermission.getEditPermission())
|
||||
.collectList();
|
||||
|
||||
|
|
@ -282,7 +290,7 @@ public class CentralGitServiceCEImpl implements CentralGitServiceCE {
|
|||
new AppsmithException(AppsmithError.GIT_FILE_SYSTEM_ERROR, error.getMessage()));
|
||||
});
|
||||
|
||||
return Mono.zip(artifactExchangeJsonMono, datasourceMono, pluginMono)
|
||||
return Mono.zip(artifactExchangeJsonMono, existingWorkspaceDatasourceMono, pluginMono)
|
||||
.flatMap(data -> {
|
||||
ArtifactExchangeJson artifactExchangeJson = data.getT1();
|
||||
List<Datasource> datasourceList = data.getT2();
|
||||
|
|
@ -312,7 +320,14 @@ public class CentralGitServiceCEImpl implements CentralGitServiceCE {
|
|||
.importArtifactInWorkspaceFromGit(
|
||||
workspaceId, baseArtifact.getId(), artifactExchangeJson, defaultBranch)
|
||||
.onErrorResume(throwable -> {
|
||||
log.error("Error in importing the artifact {}", baseArtifact.getId());
|
||||
log.error(
|
||||
"Error in importing the artifact {}",
|
||||
baseArtifact.getId(),
|
||||
throwable);
|
||||
if (throwable instanceof AppsmithException) {
|
||||
return Mono.error(throwable);
|
||||
}
|
||||
|
||||
return Mono.error(new AppsmithException(
|
||||
AppsmithError.GIT_FILE_SYSTEM_ERROR, throwable.getMessage()));
|
||||
});
|
||||
|
|
@ -343,183 +358,18 @@ public class CentralGitServiceCEImpl implements CentralGitServiceCE {
|
|||
sink -> importGitArtifactMono.subscribe(sink::success, sink::error, null, sink.currentContext()));
|
||||
}
|
||||
|
||||
protected Mono<Artifact> hydrateLatest(Artifact artifact) {
|
||||
return Mono.just(artifact);
|
||||
protected Mono<? extends Artifact> createArtifactForGitConnect(
|
||||
GitConnectDTO gitConnectDTO,
|
||||
ArtifactType artifactType,
|
||||
String workspaceId,
|
||||
String repoName,
|
||||
String uniqueIdentifier) {
|
||||
GitArtifactHelper<?> contextHelper = gitArtifactHelperResolver.getArtifactHelper(artifactType);
|
||||
return contextHelper.createArtifactForImport(workspaceId, repoName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<? extends ArtifactImportDTO> importArtifactFromGit(
|
||||
String workspaceId, GitConnectDTO gitConnectDTO, ArtifactType artifactType, GitType gitType) {
|
||||
// 1. Check private repo limit for workspace
|
||||
// 2. Create dummy artifact, clone repo from remote
|
||||
// 3. Re-hydrate artifact to DB from local repo
|
||||
// a. Save the ssh keys in artifact object with other details
|
||||
// b. During import-export need to handle the DS(empty vs non-empty)
|
||||
// 4. Return artifact
|
||||
|
||||
if (!StringUtils.hasText(workspaceId)) {
|
||||
return Mono.error(new AppsmithException(AppsmithError.INVALID_PARAMETER, "Invalid workspace id"));
|
||||
}
|
||||
|
||||
GitHandlingService gitHandlingService = gitHandlingServiceResolver.getGitHandlingService(gitType);
|
||||
Set<String> errors = gitHandlingService.validateGitConnectDTO(gitConnectDTO);
|
||||
|
||||
if (!CollectionUtils.isEmpty(errors)) {
|
||||
return Mono.error(new AppsmithException(
|
||||
AppsmithError.INVALID_PARAMETER, errors.stream().findAny().get()));
|
||||
}
|
||||
|
||||
GitArtifactHelper<?> gitArtifactHelper = gitArtifactHelperResolver.getArtifactHelper(artifactType);
|
||||
AclPermission artifactCreatePermission = gitArtifactHelper.getWorkspaceArtifactCreationPermission();
|
||||
|
||||
// TODO: permission bit deferred to gitArtifactHelper
|
||||
Mono<Workspace> workspaceMono = workspaceService
|
||||
.findById(workspaceId, artifactCreatePermission)
|
||||
.switchIfEmpty(Mono.error(
|
||||
new AppsmithException(AppsmithError.NO_RESOURCE_FOUND, FieldName.WORKSPACE, workspaceId)));
|
||||
|
||||
final String repoName = gitHandlingService.getRepoName(gitConnectDTO);
|
||||
Mono<Boolean> isRepositoryPrivateMono =
|
||||
gitHandlingService.isRepoPrivate(gitConnectDTO).cache();
|
||||
Mono<Boolean> isRepositoryLimitReachedForWorkspaceMono = isRepositoryPrivateMono.flatMap(
|
||||
isRepositoryPrivate -> isRepositoryLimitReachedForWorkspace(workspaceId, isRepositoryPrivate));
|
||||
|
||||
Mono<? extends ArtifactImportDTO> importedArtifactMono = workspaceMono
|
||||
.then(Mono.defer(() -> isRepositoryLimitReachedForWorkspaceMono))
|
||||
.flatMap(isRepositoryLimitReached -> {
|
||||
Mono<GitAuth> gitAuthForUserMono =
|
||||
gitHandlingService.getGitAuthForUser().cache();
|
||||
Mono<? extends Artifact> createArtifactMono = gitArtifactHelper
|
||||
.createArtifactForImport(workspaceId, repoName)
|
||||
.cache();
|
||||
|
||||
if (FALSE.equals(isRepositoryLimitReached)) {
|
||||
return gitAuthForUserMono.zipWith(createArtifactMono);
|
||||
}
|
||||
|
||||
// TODO: Change errors to artifact level.
|
||||
return gitAnalyticsUtils
|
||||
.addAnalyticsForGitOperation(
|
||||
AnalyticsEvents.GIT_IMPORT,
|
||||
gitArtifactHelper.getNewArtifact(workspaceId, repoName),
|
||||
AppsmithError.GIT_APPLICATION_LIMIT_ERROR.getErrorType(),
|
||||
AppsmithError.GIT_APPLICATION_LIMIT_ERROR.getMessage(),
|
||||
true)
|
||||
.then(Mono.error(new AppsmithException(AppsmithError.GIT_APPLICATION_LIMIT_ERROR)));
|
||||
})
|
||||
.flatMap(tuple2 -> {
|
||||
GitAuth gitAuth = tuple2.getT1();
|
||||
Artifact artifact = tuple2.getT2();
|
||||
|
||||
Mono<Map<String, GitProfile>> profileMono = gitProfileUtils.updateOrCreateGitProfileForCurrentUser(
|
||||
gitConnectDTO.getGitProfile(), artifact.getId());
|
||||
|
||||
Mono<String> fetchRemoteRepository =
|
||||
gitHandlingService.fetchRemoteRepository(gitConnectDTO, gitAuth, artifact, repoName);
|
||||
|
||||
return fetchRemoteRepository
|
||||
.zipWith(isRepositoryPrivateMono)
|
||||
.flatMap(tuple -> {
|
||||
String defaultBranch = tuple.getT1();
|
||||
Boolean isRepoPrivate = tuple.getT2();
|
||||
|
||||
GitArtifactMetadata gitArtifactMetadata = new GitArtifactMetadata();
|
||||
gitArtifactMetadata.setGitAuth(gitAuth);
|
||||
gitArtifactMetadata.setDefaultArtifactId(artifact.getId());
|
||||
gitArtifactMetadata.setDefaultBranchName(defaultBranch);
|
||||
gitArtifactMetadata.setRefName(defaultBranch);
|
||||
gitArtifactMetadata.setRepoName(repoName);
|
||||
gitArtifactMetadata.setIsRepoPrivate(isRepoPrivate);
|
||||
gitArtifactMetadata.setLastCommittedAt(Instant.now());
|
||||
|
||||
gitHandlingService.setRepositoryDetailsInGitArtifactMetadata(
|
||||
gitConnectDTO, gitArtifactMetadata);
|
||||
artifact.setGitArtifactMetadata(gitArtifactMetadata);
|
||||
return Mono.just(artifact).zipWith(profileMono);
|
||||
});
|
||||
})
|
||||
.flatMap(tuple2 -> {
|
||||
Artifact artifact = tuple2.getT1();
|
||||
GitArtifactMetadata gitArtifactMetadata = artifact.getGitArtifactMetadata();
|
||||
String defaultBranch = gitArtifactMetadata.getDefaultBranchName();
|
||||
|
||||
Mono<List<Datasource>> datasourceMono = datasourceService
|
||||
.getAllByWorkspaceIdWithStorages(workspaceId, datasourcePermission.getEditPermission())
|
||||
.collectList();
|
||||
|
||||
Mono<List<Plugin>> pluginMono =
|
||||
pluginService.getDefaultPlugins().collectList();
|
||||
|
||||
ArtifactJsonTransformationDTO jsonMorphDTO = new ArtifactJsonTransformationDTO();
|
||||
jsonMorphDTO.setWorkspaceId(workspaceId);
|
||||
jsonMorphDTO.setBaseArtifactId(artifact.getId());
|
||||
jsonMorphDTO.setArtifactType(artifactType);
|
||||
jsonMorphDTO.setRepoName(gitArtifactMetadata.getRepoName());
|
||||
jsonMorphDTO.setRefType(RefType.branch);
|
||||
jsonMorphDTO.setRefName(defaultBranch);
|
||||
|
||||
Mono<? extends ArtifactExchangeJson> artifactExchangeJsonMono = gitHandlingService
|
||||
.reconstructArtifactJsonFromGitRepository(jsonMorphDTO)
|
||||
.onErrorResume(error -> {
|
||||
log.error("Error while constructing artifact from git repo", error);
|
||||
return deleteArtifactCreatedFromGitImport(jsonMorphDTO, gitType)
|
||||
.then(Mono.error(new AppsmithException(
|
||||
AppsmithError.GIT_FILE_SYSTEM_ERROR, error.getMessage())));
|
||||
});
|
||||
|
||||
return Mono.zip(artifactExchangeJsonMono, datasourceMono, pluginMono)
|
||||
.flatMap(data -> {
|
||||
ArtifactExchangeJson artifactExchangeJson = data.getT1();
|
||||
List<Datasource> datasourceList = data.getT2();
|
||||
List<Plugin> pluginList = data.getT3();
|
||||
|
||||
if (artifactExchangeJson.getArtifact() == null
|
||||
|| gitArtifactHelper.isContextInArtifactEmpty(artifactExchangeJson)) {
|
||||
return deleteArtifactCreatedFromGitImport(jsonMorphDTO, gitType)
|
||||
.then(Mono.error(new AppsmithException(
|
||||
AppsmithError.GIT_ACTION_FAILED,
|
||||
"import",
|
||||
"Cannot import artifact from an empty repo")));
|
||||
}
|
||||
// If there is an existing datasource with the same name but a different type from that
|
||||
// in the repo, the import api should fail
|
||||
// TODO: change the implementation to compare datasource with gitSyncIds instead.
|
||||
if (checkIsDatasourceNameConflict(
|
||||
datasourceList, artifactExchangeJson.getDatasourceList(), pluginList)) {
|
||||
return deleteArtifactCreatedFromGitImport(jsonMorphDTO, gitType)
|
||||
.then(Mono.error(new AppsmithException(
|
||||
AppsmithError.GIT_ACTION_FAILED,
|
||||
"import",
|
||||
"Datasource already exists with the same name")));
|
||||
}
|
||||
|
||||
artifactExchangeJson.getArtifact().setGitArtifactMetadata(gitArtifactMetadata);
|
||||
return importService
|
||||
.importArtifactInWorkspaceFromGit(
|
||||
workspaceId, artifact.getId(), artifactExchangeJson, defaultBranch)
|
||||
.onErrorResume(throwable -> deleteArtifactCreatedFromGitImport(
|
||||
jsonMorphDTO, gitType)
|
||||
.then(Mono.error(new AppsmithException(
|
||||
AppsmithError.GIT_FILE_SYSTEM_ERROR, throwable.getMessage()))));
|
||||
});
|
||||
})
|
||||
.flatMap(artifact -> gitArtifactHelper.publishArtifact(artifact, false))
|
||||
// Add un-configured datasource to the list to response
|
||||
.flatMap(artifact -> importService.getArtifactImportDTO(
|
||||
artifact.getWorkspaceId(), artifact.getId(), artifact, artifactType))
|
||||
// Add analytics event
|
||||
.flatMap(artifactImportDTO -> {
|
||||
Artifact artifact = artifactImportDTO.getArtifact();
|
||||
return gitAnalyticsUtils
|
||||
.addAnalyticsForGitOperation(
|
||||
AnalyticsEvents.GIT_IMPORT,
|
||||
artifact,
|
||||
artifact.getGitArtifactMetadata().getIsRepoPrivate())
|
||||
.thenReturn(artifactImportDTO);
|
||||
});
|
||||
|
||||
return Mono.create(
|
||||
sink -> importedArtifactMono.subscribe(sink::success, sink::error, null, sink.currentContext()));
|
||||
protected Mono<Artifact> hydrateLatest(Artifact artifact) {
|
||||
return Mono.just(artifact);
|
||||
}
|
||||
|
||||
private Mono<? extends Artifact> deleteArtifactCreatedFromGitImport(
|
||||
|
|
|
|||
|
|
@ -41,6 +41,9 @@ public interface GitHandlingServiceCE {
|
|||
|
||||
Mono<ArtifactType> obtainArtifactTypeFromGitRepository(ArtifactJsonTransformationDTO jsonTransformationDTO);
|
||||
|
||||
Mono<Tuple2<ArtifactType, String>> obtainArtifactTypeAndIdentifierFromGitRepository(
|
||||
ArtifactJsonTransformationDTO jsonTransformationDTO);
|
||||
|
||||
Mono<String> fetchRemoteRepository(
|
||||
GitConnectDTO gitConnectDTO, GitAuth gitAuth, ArtifactJsonTransformationDTO jsonTransformationDTO);
|
||||
|
||||
|
|
@ -53,6 +56,9 @@ public interface GitHandlingServiceCE {
|
|||
void setRepositoryDetailsInGitArtifactMetadata(
|
||||
GitConnectDTO gitConnectDTO, GitArtifactMetadata gitArtifactMetadata);
|
||||
|
||||
Mono<Boolean> removeRepository(
|
||||
ArtifactJsonTransformationDTO artifactJsonTransformationDTO, Boolean isArtifactTypeUnknown);
|
||||
|
||||
Mono<Boolean> removeRepository(ArtifactJsonTransformationDTO artifactJsonTransformationDTO);
|
||||
|
||||
Mono<List<GitRefDTO>> listBranches(
|
||||
|
|
|
|||
|
|
@ -1,31 +1,18 @@
|
|||
package com.appsmith.server.git.fs;
|
||||
|
||||
import com.appsmith.external.git.handler.FSGitHandler;
|
||||
import com.appsmith.server.configurations.EmailConfig;
|
||||
import com.appsmith.server.datasources.base.DatasourceService;
|
||||
import com.appsmith.server.exports.internal.ExportService;
|
||||
import com.appsmith.server.git.GitRedisUtils;
|
||||
import com.appsmith.server.git.autocommit.helpers.GitAutoCommitHelper;
|
||||
import com.appsmith.server.git.central.GitHandlingServiceCECompatible;
|
||||
import com.appsmith.server.git.resolver.GitArtifactHelperResolver;
|
||||
import com.appsmith.server.git.utils.GitAnalyticsUtils;
|
||||
import com.appsmith.server.git.utils.GitProfileUtils;
|
||||
import com.appsmith.server.helpers.CommonGitFileUtils;
|
||||
import com.appsmith.server.helpers.GitPrivateRepoHelper;
|
||||
import com.appsmith.server.imports.internal.ImportService;
|
||||
import com.appsmith.server.plugins.base.PluginService;
|
||||
import com.appsmith.server.repositories.GitDeployKeysRepository;
|
||||
import com.appsmith.server.services.AnalyticsService;
|
||||
import com.appsmith.server.services.FeatureFlagService;
|
||||
import com.appsmith.server.services.SessionUserService;
|
||||
import com.appsmith.server.services.UserDataService;
|
||||
import com.appsmith.server.services.UserService;
|
||||
import com.appsmith.server.services.WorkspaceService;
|
||||
import com.appsmith.server.solutions.DatasourcePermission;
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.reactive.TransactionalOperator;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
|
|
@ -33,49 +20,23 @@ public class GitFSServiceCECompatibleImpl extends GitFSServiceCEImpl implements
|
|||
|
||||
public GitFSServiceCECompatibleImpl(
|
||||
GitDeployKeysRepository gitDeployKeysRepository,
|
||||
GitPrivateRepoHelper gitPrivateRepoHelper,
|
||||
CommonGitFileUtils commonGitFileUtils,
|
||||
GitRedisUtils gitRedisUtils,
|
||||
SessionUserService sessionUserService,
|
||||
UserDataService userDataService,
|
||||
UserService userService,
|
||||
EmailConfig emailConfig,
|
||||
TransactionalOperator transactionalOperator,
|
||||
AnalyticsService analyticsService,
|
||||
ObservationRegistry observationRegistry,
|
||||
WorkspaceService workspaceService,
|
||||
DatasourceService datasourceService,
|
||||
DatasourcePermission datasourcePermission,
|
||||
PluginService pluginService,
|
||||
ExportService exportService,
|
||||
ImportService importService,
|
||||
FSGitHandler fsGitHandler,
|
||||
GitAutoCommitHelper gitAutoCommitHelper,
|
||||
GitProfileUtils gitProfileUtils,
|
||||
GitAnalyticsUtils gitAnalyticsUtils,
|
||||
GitArtifactHelperResolver gitArtifactHelperResolver,
|
||||
FeatureFlagService featureFlagService) {
|
||||
super(
|
||||
gitDeployKeysRepository,
|
||||
gitPrivateRepoHelper,
|
||||
commonGitFileUtils,
|
||||
gitRedisUtils,
|
||||
sessionUserService,
|
||||
userDataService,
|
||||
userService,
|
||||
emailConfig,
|
||||
transactionalOperator,
|
||||
analyticsService,
|
||||
observationRegistry,
|
||||
workspaceService,
|
||||
datasourceService,
|
||||
datasourcePermission,
|
||||
pluginService,
|
||||
exportService,
|
||||
importService,
|
||||
fsGitHandler,
|
||||
gitAutoCommitHelper,
|
||||
gitProfileUtils,
|
||||
gitAnalyticsUtils,
|
||||
gitArtifactHelperResolver,
|
||||
featureFlagService);
|
||||
|
|
|
|||
|
|
@ -11,9 +11,7 @@ import com.appsmith.external.git.constants.ce.RefType;
|
|||
import com.appsmith.external.git.dtos.FetchRemoteDTO;
|
||||
import com.appsmith.external.git.handler.FSGitHandler;
|
||||
import com.appsmith.git.dto.CommitDTO;
|
||||
import com.appsmith.server.configurations.EmailConfig;
|
||||
import com.appsmith.server.constants.ArtifactType;
|
||||
import com.appsmith.server.datasources.base.DatasourceService;
|
||||
import com.appsmith.server.domains.Artifact;
|
||||
import com.appsmith.server.domains.GitArtifactMetadata;
|
||||
import com.appsmith.server.domains.GitAuth;
|
||||
|
|
@ -23,29 +21,19 @@ import com.appsmith.server.dtos.GitConnectDTO;
|
|||
import com.appsmith.server.dtos.GitMergeDTO;
|
||||
import com.appsmith.server.exceptions.AppsmithError;
|
||||
import com.appsmith.server.exceptions.AppsmithException;
|
||||
import com.appsmith.server.exports.internal.ExportService;
|
||||
import com.appsmith.server.git.GitRedisUtils;
|
||||
import com.appsmith.server.git.autocommit.helpers.GitAutoCommitHelper;
|
||||
import com.appsmith.server.git.central.GitHandlingServiceCE;
|
||||
import com.appsmith.server.git.dtos.ArtifactJsonTransformationDTO;
|
||||
import com.appsmith.server.git.resolver.GitArtifactHelperResolver;
|
||||
import com.appsmith.server.git.utils.GitAnalyticsUtils;
|
||||
import com.appsmith.server.git.utils.GitProfileUtils;
|
||||
import com.appsmith.server.helpers.CollectionUtils;
|
||||
import com.appsmith.server.helpers.CommonGitFileUtils;
|
||||
import com.appsmith.server.helpers.GitPrivateRepoHelper;
|
||||
import com.appsmith.server.helpers.GitUtils;
|
||||
import com.appsmith.server.imports.internal.ImportService;
|
||||
import com.appsmith.server.plugins.base.PluginService;
|
||||
import com.appsmith.server.repositories.GitDeployKeysRepository;
|
||||
import com.appsmith.server.services.AnalyticsService;
|
||||
import com.appsmith.server.services.FeatureFlagService;
|
||||
import com.appsmith.server.services.GitArtifactHelper;
|
||||
import com.appsmith.server.services.SessionUserService;
|
||||
import com.appsmith.server.services.UserDataService;
|
||||
import com.appsmith.server.services.UserService;
|
||||
import com.appsmith.server.services.WorkspaceService;
|
||||
import com.appsmith.server.solutions.DatasourcePermission;
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
|
@ -55,7 +43,6 @@ import org.eclipse.jgit.api.errors.InvalidRemoteException;
|
|||
import org.eclipse.jgit.api.errors.TransportException;
|
||||
import org.eclipse.jgit.errors.RepositoryNotFoundException;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.reactive.TransactionalOperator;
|
||||
import org.springframework.util.StringUtils;
|
||||
import reactor.core.observability.micrometer.Micrometer;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
|
@ -79,30 +66,14 @@ import static java.lang.Boolean.TRUE;
|
|||
public class GitFSServiceCEImpl implements GitHandlingServiceCE {
|
||||
|
||||
private final GitDeployKeysRepository gitDeployKeysRepository;
|
||||
private final GitPrivateRepoHelper gitPrivateRepoHelper;
|
||||
private final CommonGitFileUtils commonGitFileUtils;
|
||||
private final GitRedisUtils gitRedisUtils;
|
||||
protected final CommonGitFileUtils commonGitFileUtils;
|
||||
protected final GitRedisUtils gitRedisUtils;
|
||||
protected final SessionUserService sessionUserService;
|
||||
private final UserDataService userDataService;
|
||||
protected final UserService userService;
|
||||
private final EmailConfig emailConfig;
|
||||
private final TransactionalOperator transactionalOperator;
|
||||
|
||||
protected final AnalyticsService analyticsService;
|
||||
private final ObservationRegistry observationRegistry;
|
||||
|
||||
private final WorkspaceService workspaceService;
|
||||
private final DatasourceService datasourceService;
|
||||
private final DatasourcePermission datasourcePermission;
|
||||
private final PluginService pluginService;
|
||||
|
||||
private final ExportService exportService;
|
||||
private final ImportService importService;
|
||||
|
||||
protected final FSGitHandler fsGitHandler;
|
||||
private final GitAutoCommitHelper gitAutoCommitHelper;
|
||||
|
||||
private final GitProfileUtils gitProfileUtils;
|
||||
private final GitAnalyticsUtils gitAnalyticsUtils;
|
||||
|
||||
protected final GitArtifactHelperResolver gitArtifactHelperResolver;
|
||||
|
|
@ -202,6 +173,12 @@ public class GitFSServiceCEImpl implements GitHandlingServiceCE {
|
|||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Tuple2<ArtifactType, String>> obtainArtifactTypeAndIdentifierFromGitRepository(
|
||||
ArtifactJsonTransformationDTO jsonTransformationDTO) {
|
||||
return obtainArtifactTypeFromGitRepository(jsonTransformationDTO).zipWith(Mono.just(""));
|
||||
}
|
||||
|
||||
public Mono<ArtifactType> obtainArtifactTypeFromGitRepository(ArtifactJsonTransformationDTO jsonTransformationDTO) {
|
||||
String workspaceId = jsonTransformationDTO.getWorkspaceId();
|
||||
String placeHolder = jsonTransformationDTO.getBaseArtifactId();
|
||||
|
|
@ -292,6 +269,17 @@ public class GitFSServiceCEImpl implements GitHandlingServiceCE {
|
|||
artifactJsonTransformationDTO));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Boolean> removeRepository(
|
||||
ArtifactJsonTransformationDTO artifactJsonTransformationDTO, Boolean isArtifactTypeUnknown) {
|
||||
// Since the artifact type is unknown, we can assume that the repository is yet to be
|
||||
if (TRUE.equals(isArtifactTypeUnknown)) {
|
||||
artifactJsonTransformationDTO.setArtifactType(ArtifactType.APPLICATION);
|
||||
}
|
||||
|
||||
return removeRepository(artifactJsonTransformationDTO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Boolean> removeRepository(ArtifactJsonTransformationDTO artifactJsonTransformationDTO) {
|
||||
GitArtifactHelper<?> gitArtifactHelper =
|
||||
|
|
|
|||
|
|
@ -1,31 +1,18 @@
|
|||
package com.appsmith.server.git.fs;
|
||||
|
||||
import com.appsmith.external.git.handler.FSGitHandler;
|
||||
import com.appsmith.server.configurations.EmailConfig;
|
||||
import com.appsmith.server.datasources.base.DatasourceService;
|
||||
import com.appsmith.server.exports.internal.ExportService;
|
||||
import com.appsmith.server.git.GitRedisUtils;
|
||||
import com.appsmith.server.git.autocommit.helpers.GitAutoCommitHelper;
|
||||
import com.appsmith.server.git.central.GitHandlingService;
|
||||
import com.appsmith.server.git.resolver.GitArtifactHelperResolver;
|
||||
import com.appsmith.server.git.utils.GitAnalyticsUtils;
|
||||
import com.appsmith.server.git.utils.GitProfileUtils;
|
||||
import com.appsmith.server.helpers.CommonGitFileUtils;
|
||||
import com.appsmith.server.helpers.GitPrivateRepoHelper;
|
||||
import com.appsmith.server.imports.internal.ImportService;
|
||||
import com.appsmith.server.plugins.base.PluginService;
|
||||
import com.appsmith.server.repositories.GitDeployKeysRepository;
|
||||
import com.appsmith.server.services.AnalyticsService;
|
||||
import com.appsmith.server.services.FeatureFlagService;
|
||||
import com.appsmith.server.services.SessionUserService;
|
||||
import com.appsmith.server.services.UserDataService;
|
||||
import com.appsmith.server.services.UserService;
|
||||
import com.appsmith.server.services.WorkspaceService;
|
||||
import com.appsmith.server.solutions.DatasourcePermission;
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.reactive.TransactionalOperator;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
|
|
@ -33,49 +20,23 @@ public class GitFSServiceImpl extends GitFSServiceCECompatibleImpl implements Gi
|
|||
|
||||
public GitFSServiceImpl(
|
||||
GitDeployKeysRepository gitDeployKeysRepository,
|
||||
GitPrivateRepoHelper gitPrivateRepoHelper,
|
||||
CommonGitFileUtils commonGitFileUtils,
|
||||
GitRedisUtils gitRedisUtils,
|
||||
SessionUserService sessionUserService,
|
||||
UserDataService userDataService,
|
||||
UserService userService,
|
||||
EmailConfig emailConfig,
|
||||
TransactionalOperator transactionalOperator,
|
||||
AnalyticsService analyticsService,
|
||||
ObservationRegistry observationRegistry,
|
||||
WorkspaceService workspaceService,
|
||||
DatasourceService datasourceService,
|
||||
DatasourcePermission datasourcePermission,
|
||||
PluginService pluginService,
|
||||
ExportService exportService,
|
||||
ImportService importService,
|
||||
FSGitHandler fsGitHandler,
|
||||
GitAutoCommitHelper gitAutoCommitHelper,
|
||||
GitProfileUtils gitProfileUtils,
|
||||
GitAnalyticsUtils gitAnalyticsUtils,
|
||||
GitArtifactHelperResolver gitArtifactHelperResolver,
|
||||
FeatureFlagService featureFlagService) {
|
||||
super(
|
||||
gitDeployKeysRepository,
|
||||
gitPrivateRepoHelper,
|
||||
commonGitFileUtils,
|
||||
gitRedisUtils,
|
||||
sessionUserService,
|
||||
userDataService,
|
||||
userService,
|
||||
emailConfig,
|
||||
transactionalOperator,
|
||||
analyticsService,
|
||||
observationRegistry,
|
||||
workspaceService,
|
||||
datasourceService,
|
||||
datasourcePermission,
|
||||
pluginService,
|
||||
exportService,
|
||||
importService,
|
||||
fsGitHandler,
|
||||
gitAutoCommitHelper,
|
||||
gitProfileUtils,
|
||||
gitAnalyticsUtils,
|
||||
gitArtifactHelperResolver,
|
||||
featureFlagService);
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@ public class CommonGitFileUtilsCE {
|
|||
|
||||
protected final ArtifactGitFileUtils<ApplicationJson> applicationGitFileUtils;
|
||||
protected final GitServiceConfig gitServiceConfig;
|
||||
private final FileInterface fileUtils;
|
||||
protected final FileInterface fileUtils;
|
||||
private final FileOperations fileOperations;
|
||||
private final AnalyticsService analyticsService;
|
||||
private final SessionUserService sessionUserService;
|
||||
|
|
@ -830,7 +830,7 @@ public class CommonGitFileUtilsCE {
|
|||
"Error while moving repository from temporary storage {} to permanent storage {}",
|
||||
currentGitPath,
|
||||
targetPath,
|
||||
error.getMessage());
|
||||
error);
|
||||
return Mono.error(error);
|
||||
})
|
||||
.subscribeOn(Schedulers.boundedElastic());
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ import lombok.RequiredArgsConstructor;
|
|||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.core.io.buffer.DataBufferUtils;
|
||||
import org.springframework.dao.DuplicateKeyException;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.codec.multipart.Part;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
|
@ -530,6 +531,11 @@ public class ImportServiceCEImpl implements ImportServiceCE {
|
|||
.onErrorResume(throwable -> {
|
||||
String errorMessage = ImportExportUtils.getErrorMessage(throwable);
|
||||
log.error("Error importing {}. Error: {}", artifactContextString, errorMessage, throwable);
|
||||
|
||||
if (throwable instanceof DuplicateKeyException) {
|
||||
return Mono.error(new AppsmithException(
|
||||
AppsmithError.ARTIFACT_IMPORT_DUPLICATE_KEY_WRITE_ERROR, "Duplicate artifact key"));
|
||||
}
|
||||
return Mono.error(new AppsmithException(
|
||||
AppsmithError.GENERIC_JSON_IMPORT_ERROR, workspaceId, errorMessage));
|
||||
})
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user