Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions app/client/src/ce/pages/Applications/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand All @@ -145,6 +146,7 @@ function GitModals() {
<>
<NewGitImportModal />
<NewGitRepoLimitErrorModal />
<GitImportOverrideModal />
</>
) : (
<>
Expand Down
8 changes: 8 additions & 0 deletions app/client/src/git/ce/constants/messages.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
10 changes: 8 additions & 2 deletions app/client/src/git/components/ImportModal/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ function ImportModal() {
gitImportError,
isGitImportLoading,
isImportModalOpen,
resetGitImport,
toggleImportModal,
} = useImport();
const {
Expand All @@ -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"
Expand All @@ -41,7 +47,7 @@ function ImportModal() {
onGenerateSSHKey={fetchGlobalSSHKey}
onOpenImport={null}
onSubmit={onSubmit}
resetConnectState={resetGlobalSSHKey}
resetConnectState={resetConnectState}
sshPublicKey={sshPublicKey}
toggleModalOpen={toggleImportModal}
/>
Expand Down
Original file line number Diff line number Diff line change
@@ -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 changes: 46 additions & 0 deletions app/client/src/git/components/ImportOverrideModal/index.tsx
Original file line number Diff line number Diff line change
@@ -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;
1 change: 1 addition & 0 deletions app/client/src/git/constants/enums.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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",
}
27 changes: 27 additions & 0 deletions app/client/src/git/hooks/useImport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -19,6 +22,10 @@ export default function useImport() {
[dispatch],
);

const resetGitImport = useCallback(() => {
dispatch(gitGlobalActions.resetGitImport());
}, [dispatch]);

const isImportModalOpen = useSelector(selectImportModalOpen);

const toggleImportModal = useCallback(
Expand All @@ -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 changes: 12 additions & 0 deletions app/client/src/git/hooks/useMessage.ts
Original file line number Diff line number Diff line change
@@ -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]);
}
1 change: 1 addition & 0 deletions app/client/src/git/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
5 changes: 4 additions & 1 deletion app/client/src/git/requests/gitImportRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
1 change: 1 addition & 0 deletions app/client/src/git/requests/gitImportRequest.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export interface GitImportRequestParams {
authorEmail: string;
useDefaultProfile?: boolean;
};
override?: boolean;
}

export interface GitImportResponseData {
Expand Down
39 changes: 38 additions & 1 deletion app/client/src/git/sagas/gitImportSaga.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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>,
Expand Down Expand Up @@ -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);
Expand All @@ -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 {}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally we should log the error here

}
}

yield put(
gitGlobalActions.setImportOverrideDetails({
params,
oldArtifactName: artifactNames.oldArtifactName ?? "",
newArtifactName: artifactNames.newArtifactName ?? "",
}),
);
}
7 changes: 7 additions & 0 deletions app/client/src/git/store/actions/gitImportActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,10 @@ export const gitImportErrorAction = (

return state;
};

export const resetGitImportAction = (state: GitGlobalReduxState) => {
state.gitImport.loading = false;
state.gitImport.error = null;

return state;
};
Loading
Loading