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 {
|
import {
|
||||||
GitRepoLimitErrorModal as NewGitRepoLimitErrorModal,
|
GitRepoLimitErrorModal as NewGitRepoLimitErrorModal,
|
||||||
GitImportModal as NewGitImportModal,
|
GitImportModal as NewGitImportModal,
|
||||||
|
GitImportOverrideModal,
|
||||||
} from "git";
|
} from "git";
|
||||||
import OldRepoLimitExceededErrorModal from "pages/Editor/gitSync/RepoLimitExceededErrorModal";
|
import OldRepoLimitExceededErrorModal from "pages/Editor/gitSync/RepoLimitExceededErrorModal";
|
||||||
|
|
||||||
|
|
@ -145,6 +146,7 @@ function GitModals() {
|
||||||
<>
|
<>
|
||||||
<NewGitImportModal />
|
<NewGitImportModal />
|
||||||
<NewGitRepoLimitErrorModal />
|
<NewGitRepoLimitErrorModal />
|
||||||
|
<GitImportOverrideModal />
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,14 @@ export const IMPORT_GIT = {
|
||||||
WAIT_TEXT: "Please wait while we import via 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 = {
|
export const CONNECT_GIT = {
|
||||||
MODAL_TITLE: "Configure Git",
|
MODAL_TITLE: "Configure Git",
|
||||||
CHOOSE_PROVIDER_CTA: "Configure Git",
|
CHOOSE_PROVIDER_CTA: "Configure Git",
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ function ImportModal() {
|
||||||
gitImportError,
|
gitImportError,
|
||||||
isGitImportLoading,
|
isGitImportLoading,
|
||||||
isImportModalOpen,
|
isImportModalOpen,
|
||||||
|
resetGitImport,
|
||||||
toggleImportModal,
|
toggleImportModal,
|
||||||
} = useImport();
|
} = useImport();
|
||||||
const {
|
const {
|
||||||
|
|
@ -24,11 +25,16 @@ function ImportModal() {
|
||||||
|
|
||||||
const onSubmit = useCallback(
|
const onSubmit = useCallback(
|
||||||
(params: GitImportRequestParams) => {
|
(params: GitImportRequestParams) => {
|
||||||
gitImport(params);
|
gitImport({ ...params, override: false });
|
||||||
},
|
},
|
||||||
[gitImport],
|
[gitImport],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const resetConnectState = useCallback(() => {
|
||||||
|
resetGlobalSSHKey();
|
||||||
|
resetGitImport();
|
||||||
|
}, [resetGitImport, resetGlobalSSHKey]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ConnectModalView
|
<ConnectModalView
|
||||||
artifactType="artifact"
|
artifactType="artifact"
|
||||||
|
|
@ -41,7 +47,7 @@ function ImportModal() {
|
||||||
onGenerateSSHKey={fetchGlobalSSHKey}
|
onGenerateSSHKey={fetchGlobalSSHKey}
|
||||||
onOpenImport={null}
|
onOpenImport={null}
|
||||||
onSubmit={onSubmit}
|
onSubmit={onSubmit}
|
||||||
resetConnectState={resetGlobalSSHKey}
|
resetConnectState={resetConnectState}
|
||||||
sshPublicKey={sshPublicKey}
|
sshPublicKey={sshPublicKey}
|
||||||
toggleModalOpen={toggleImportModal}
|
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_NOT_EMPTY = "AE-GIT-4033",
|
||||||
REPO_LIMIT_REACHED = "AE-GIT-4043",
|
REPO_LIMIT_REACHED = "AE-GIT-4043",
|
||||||
PUSH_FAILED_REMOTE_COUNTERPART_IS_AHEAD = "AE-GIT-4048",
|
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 {
|
import {
|
||||||
selectGitImportState,
|
selectGitImportState,
|
||||||
selectImportModalOpen,
|
selectImportModalOpen,
|
||||||
|
selectImportOverrideDetails,
|
||||||
|
selectImportOverrideModalOpen,
|
||||||
} from "git/store/selectors/gitGlobalSelectors";
|
} from "git/store/selectors/gitGlobalSelectors";
|
||||||
import { gitGlobalActions } from "git/store/gitGlobalSlice";
|
import { gitGlobalActions } from "git/store/gitGlobalSlice";
|
||||||
import type { GitImportRequestParams } from "git/requests/gitImportRequest.types";
|
import type { GitImportRequestParams } from "git/requests/gitImportRequest.types";
|
||||||
|
import type { SetImportOverrideDetailsPayload } from "git/store/actions/uiActions";
|
||||||
|
|
||||||
export default function useImport() {
|
export default function useImport() {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
|
@ -19,6 +22,10 @@ export default function useImport() {
|
||||||
[dispatch],
|
[dispatch],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const resetGitImport = useCallback(() => {
|
||||||
|
dispatch(gitGlobalActions.resetGitImport());
|
||||||
|
}, [dispatch]);
|
||||||
|
|
||||||
const isImportModalOpen = useSelector(selectImportModalOpen);
|
const isImportModalOpen = useSelector(selectImportModalOpen);
|
||||||
|
|
||||||
const toggleImportModal = useCallback(
|
const toggleImportModal = useCallback(
|
||||||
|
|
@ -28,11 +35,31 @@ export default function useImport() {
|
||||||
[dispatch],
|
[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 {
|
return {
|
||||||
isGitImportLoading: gitImportState?.loading ?? false,
|
isGitImportLoading: gitImportState?.loading ?? false,
|
||||||
gitImportError: gitImportState?.error ?? null,
|
gitImportError: gitImportState?.error ?? null,
|
||||||
gitImport,
|
gitImport,
|
||||||
|
resetGitImport,
|
||||||
isImportModalOpen: isImportModalOpen ?? false,
|
isImportModalOpen: isImportModalOpen ?? false,
|
||||||
toggleImportModal,
|
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 GitContextProvider } from "./components/GitContextProvider";
|
||||||
export { default as GitModals } from "./ee/components/GitModals";
|
export { default as GitModals } from "./ee/components/GitModals";
|
||||||
export { default as GitImportModal } from "./components/ImportModal";
|
export { default as GitImportModal } from "./components/ImportModal";
|
||||||
|
export { default as GitImportOverrideModal } from "./components/ImportOverrideModal";
|
||||||
export { default as GitRepoLimitErrorModal } from "./components/RepoLimitErrorModal";
|
export { default as GitRepoLimitErrorModal } from "./components/RepoLimitErrorModal";
|
||||||
export { default as GitQuickActions } from "./components/QuickActions";
|
export { default as GitQuickActions } from "./components/QuickActions";
|
||||||
export { default as GitProtectedBranchCallout } from "./components/ProtectedBranchCallout";
|
export { default as GitProtectedBranchCallout } from "./components/ProtectedBranchCallout";
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,10 @@ async function gitImportRequestNew(
|
||||||
workspaceId: string,
|
workspaceId: string,
|
||||||
params: GitImportRequestParams,
|
params: GitImportRequestParams,
|
||||||
): AxiosPromise<GitImportResponse> {
|
): 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(
|
export default async function gitImportRequest(
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ export interface GitImportRequestParams {
|
||||||
authorEmail: string;
|
authorEmail: string;
|
||||||
useDefaultProfile?: boolean;
|
useDefaultProfile?: boolean;
|
||||||
};
|
};
|
||||||
|
override?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GitImportResponseData {
|
export interface GitImportResponseData {
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,17 @@ import { call, put, select } from "redux-saga/effects";
|
||||||
import { validateResponse } from "sagas/ErrorSagas";
|
import { validateResponse } from "sagas/ErrorSagas";
|
||||||
import type { PayloadAction } from "@reduxjs/toolkit";
|
import type { PayloadAction } from "@reduxjs/toolkit";
|
||||||
import gitImportRequest from "git/requests/gitImportRequest";
|
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 type { GitImportInitPayload } from "git/store/actions/gitImportActions";
|
||||||
import { gitGlobalActions } from "git/store/gitGlobalSlice";
|
import { gitGlobalActions } from "git/store/gitGlobalSlice";
|
||||||
import { getWorkspaceIdForImport } from "ee/selectors/applicationSelectors";
|
import { getWorkspaceIdForImport } from "ee/selectors/applicationSelectors";
|
||||||
import { GitErrorCodes } from "git/constants/enums";
|
import { GitErrorCodes } from "git/constants/enums";
|
||||||
import { selectGitApiContractsEnabled } from "git/store/selectors/gitFeatureFlagSelectors";
|
import { selectGitApiContractsEnabled } from "git/store/selectors/gitFeatureFlagSelectors";
|
||||||
import handleApiErrors from "./helpers/handleApiErrors";
|
import handleApiErrors from "./helpers/handleApiErrors";
|
||||||
|
import type { GitApiError } from "git/store/types";
|
||||||
|
|
||||||
export default function* gitImportSaga(
|
export default function* gitImportSaga(
|
||||||
action: PayloadAction<GitImportInitPayload>,
|
action: PayloadAction<GitImportInitPayload>,
|
||||||
|
|
@ -36,6 +40,7 @@ export default function* gitImportSaga(
|
||||||
gitGlobalActions.gitImportSuccess({ responseData: response.data }),
|
gitGlobalActions.gitImportSuccess({ responseData: response.data }),
|
||||||
);
|
);
|
||||||
yield put(gitGlobalActions.toggleImportModal({ open: false }));
|
yield put(gitGlobalActions.toggleImportModal({ open: false }));
|
||||||
|
yield put(gitGlobalActions.resetImportOverrideDetails());
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const error = handleApiErrors(e as Error, response);
|
const error = handleApiErrors(e as Error, response);
|
||||||
|
|
@ -47,6 +52,38 @@ export default function* gitImportSaga(
|
||||||
yield put(gitGlobalActions.toggleImportModal({ open: false }));
|
yield put(gitGlobalActions.toggleImportModal({ open: false }));
|
||||||
yield put(gitGlobalActions.toggleRepoLimitErrorModal({ open: true }));
|
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;
|
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 { GitGlobalReduxState } from "../types";
|
||||||
import type { PayloadAction } from "@reduxjs/toolkit";
|
import type { PayloadAction } from "@reduxjs/toolkit";
|
||||||
import type { GitArtifactDef } from "git/types";
|
import type { GitArtifactDef } from "git/types";
|
||||||
|
import type { GitImportRequestParams } from "git/requests/gitImportRequest.types";
|
||||||
|
|
||||||
// connect modal
|
// connect modal
|
||||||
export interface ToggleConnectModalPayload {
|
export interface ToggleConnectModalPayload {
|
||||||
|
|
@ -31,6 +32,7 @@ export const toggleConnectSuccessModalAction =
|
||||||
return state;
|
return state;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// import
|
||||||
export interface ToggleImportModalPayload {
|
export interface ToggleImportModalPayload {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
}
|
}
|
||||||
|
|
@ -46,6 +48,29 @@ export const toggleImportModalAction = (
|
||||||
return state;
|
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
|
// disconnect modal
|
||||||
export interface OpenDisconnectModalPayload {
|
export interface OpenDisconnectModalPayload {
|
||||||
targetArtifactDef: GitArtifactDef;
|
targetArtifactDef: GitArtifactDef;
|
||||||
|
|
|
||||||
|
|
@ -10,11 +10,16 @@ import {
|
||||||
updateGlobalProfileSuccessAction,
|
updateGlobalProfileSuccessAction,
|
||||||
} from "./actions/updateGlobalProfileActions";
|
} from "./actions/updateGlobalProfileActions";
|
||||||
import { gitGlobalInitialState } from "./helpers/initialState";
|
import { gitGlobalInitialState } from "./helpers/initialState";
|
||||||
import { toggleImportModalAction } from "./actions/uiActions";
|
import {
|
||||||
|
resetImportOverrideDetailsAction,
|
||||||
|
setImportOverrideDetailsAction,
|
||||||
|
toggleImportModalAction,
|
||||||
|
} from "./actions/uiActions";
|
||||||
import {
|
import {
|
||||||
gitImportErrorAction,
|
gitImportErrorAction,
|
||||||
gitImportInitAction,
|
gitImportInitAction,
|
||||||
gitImportSuccessAction,
|
gitImportSuccessAction,
|
||||||
|
resetGitImportAction,
|
||||||
} from "./actions/gitImportActions";
|
} from "./actions/gitImportActions";
|
||||||
import {
|
import {
|
||||||
fetchGlobalSSHKeyErrorAction,
|
fetchGlobalSSHKeyErrorAction,
|
||||||
|
|
@ -41,7 +46,10 @@ export const gitGlobalSlice = createSlice({
|
||||||
gitImportInit: gitImportInitAction,
|
gitImportInit: gitImportInitAction,
|
||||||
gitImportSuccess: gitImportSuccessAction,
|
gitImportSuccess: gitImportSuccessAction,
|
||||||
gitImportError: gitImportErrorAction,
|
gitImportError: gitImportErrorAction,
|
||||||
|
resetGitImport: resetGitImportAction,
|
||||||
toggleImportModal: toggleImportModalAction,
|
toggleImportModal: toggleImportModalAction,
|
||||||
|
resetImportOverrideDetails: resetImportOverrideDetailsAction,
|
||||||
|
setImportOverrideDetails: setImportOverrideDetailsAction,
|
||||||
toggleRepoLimitErrorModal: toggleRepoLimitErrorModalAction,
|
toggleRepoLimitErrorModal: toggleRepoLimitErrorModalAction,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -168,5 +168,6 @@ export const gitGlobalInitialState: GitGlobalReduxState = {
|
||||||
error: null,
|
error: null,
|
||||||
},
|
},
|
||||||
isImportModalOpen: false,
|
isImportModalOpen: false,
|
||||||
|
importOverrideDetails: null,
|
||||||
repoLimitErrorModalOpen: false,
|
repoLimitErrorModalOpen: false,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,12 @@ export const selectUpdateGlobalProfileState = (state: GitRootState) =>
|
||||||
export const selectImportModalOpen = (state: GitRootState) =>
|
export const selectImportModalOpen = (state: GitRootState) =>
|
||||||
selectGitGlobal(state).isImportModalOpen;
|
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) =>
|
export const selectGitImportState = (state: GitRootState) =>
|
||||||
selectGitGlobal(state).gitImport;
|
selectGitGlobal(state).gitImport;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ import type { FetchGlobalSSHKeyResponseData } from "git/requests/fetchGlobalSSHK
|
||||||
import type { FetchRefsResponseData } from "git/requests/fetchRefsRequest.types";
|
import type { FetchRefsResponseData } from "git/requests/fetchRefsRequest.types";
|
||||||
import type { GitArtifactDef } from "git/types";
|
import type { GitArtifactDef } from "git/types";
|
||||||
import type { PretagResponseData } from "git/requests/pretagRequest.types";
|
import type { PretagResponseData } from "git/requests/pretagRequest.types";
|
||||||
|
import type { GitImportRequestParams } from "git/requests/gitImportRequest.types";
|
||||||
|
|
||||||
export interface GitApiError extends ApiResponseError {
|
export interface GitApiError extends ApiResponseError {
|
||||||
errorType?: string;
|
errorType?: string;
|
||||||
|
|
@ -98,6 +99,11 @@ export interface GitGlobalReduxState {
|
||||||
globalSSHKey: GitAsyncState<FetchGlobalSSHKeyResponseData>;
|
globalSSHKey: GitAsyncState<FetchGlobalSSHKeyResponseData>;
|
||||||
// ui
|
// ui
|
||||||
isImportModalOpen: boolean;
|
isImportModalOpen: boolean;
|
||||||
|
importOverrideDetails: {
|
||||||
|
params: GitImportRequestParams;
|
||||||
|
oldArtifactName: string;
|
||||||
|
newArtifactName: string;
|
||||||
|
} | null;
|
||||||
repoLimitErrorModalOpen: boolean;
|
repoLimitErrorModalOpen: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -84,7 +84,7 @@ import static com.appsmith.git.constants.ce.CommonConstantsCE.DELIMITER_PATH;
|
||||||
@Import({GitServiceConfig.class})
|
@Import({GitServiceConfig.class})
|
||||||
public class FileUtilsCEImpl implements FileInterface {
|
public class FileUtilsCEImpl implements FileInterface {
|
||||||
|
|
||||||
private final GitServiceConfig gitServiceConfig;
|
protected final GitServiceConfig gitServiceConfig;
|
||||||
protected final FSGitHandler fsGitHandler;
|
protected final FSGitHandler fsGitHandler;
|
||||||
private final GitExecutor gitExecutor;
|
private final GitExecutor gitExecutor;
|
||||||
protected final FileOperations fileOperations;
|
protected final FileOperations fileOperations;
|
||||||
|
|
@ -98,7 +98,7 @@ public class FileUtilsCEImpl implements FileInterface {
|
||||||
private static final Pattern ALLOWED_FILE_EXTENSION_PATTERN =
|
private static final Pattern ALLOWED_FILE_EXTENSION_PATTERN =
|
||||||
Pattern.compile("(.*?)\\.(md|MD|git|gitignore|github|yml|yaml)$");
|
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]*.";
|
private static final String CANVAS_WIDGET = "(Canvas)[0-9]*.";
|
||||||
|
|
||||||
|
|
@ -1250,6 +1250,12 @@ public class FileUtilsCEImpl implements FileInterface {
|
||||||
return metadataMono.subscribeOn(scheduler);
|
return metadataMono.subscribeOn(scheduler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mono<Object> reconstructPackageJsonFromGitRepository(Path repoSuffix) {
|
||||||
|
return Mono.error(
|
||||||
|
new AppsmithPluginException(AppsmithPluginError.PLUGIN_UNSUPPORTED_OPERATION, "package json creation"));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Mono<Object> reconstructMetadataFromGitRepository(Path repoSuffix) {
|
public Mono<Object> reconstructMetadataFromGitRepository(Path repoSuffix) {
|
||||||
Mono<Object> metadataMono = Mono.fromCallable(() -> {
|
Mono<Object> metadataMono = Mono.fromCallable(() -> {
|
||||||
|
|
|
||||||
|
|
@ -77,6 +77,8 @@ public interface FileInterface {
|
||||||
|
|
||||||
Mono<Object> reconstructMetadataFromGitRepository(Path repoSuffix);
|
Mono<Object> reconstructMetadataFromGitRepository(Path repoSuffix);
|
||||||
|
|
||||||
|
Mono<Object> reconstructPackageJsonFromGitRepository(Path repoSuffix);
|
||||||
|
|
||||||
Mono<Object> reconstructPageFromGitRepo(
|
Mono<Object> reconstructPageFromGitRepo(
|
||||||
String pageName, String branchName, Path repoSuffixPath, Boolean checkoutRequired);
|
String pageName, String branchName, Path repoSuffixPath, Boolean checkoutRequired);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ public enum ErrorType {
|
||||||
BAD_REQUEST,
|
BAD_REQUEST,
|
||||||
INTERNAL_ERROR,
|
INTERNAL_ERROR,
|
||||||
ACTION_CONFIGURATION_ERROR,
|
ACTION_CONFIGURATION_ERROR,
|
||||||
|
ARTIFACT_IMPORT_ERROR,
|
||||||
GIT_CONFIGURATION_ERROR,
|
GIT_CONFIGURATION_ERROR,
|
||||||
GIT_ACTION_EXECUTION_ERROR,
|
GIT_ACTION_EXECUTION_ERROR,
|
||||||
GIT_UPSTREAM_CHANGES_PUSH_EXECUTION_ERROR,
|
GIT_UPSTREAM_CHANGES_PUSH_EXECUTION_ERROR,
|
||||||
|
|
|
||||||
|
|
@ -11,4 +11,13 @@ public class GitConnectDTO {
|
||||||
String remoteUrl;
|
String remoteUrl;
|
||||||
|
|
||||||
GitProfile gitProfile;
|
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",
|
"Duplicate Configuration",
|
||||||
ErrorType.BAD_REQUEST,
|
ErrorType.BAD_REQUEST,
|
||||||
null),
|
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(
|
INVALID_PROPERTIES_CONFIGURATION(
|
||||||
500,
|
500,
|
||||||
AppsmithErrorCode.INVALID_PROPERTIES_CONFIGURATION.getCode(),
|
AppsmithErrorCode.INVALID_PROPERTIES_CONFIGURATION.getCode(),
|
||||||
|
|
|
||||||
|
|
@ -103,6 +103,8 @@ public enum AppsmithErrorCode {
|
||||||
JSON_PROCESSING_ERROR("AE-JSN-4001", "Json processing error"),
|
JSON_PROCESSING_ERROR("AE-JSN-4001", "Json processing error"),
|
||||||
INCOMPATIBLE_IMPORTED_JSON("AE-JSN-4045", "Incompatible imported json"),
|
INCOMPATIBLE_IMPORTED_JSON("AE-JSN-4045", "Incompatible imported json"),
|
||||||
GENERIC_JSON_IMPORT_ERROR("AE-JSN-4049", "Generic json import error"),
|
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"),
|
INVALID_LOGIN_METHOD("AE-LGN-4000", "Invalid login method"),
|
||||||
PLUGIN_NOT_INSTALLED("AE-PLG-4001", "Plugin not installed"),
|
PLUGIN_NOT_INSTALLED("AE-PLG-4001", "Plugin not installed"),
|
||||||
PLUGIN_ID_NOT_GIVEN("AE-PLG-4002", "Plugin id not given"),
|
PLUGIN_ID_NOT_GIVEN("AE-PLG-4002", "Plugin id not given"),
|
||||||
|
|
|
||||||
|
|
@ -21,9 +21,6 @@ import java.util.List;
|
||||||
|
|
||||||
public interface CentralGitServiceCE {
|
public interface CentralGitServiceCE {
|
||||||
|
|
||||||
Mono<? extends ArtifactImportDTO> importArtifactFromGit(
|
|
||||||
String workspaceId, GitConnectDTO gitConnectDTO, ArtifactType artifactType, GitType gitType);
|
|
||||||
|
|
||||||
Mono<? extends ArtifactImportDTO> importArtifactFromGit(
|
Mono<? extends ArtifactImportDTO> importArtifactFromGit(
|
||||||
String workspaceId, GitConnectDTO gitConnectDTO, GitType gitType);
|
String workspaceId, GitConnectDTO gitConnectDTO, GitType gitType);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -161,11 +161,11 @@ public class CentralGitServiceCEImpl implements CentralGitServiceCE {
|
||||||
final String repoName = gitHandlingService.getRepoName(gitConnectDTO);
|
final String repoName = gitHandlingService.getRepoName(gitConnectDTO);
|
||||||
|
|
||||||
// since at this point in the import flow, there is no context about the artifact type
|
// 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
|
// it needs to be retrieved from the fetched repository itself. however, to retrieve
|
||||||
// the artifact type from repository, the repository needs to be saved.
|
// the artifact type from the repository, the repository needs to be saved.
|
||||||
// for saving the repo an identifier is required (which usually is the artifact id);
|
// 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.
|
// 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();
|
String placeholder = "temp" + UUID.randomUUID();
|
||||||
ArtifactJsonTransformationDTO tempJsonTransformationDTO =
|
ArtifactJsonTransformationDTO tempJsonTransformationDTO =
|
||||||
new ArtifactJsonTransformationDTO(workspaceId, placeholder, repoName);
|
new ArtifactJsonTransformationDTO(workspaceId, placeholder, repoName);
|
||||||
|
|
@ -203,7 +203,7 @@ public class CentralGitServiceCEImpl implements CentralGitServiceCE {
|
||||||
.flatMap(defaultBranch -> {
|
.flatMap(defaultBranch -> {
|
||||||
return Mono.zip(
|
return Mono.zip(
|
||||||
Mono.just(defaultBranch),
|
Mono.just(defaultBranch),
|
||||||
gitHandlingService.obtainArtifactTypeFromGitRepository(
|
gitHandlingService.obtainArtifactTypeAndIdentifierFromGitRepository(
|
||||||
tempJsonTransformationDTO),
|
tempJsonTransformationDTO),
|
||||||
isRepositoryPrivateMonoCached,
|
isRepositoryPrivateMonoCached,
|
||||||
Mono.just(gitAuth));
|
Mono.just(gitAuth));
|
||||||
|
|
@ -211,7 +211,8 @@ public class CentralGitServiceCEImpl implements CentralGitServiceCE {
|
||||||
})
|
})
|
||||||
.flatMap(tuple4 -> {
|
.flatMap(tuple4 -> {
|
||||||
String defaultBranch = tuple4.getT1();
|
String defaultBranch = tuple4.getT1();
|
||||||
ArtifactType artifactType = tuple4.getT2();
|
ArtifactType artifactType = tuple4.getT2().getT1();
|
||||||
|
String uniqueIdentifier = tuple4.getT2().getT2();
|
||||||
Boolean isRepoPrivate = tuple4.getT3();
|
Boolean isRepoPrivate = tuple4.getT3();
|
||||||
GitAuth gitAuth = tuple4.getT4();
|
GitAuth gitAuth = tuple4.getT4();
|
||||||
|
|
||||||
|
|
@ -224,7 +225,8 @@ public class CentralGitServiceCEImpl implements CentralGitServiceCE {
|
||||||
AppsmithError.NO_RESOURCE_FOUND, FieldName.WORKSPACE, workspaceId)));
|
AppsmithError.NO_RESOURCE_FOUND, FieldName.WORKSPACE, workspaceId)));
|
||||||
|
|
||||||
return workspaceMono
|
return workspaceMono
|
||||||
.flatMap(workspace -> contextHelper.createArtifactForImport(workspaceId, repoName))
|
.flatMap(workspace -> createArtifactForGitConnect(
|
||||||
|
gitConnectDTO, artifactType, workspaceId, repoName, uniqueIdentifier))
|
||||||
.map(baseArtifact -> {
|
.map(baseArtifact -> {
|
||||||
GitArtifactMetadata gitArtifactMetadata = new GitArtifactMetadata();
|
GitArtifactMetadata gitArtifactMetadata = new GitArtifactMetadata();
|
||||||
gitArtifactMetadata.setGitAuth(gitAuth);
|
gitArtifactMetadata.setGitAuth(gitAuth);
|
||||||
|
|
@ -240,6 +242,12 @@ public class CentralGitServiceCEImpl implements CentralGitServiceCE {
|
||||||
baseArtifact.setGitArtifactMetadata(gitArtifactMetadata);
|
baseArtifact.setGitArtifactMetadata(gitArtifactMetadata);
|
||||||
return baseArtifact;
|
return baseArtifact;
|
||||||
});
|
});
|
||||||
|
})
|
||||||
|
.onErrorResume(error -> {
|
||||||
|
Mono<Boolean> removeRepositoryMono =
|
||||||
|
gitHandlingService.removeRepository(tempJsonTransformationDTO, TRUE);
|
||||||
|
|
||||||
|
return removeRepositoryMono.then(Mono.error(error));
|
||||||
});
|
});
|
||||||
|
|
||||||
Mono<? extends Artifact> containerArtifactForImport = Mono.usingWhen(
|
Mono<? extends Artifact> containerArtifactForImport = Mono.usingWhen(
|
||||||
|
|
@ -267,7 +275,7 @@ public class CentralGitServiceCEImpl implements CentralGitServiceCE {
|
||||||
GitArtifactHelper<?> gitArtifactHelper =
|
GitArtifactHelper<?> gitArtifactHelper =
|
||||||
gitArtifactHelperResolver.getArtifactHelper(baseArtifact.getArtifactType());
|
gitArtifactHelperResolver.getArtifactHelper(baseArtifact.getArtifactType());
|
||||||
|
|
||||||
Mono<List<Datasource>> datasourceMono = datasourceService
|
Mono<List<Datasource>> existingWorkspaceDatasourceMono = datasourceService
|
||||||
.getAllByWorkspaceIdWithStorages(workspaceId, datasourcePermission.getEditPermission())
|
.getAllByWorkspaceIdWithStorages(workspaceId, datasourcePermission.getEditPermission())
|
||||||
.collectList();
|
.collectList();
|
||||||
|
|
||||||
|
|
@ -282,7 +290,7 @@ public class CentralGitServiceCEImpl implements CentralGitServiceCE {
|
||||||
new AppsmithException(AppsmithError.GIT_FILE_SYSTEM_ERROR, error.getMessage()));
|
new AppsmithException(AppsmithError.GIT_FILE_SYSTEM_ERROR, error.getMessage()));
|
||||||
});
|
});
|
||||||
|
|
||||||
return Mono.zip(artifactExchangeJsonMono, datasourceMono, pluginMono)
|
return Mono.zip(artifactExchangeJsonMono, existingWorkspaceDatasourceMono, pluginMono)
|
||||||
.flatMap(data -> {
|
.flatMap(data -> {
|
||||||
ArtifactExchangeJson artifactExchangeJson = data.getT1();
|
ArtifactExchangeJson artifactExchangeJson = data.getT1();
|
||||||
List<Datasource> datasourceList = data.getT2();
|
List<Datasource> datasourceList = data.getT2();
|
||||||
|
|
@ -312,7 +320,14 @@ public class CentralGitServiceCEImpl implements CentralGitServiceCE {
|
||||||
.importArtifactInWorkspaceFromGit(
|
.importArtifactInWorkspaceFromGit(
|
||||||
workspaceId, baseArtifact.getId(), artifactExchangeJson, defaultBranch)
|
workspaceId, baseArtifact.getId(), artifactExchangeJson, defaultBranch)
|
||||||
.onErrorResume(throwable -> {
|
.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(
|
return Mono.error(new AppsmithException(
|
||||||
AppsmithError.GIT_FILE_SYSTEM_ERROR, throwable.getMessage()));
|
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()));
|
sink -> importGitArtifactMono.subscribe(sink::success, sink::error, null, sink.currentContext()));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Mono<Artifact> hydrateLatest(Artifact artifact) {
|
protected Mono<? extends Artifact> createArtifactForGitConnect(
|
||||||
return Mono.just(artifact);
|
GitConnectDTO gitConnectDTO,
|
||||||
|
ArtifactType artifactType,
|
||||||
|
String workspaceId,
|
||||||
|
String repoName,
|
||||||
|
String uniqueIdentifier) {
|
||||||
|
GitArtifactHelper<?> contextHelper = gitArtifactHelperResolver.getArtifactHelper(artifactType);
|
||||||
|
return contextHelper.createArtifactForImport(workspaceId, repoName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
protected Mono<Artifact> hydrateLatest(Artifact artifact) {
|
||||||
public Mono<? extends ArtifactImportDTO> importArtifactFromGit(
|
return Mono.just(artifact);
|
||||||
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()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Mono<? extends Artifact> deleteArtifactCreatedFromGitImport(
|
private Mono<? extends Artifact> deleteArtifactCreatedFromGitImport(
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,9 @@ public interface GitHandlingServiceCE {
|
||||||
|
|
||||||
Mono<ArtifactType> obtainArtifactTypeFromGitRepository(ArtifactJsonTransformationDTO jsonTransformationDTO);
|
Mono<ArtifactType> obtainArtifactTypeFromGitRepository(ArtifactJsonTransformationDTO jsonTransformationDTO);
|
||||||
|
|
||||||
|
Mono<Tuple2<ArtifactType, String>> obtainArtifactTypeAndIdentifierFromGitRepository(
|
||||||
|
ArtifactJsonTransformationDTO jsonTransformationDTO);
|
||||||
|
|
||||||
Mono<String> fetchRemoteRepository(
|
Mono<String> fetchRemoteRepository(
|
||||||
GitConnectDTO gitConnectDTO, GitAuth gitAuth, ArtifactJsonTransformationDTO jsonTransformationDTO);
|
GitConnectDTO gitConnectDTO, GitAuth gitAuth, ArtifactJsonTransformationDTO jsonTransformationDTO);
|
||||||
|
|
||||||
|
|
@ -53,6 +56,9 @@ public interface GitHandlingServiceCE {
|
||||||
void setRepositoryDetailsInGitArtifactMetadata(
|
void setRepositoryDetailsInGitArtifactMetadata(
|
||||||
GitConnectDTO gitConnectDTO, GitArtifactMetadata gitArtifactMetadata);
|
GitConnectDTO gitConnectDTO, GitArtifactMetadata gitArtifactMetadata);
|
||||||
|
|
||||||
|
Mono<Boolean> removeRepository(
|
||||||
|
ArtifactJsonTransformationDTO artifactJsonTransformationDTO, Boolean isArtifactTypeUnknown);
|
||||||
|
|
||||||
Mono<Boolean> removeRepository(ArtifactJsonTransformationDTO artifactJsonTransformationDTO);
|
Mono<Boolean> removeRepository(ArtifactJsonTransformationDTO artifactJsonTransformationDTO);
|
||||||
|
|
||||||
Mono<List<GitRefDTO>> listBranches(
|
Mono<List<GitRefDTO>> listBranches(
|
||||||
|
|
|
||||||
|
|
@ -1,31 +1,18 @@
|
||||||
package com.appsmith.server.git.fs;
|
package com.appsmith.server.git.fs;
|
||||||
|
|
||||||
import com.appsmith.external.git.handler.FSGitHandler;
|
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.GitRedisUtils;
|
||||||
import com.appsmith.server.git.autocommit.helpers.GitAutoCommitHelper;
|
|
||||||
import com.appsmith.server.git.central.GitHandlingServiceCECompatible;
|
import com.appsmith.server.git.central.GitHandlingServiceCECompatible;
|
||||||
import com.appsmith.server.git.resolver.GitArtifactHelperResolver;
|
import com.appsmith.server.git.resolver.GitArtifactHelperResolver;
|
||||||
import com.appsmith.server.git.utils.GitAnalyticsUtils;
|
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.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.repositories.GitDeployKeysRepository;
|
||||||
import com.appsmith.server.services.AnalyticsService;
|
import com.appsmith.server.services.AnalyticsService;
|
||||||
import com.appsmith.server.services.FeatureFlagService;
|
import com.appsmith.server.services.FeatureFlagService;
|
||||||
import com.appsmith.server.services.SessionUserService;
|
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 io.micrometer.observation.ObservationRegistry;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.reactive.TransactionalOperator;
|
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
|
|
@ -33,49 +20,23 @@ public class GitFSServiceCECompatibleImpl extends GitFSServiceCEImpl implements
|
||||||
|
|
||||||
public GitFSServiceCECompatibleImpl(
|
public GitFSServiceCECompatibleImpl(
|
||||||
GitDeployKeysRepository gitDeployKeysRepository,
|
GitDeployKeysRepository gitDeployKeysRepository,
|
||||||
GitPrivateRepoHelper gitPrivateRepoHelper,
|
|
||||||
CommonGitFileUtils commonGitFileUtils,
|
CommonGitFileUtils commonGitFileUtils,
|
||||||
GitRedisUtils gitRedisUtils,
|
GitRedisUtils gitRedisUtils,
|
||||||
SessionUserService sessionUserService,
|
SessionUserService sessionUserService,
|
||||||
UserDataService userDataService,
|
|
||||||
UserService userService,
|
|
||||||
EmailConfig emailConfig,
|
|
||||||
TransactionalOperator transactionalOperator,
|
|
||||||
AnalyticsService analyticsService,
|
AnalyticsService analyticsService,
|
||||||
ObservationRegistry observationRegistry,
|
ObservationRegistry observationRegistry,
|
||||||
WorkspaceService workspaceService,
|
|
||||||
DatasourceService datasourceService,
|
|
||||||
DatasourcePermission datasourcePermission,
|
|
||||||
PluginService pluginService,
|
|
||||||
ExportService exportService,
|
|
||||||
ImportService importService,
|
|
||||||
FSGitHandler fsGitHandler,
|
FSGitHandler fsGitHandler,
|
||||||
GitAutoCommitHelper gitAutoCommitHelper,
|
|
||||||
GitProfileUtils gitProfileUtils,
|
|
||||||
GitAnalyticsUtils gitAnalyticsUtils,
|
GitAnalyticsUtils gitAnalyticsUtils,
|
||||||
GitArtifactHelperResolver gitArtifactHelperResolver,
|
GitArtifactHelperResolver gitArtifactHelperResolver,
|
||||||
FeatureFlagService featureFlagService) {
|
FeatureFlagService featureFlagService) {
|
||||||
super(
|
super(
|
||||||
gitDeployKeysRepository,
|
gitDeployKeysRepository,
|
||||||
gitPrivateRepoHelper,
|
|
||||||
commonGitFileUtils,
|
commonGitFileUtils,
|
||||||
gitRedisUtils,
|
gitRedisUtils,
|
||||||
sessionUserService,
|
sessionUserService,
|
||||||
userDataService,
|
|
||||||
userService,
|
|
||||||
emailConfig,
|
|
||||||
transactionalOperator,
|
|
||||||
analyticsService,
|
analyticsService,
|
||||||
observationRegistry,
|
observationRegistry,
|
||||||
workspaceService,
|
|
||||||
datasourceService,
|
|
||||||
datasourcePermission,
|
|
||||||
pluginService,
|
|
||||||
exportService,
|
|
||||||
importService,
|
|
||||||
fsGitHandler,
|
fsGitHandler,
|
||||||
gitAutoCommitHelper,
|
|
||||||
gitProfileUtils,
|
|
||||||
gitAnalyticsUtils,
|
gitAnalyticsUtils,
|
||||||
gitArtifactHelperResolver,
|
gitArtifactHelperResolver,
|
||||||
featureFlagService);
|
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.dtos.FetchRemoteDTO;
|
||||||
import com.appsmith.external.git.handler.FSGitHandler;
|
import com.appsmith.external.git.handler.FSGitHandler;
|
||||||
import com.appsmith.git.dto.CommitDTO;
|
import com.appsmith.git.dto.CommitDTO;
|
||||||
import com.appsmith.server.configurations.EmailConfig;
|
|
||||||
import com.appsmith.server.constants.ArtifactType;
|
import com.appsmith.server.constants.ArtifactType;
|
||||||
import com.appsmith.server.datasources.base.DatasourceService;
|
|
||||||
import com.appsmith.server.domains.Artifact;
|
import com.appsmith.server.domains.Artifact;
|
||||||
import com.appsmith.server.domains.GitArtifactMetadata;
|
import com.appsmith.server.domains.GitArtifactMetadata;
|
||||||
import com.appsmith.server.domains.GitAuth;
|
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.dtos.GitMergeDTO;
|
||||||
import com.appsmith.server.exceptions.AppsmithError;
|
import com.appsmith.server.exceptions.AppsmithError;
|
||||||
import com.appsmith.server.exceptions.AppsmithException;
|
import com.appsmith.server.exceptions.AppsmithException;
|
||||||
import com.appsmith.server.exports.internal.ExportService;
|
|
||||||
import com.appsmith.server.git.GitRedisUtils;
|
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.central.GitHandlingServiceCE;
|
||||||
import com.appsmith.server.git.dtos.ArtifactJsonTransformationDTO;
|
import com.appsmith.server.git.dtos.ArtifactJsonTransformationDTO;
|
||||||
import com.appsmith.server.git.resolver.GitArtifactHelperResolver;
|
import com.appsmith.server.git.resolver.GitArtifactHelperResolver;
|
||||||
import com.appsmith.server.git.utils.GitAnalyticsUtils;
|
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.CollectionUtils;
|
||||||
import com.appsmith.server.helpers.CommonGitFileUtils;
|
import com.appsmith.server.helpers.CommonGitFileUtils;
|
||||||
import com.appsmith.server.helpers.GitPrivateRepoHelper;
|
|
||||||
import com.appsmith.server.helpers.GitUtils;
|
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.repositories.GitDeployKeysRepository;
|
||||||
import com.appsmith.server.services.AnalyticsService;
|
import com.appsmith.server.services.AnalyticsService;
|
||||||
import com.appsmith.server.services.FeatureFlagService;
|
import com.appsmith.server.services.FeatureFlagService;
|
||||||
import com.appsmith.server.services.GitArtifactHelper;
|
import com.appsmith.server.services.GitArtifactHelper;
|
||||||
import com.appsmith.server.services.SessionUserService;
|
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 io.micrometer.observation.ObservationRegistry;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
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.api.errors.TransportException;
|
||||||
import org.eclipse.jgit.errors.RepositoryNotFoundException;
|
import org.eclipse.jgit.errors.RepositoryNotFoundException;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.reactive.TransactionalOperator;
|
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
import reactor.core.observability.micrometer.Micrometer;
|
import reactor.core.observability.micrometer.Micrometer;
|
||||||
import reactor.core.publisher.Flux;
|
import reactor.core.publisher.Flux;
|
||||||
|
|
@ -79,30 +66,14 @@ import static java.lang.Boolean.TRUE;
|
||||||
public class GitFSServiceCEImpl implements GitHandlingServiceCE {
|
public class GitFSServiceCEImpl implements GitHandlingServiceCE {
|
||||||
|
|
||||||
private final GitDeployKeysRepository gitDeployKeysRepository;
|
private final GitDeployKeysRepository gitDeployKeysRepository;
|
||||||
private final GitPrivateRepoHelper gitPrivateRepoHelper;
|
protected final CommonGitFileUtils commonGitFileUtils;
|
||||||
private final CommonGitFileUtils commonGitFileUtils;
|
protected final GitRedisUtils gitRedisUtils;
|
||||||
private final GitRedisUtils gitRedisUtils;
|
|
||||||
protected final SessionUserService sessionUserService;
|
protected final SessionUserService sessionUserService;
|
||||||
private final UserDataService userDataService;
|
|
||||||
protected final UserService userService;
|
|
||||||
private final EmailConfig emailConfig;
|
|
||||||
private final TransactionalOperator transactionalOperator;
|
|
||||||
|
|
||||||
protected final AnalyticsService analyticsService;
|
protected final AnalyticsService analyticsService;
|
||||||
private final ObservationRegistry observationRegistry;
|
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;
|
protected final FSGitHandler fsGitHandler;
|
||||||
private final GitAutoCommitHelper gitAutoCommitHelper;
|
|
||||||
|
|
||||||
private final GitProfileUtils gitProfileUtils;
|
|
||||||
private final GitAnalyticsUtils gitAnalyticsUtils;
|
private final GitAnalyticsUtils gitAnalyticsUtils;
|
||||||
|
|
||||||
protected final GitArtifactHelperResolver gitArtifactHelperResolver;
|
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) {
|
public Mono<ArtifactType> obtainArtifactTypeFromGitRepository(ArtifactJsonTransformationDTO jsonTransformationDTO) {
|
||||||
String workspaceId = jsonTransformationDTO.getWorkspaceId();
|
String workspaceId = jsonTransformationDTO.getWorkspaceId();
|
||||||
String placeHolder = jsonTransformationDTO.getBaseArtifactId();
|
String placeHolder = jsonTransformationDTO.getBaseArtifactId();
|
||||||
|
|
@ -292,6 +269,17 @@ public class GitFSServiceCEImpl implements GitHandlingServiceCE {
|
||||||
artifactJsonTransformationDTO));
|
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
|
@Override
|
||||||
public Mono<Boolean> removeRepository(ArtifactJsonTransformationDTO artifactJsonTransformationDTO) {
|
public Mono<Boolean> removeRepository(ArtifactJsonTransformationDTO artifactJsonTransformationDTO) {
|
||||||
GitArtifactHelper<?> gitArtifactHelper =
|
GitArtifactHelper<?> gitArtifactHelper =
|
||||||
|
|
|
||||||
|
|
@ -1,31 +1,18 @@
|
||||||
package com.appsmith.server.git.fs;
|
package com.appsmith.server.git.fs;
|
||||||
|
|
||||||
import com.appsmith.external.git.handler.FSGitHandler;
|
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.GitRedisUtils;
|
||||||
import com.appsmith.server.git.autocommit.helpers.GitAutoCommitHelper;
|
|
||||||
import com.appsmith.server.git.central.GitHandlingService;
|
import com.appsmith.server.git.central.GitHandlingService;
|
||||||
import com.appsmith.server.git.resolver.GitArtifactHelperResolver;
|
import com.appsmith.server.git.resolver.GitArtifactHelperResolver;
|
||||||
import com.appsmith.server.git.utils.GitAnalyticsUtils;
|
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.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.repositories.GitDeployKeysRepository;
|
||||||
import com.appsmith.server.services.AnalyticsService;
|
import com.appsmith.server.services.AnalyticsService;
|
||||||
import com.appsmith.server.services.FeatureFlagService;
|
import com.appsmith.server.services.FeatureFlagService;
|
||||||
import com.appsmith.server.services.SessionUserService;
|
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 io.micrometer.observation.ObservationRegistry;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.reactive.TransactionalOperator;
|
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
|
|
@ -33,49 +20,23 @@ public class GitFSServiceImpl extends GitFSServiceCECompatibleImpl implements Gi
|
||||||
|
|
||||||
public GitFSServiceImpl(
|
public GitFSServiceImpl(
|
||||||
GitDeployKeysRepository gitDeployKeysRepository,
|
GitDeployKeysRepository gitDeployKeysRepository,
|
||||||
GitPrivateRepoHelper gitPrivateRepoHelper,
|
|
||||||
CommonGitFileUtils commonGitFileUtils,
|
CommonGitFileUtils commonGitFileUtils,
|
||||||
GitRedisUtils gitRedisUtils,
|
GitRedisUtils gitRedisUtils,
|
||||||
SessionUserService sessionUserService,
|
SessionUserService sessionUserService,
|
||||||
UserDataService userDataService,
|
|
||||||
UserService userService,
|
|
||||||
EmailConfig emailConfig,
|
|
||||||
TransactionalOperator transactionalOperator,
|
|
||||||
AnalyticsService analyticsService,
|
AnalyticsService analyticsService,
|
||||||
ObservationRegistry observationRegistry,
|
ObservationRegistry observationRegistry,
|
||||||
WorkspaceService workspaceService,
|
|
||||||
DatasourceService datasourceService,
|
|
||||||
DatasourcePermission datasourcePermission,
|
|
||||||
PluginService pluginService,
|
|
||||||
ExportService exportService,
|
|
||||||
ImportService importService,
|
|
||||||
FSGitHandler fsGitHandler,
|
FSGitHandler fsGitHandler,
|
||||||
GitAutoCommitHelper gitAutoCommitHelper,
|
|
||||||
GitProfileUtils gitProfileUtils,
|
|
||||||
GitAnalyticsUtils gitAnalyticsUtils,
|
GitAnalyticsUtils gitAnalyticsUtils,
|
||||||
GitArtifactHelperResolver gitArtifactHelperResolver,
|
GitArtifactHelperResolver gitArtifactHelperResolver,
|
||||||
FeatureFlagService featureFlagService) {
|
FeatureFlagService featureFlagService) {
|
||||||
super(
|
super(
|
||||||
gitDeployKeysRepository,
|
gitDeployKeysRepository,
|
||||||
gitPrivateRepoHelper,
|
|
||||||
commonGitFileUtils,
|
commonGitFileUtils,
|
||||||
gitRedisUtils,
|
gitRedisUtils,
|
||||||
sessionUserService,
|
sessionUserService,
|
||||||
userDataService,
|
|
||||||
userService,
|
|
||||||
emailConfig,
|
|
||||||
transactionalOperator,
|
|
||||||
analyticsService,
|
analyticsService,
|
||||||
observationRegistry,
|
observationRegistry,
|
||||||
workspaceService,
|
|
||||||
datasourceService,
|
|
||||||
datasourcePermission,
|
|
||||||
pluginService,
|
|
||||||
exportService,
|
|
||||||
importService,
|
|
||||||
fsGitHandler,
|
fsGitHandler,
|
||||||
gitAutoCommitHelper,
|
|
||||||
gitProfileUtils,
|
|
||||||
gitAnalyticsUtils,
|
gitAnalyticsUtils,
|
||||||
gitArtifactHelperResolver,
|
gitArtifactHelperResolver,
|
||||||
featureFlagService);
|
featureFlagService);
|
||||||
|
|
|
||||||
|
|
@ -95,7 +95,7 @@ public class CommonGitFileUtilsCE {
|
||||||
|
|
||||||
protected final ArtifactGitFileUtils<ApplicationJson> applicationGitFileUtils;
|
protected final ArtifactGitFileUtils<ApplicationJson> applicationGitFileUtils;
|
||||||
protected final GitServiceConfig gitServiceConfig;
|
protected final GitServiceConfig gitServiceConfig;
|
||||||
private final FileInterface fileUtils;
|
protected final FileInterface fileUtils;
|
||||||
private final FileOperations fileOperations;
|
private final FileOperations fileOperations;
|
||||||
private final AnalyticsService analyticsService;
|
private final AnalyticsService analyticsService;
|
||||||
private final SessionUserService sessionUserService;
|
private final SessionUserService sessionUserService;
|
||||||
|
|
@ -830,7 +830,7 @@ public class CommonGitFileUtilsCE {
|
||||||
"Error while moving repository from temporary storage {} to permanent storage {}",
|
"Error while moving repository from temporary storage {} to permanent storage {}",
|
||||||
currentGitPath,
|
currentGitPath,
|
||||||
targetPath,
|
targetPath,
|
||||||
error.getMessage());
|
error);
|
||||||
return Mono.error(error);
|
return Mono.error(error);
|
||||||
})
|
})
|
||||||
.subscribeOn(Schedulers.boundedElastic());
|
.subscribeOn(Schedulers.boundedElastic());
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@ import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.springframework.core.io.buffer.DataBufferUtils;
|
import org.springframework.core.io.buffer.DataBufferUtils;
|
||||||
|
import org.springframework.dao.DuplicateKeyException;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.http.codec.multipart.Part;
|
import org.springframework.http.codec.multipart.Part;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
@ -530,6 +531,11 @@ public class ImportServiceCEImpl implements ImportServiceCE {
|
||||||
.onErrorResume(throwable -> {
|
.onErrorResume(throwable -> {
|
||||||
String errorMessage = ImportExportUtils.getErrorMessage(throwable);
|
String errorMessage = ImportExportUtils.getErrorMessage(throwable);
|
||||||
log.error("Error importing {}. Error: {}", artifactContextString, errorMessage, 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(
|
return Mono.error(new AppsmithException(
|
||||||
AppsmithError.GENERIC_JSON_IMPORT_ERROR, workspaceId, errorMessage));
|
AppsmithError.GENERIC_JSON_IMPORT_ERROR, workspaceId, errorMessage));
|
||||||
})
|
})
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user