Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 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";
Comment thread
brayn003 marked this conversation as resolved.
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,
});
Comment thread
brayn003 marked this conversation as resolved.

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-5003",
}
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
Copy Markdown
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