mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-12-30 06:11:38 +00:00
Container Copy Job implementation for SQL accounts (#2241)
* Initial dev for container copy * remove padding from label * Added Copy Job prerequisites screen * Added hooks to evaluate reader role access * added copyjob pre-requsite screen along with it's validations * Added monitor copy job list screen * added copy job list refresh and reset functionality * remove arm token dependency * fetch account details from account id instead of context * Fix lint & typescript checks * show copyjob screen from portal navigation * adding copy job details screen * remove duplicate code & show sql accounts only * ui fixes for list job page * pending icon * copy job details screen ui * reset .vscode/settings.json * Fixed existing UTs * disabling action buttons until it's in progress * fixed formatting * Adding loader on submit button and show job creation errors in the panel itself * updating disabling action menu item logic * added custom pager * fix lint and ts errors * updating file names and removing comments * remove comments * modularize the arom common code * Adding content and removing tooltip * updating job details screen * updating online copy enabled screen * Adding below changes - Don't show permission screen for same account in offline mode - Don't show identity permissions for same account in online mode - Show error message if selected containers are identical - Update abort signal messages * added feedback code from explorer * Add tooltips and long polling - Added tooltips to permission sections - Implemented long polling for PITR and online copy enabled sections - Long polling automatically stops after 15 minutes - After polling ends, a refresh button will be displayed --------- Co-authored-by: nishthaAhujaa <nishtha17354@iiittd.ac.in>
This commit is contained in:
@@ -0,0 +1,152 @@
|
||||
import { useCallback, useMemo, useReducer, useState } from "react";
|
||||
import { useSidePanel } from "../../../../hooks/useSidePanel";
|
||||
import { submitCreateCopyJob } from "../../Actions/CopyJobActions";
|
||||
import { useCopyJobContext } from "../../Context/CopyJobContext";
|
||||
import { CopyJobMigrationType } from "../../Enums/CopyJobEnums";
|
||||
import { useCopyJobPrerequisitesCache } from "./useCopyJobPrerequisitesCache";
|
||||
import { SCREEN_KEYS, useCreateCopyJobScreensList } from "./useCreateCopyJobScreensList";
|
||||
|
||||
type NavigationState = {
|
||||
screenHistory: string[];
|
||||
};
|
||||
|
||||
type Action = { type: "NEXT"; nextScreen: string } | { type: "PREVIOUS" } | { type: "RESET" };
|
||||
|
||||
function navigationReducer(state: NavigationState, action: Action): NavigationState {
|
||||
switch (action.type) {
|
||||
case "NEXT":
|
||||
return {
|
||||
screenHistory: [...state.screenHistory, action.nextScreen],
|
||||
};
|
||||
case "PREVIOUS":
|
||||
return {
|
||||
screenHistory: state.screenHistory.length > 1 ? state.screenHistory.slice(0, -1) : state.screenHistory,
|
||||
};
|
||||
case "RESET":
|
||||
return {
|
||||
screenHistory: [SCREEN_KEYS.SelectAccount],
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
export function useCopyJobNavigation() {
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const { copyJobState, resetCopyJobState } = useCopyJobContext();
|
||||
const screens = useCreateCopyJobScreensList();
|
||||
const { validationCache: cache } = useCopyJobPrerequisitesCache();
|
||||
const [state, dispatch] = useReducer(navigationReducer, { screenHistory: [SCREEN_KEYS.SelectAccount] });
|
||||
|
||||
const currentScreenKey = state.screenHistory[state.screenHistory.length - 1];
|
||||
const currentScreen = screens.find((screen) => screen.key === currentScreenKey);
|
||||
|
||||
const isPrimaryDisabled = useMemo(() => {
|
||||
if (isLoading) {
|
||||
return true;
|
||||
}
|
||||
const context = currentScreenKey === SCREEN_KEYS.AssignPermissions ? cache : copyJobState;
|
||||
return !currentScreen?.validations.every((v) => v.validate(context));
|
||||
}, [currentScreen.key, copyJobState, cache, isLoading]);
|
||||
|
||||
const primaryBtnText = useMemo(() => {
|
||||
if (currentScreenKey === SCREEN_KEYS.PreviewCopyJob) {
|
||||
return "Copy";
|
||||
}
|
||||
return "Next";
|
||||
}, [currentScreenKey]);
|
||||
|
||||
const isPreviousDisabled = state.screenHistory.length <= 1;
|
||||
|
||||
const handleCancel = useCallback(() => {
|
||||
dispatch({ type: "RESET" });
|
||||
resetCopyJobState();
|
||||
useSidePanel.getState().closeSidePanel();
|
||||
}, []);
|
||||
|
||||
const getContainerIdentifiers = (container: typeof copyJobState.source | typeof copyJobState.target) => ({
|
||||
accountId: container?.account?.id || "",
|
||||
databaseId: container?.databaseId || "",
|
||||
containerId: container?.containerId || "",
|
||||
});
|
||||
|
||||
const isSameAccount = (
|
||||
sourceIds: ReturnType<typeof getContainerIdentifiers>,
|
||||
targetIds: ReturnType<typeof getContainerIdentifiers>,
|
||||
) => sourceIds.accountId === targetIds.accountId;
|
||||
|
||||
const areContainersIdentical = () => {
|
||||
const { source, target } = copyJobState;
|
||||
const sourceIds = getContainerIdentifiers(source);
|
||||
const targetIds = getContainerIdentifiers(target);
|
||||
|
||||
return (
|
||||
isSameAccount(sourceIds, targetIds) &&
|
||||
sourceIds.databaseId === targetIds.databaseId &&
|
||||
sourceIds.containerId === targetIds.containerId
|
||||
);
|
||||
};
|
||||
|
||||
const shouldNotShowPermissionScreen = () => {
|
||||
const { source, target, migrationType } = copyJobState;
|
||||
return (
|
||||
migrationType === CopyJobMigrationType.Offline &&
|
||||
isSameAccount(getContainerIdentifiers(source), getContainerIdentifiers(target))
|
||||
);
|
||||
};
|
||||
|
||||
const handleCopyJobSubmission = async () => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
await submitCreateCopyJob(copyJobState, handleCancel);
|
||||
} catch (error: unknown) {
|
||||
const errorMessage =
|
||||
error instanceof Error
|
||||
? error.message || "Failed to create copy job. Please try again later."
|
||||
: "Failed to create copy job. Please try again later.";
|
||||
setError(errorMessage);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handlePrimary = useCallback(() => {
|
||||
if (currentScreenKey === SCREEN_KEYS.SelectSourceAndTargetContainers && areContainersIdentical()) {
|
||||
setError("Source and destination containers cannot be the same. Please select different containers to proceed.");
|
||||
return;
|
||||
}
|
||||
|
||||
setError(null);
|
||||
const transitions = {
|
||||
[SCREEN_KEYS.SelectAccount]: shouldNotShowPermissionScreen()
|
||||
? SCREEN_KEYS.SelectSourceAndTargetContainers
|
||||
: SCREEN_KEYS.AssignPermissions,
|
||||
[SCREEN_KEYS.AssignPermissions]: SCREEN_KEYS.SelectSourceAndTargetContainers,
|
||||
[SCREEN_KEYS.SelectSourceAndTargetContainers]: SCREEN_KEYS.PreviewCopyJob,
|
||||
};
|
||||
|
||||
const nextScreen = transitions[currentScreenKey];
|
||||
if (nextScreen) {
|
||||
dispatch({ type: "NEXT", nextScreen });
|
||||
} else if (currentScreenKey === SCREEN_KEYS.PreviewCopyJob) {
|
||||
handleCopyJobSubmission();
|
||||
}
|
||||
}, [currentScreenKey, copyJobState, areContainersIdentical, handleCopyJobSubmission]);
|
||||
|
||||
const handlePrevious = useCallback(() => {
|
||||
dispatch({ type: "PREVIOUS" });
|
||||
}, []);
|
||||
|
||||
return {
|
||||
currentScreen,
|
||||
isPrimaryDisabled,
|
||||
isPreviousDisabled,
|
||||
handlePrimary,
|
||||
handlePrevious,
|
||||
handleCancel,
|
||||
primaryBtnText,
|
||||
error,
|
||||
setError,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user