feat: Redesign container-copy flow to select destination account and enable cross-account container creation (#2436)

* feat: Redesign container-copy flow to select destination account and enable cross-account container creation

* upgrade RBAC permissions from read only to read-write

* fix copyjob playwright tests

* swap source-destination content

* fix formating

* use targetAccountOverride for capability checks in AddCollectionPanel

* feat: localize ContainerCopy hardcoded strings using i18next and refactor readDatabasesWithARM

* removed container copy messages json file
This commit is contained in:
BChoudhury-ms
2026-05-11 17:24:51 +05:30
committed by GitHub
parent 762e12a3f9
commit d374458e1b
81 changed files with 1810 additions and 1076 deletions
@@ -457,13 +457,13 @@ describe("CopyJobActions", () => {
jobName: "test-job",
migrationType: "online" as any,
source: {
subscription: {} as any,
subscriptionId: "sub-123",
account: { id: "account-1", name: "source-account" } as any,
databaseId: "source-db",
containerId: "source-container",
},
target: {
subscriptionId: "sub-123",
subscription: {} as any,
account: { id: "account-1", name: "target-account" } as any,
databaseId: "target-db",
containerId: "target-container",
@@ -498,7 +498,7 @@ describe("CopyJobActions", () => {
);
const callArgs = (dataTransferService.create as jest.Mock).mock.calls[0][4];
expect(callArgs.properties.source.remoteAccountName).toBeUndefined();
expect(callArgs.properties.destination.remoteAccountName).toBeUndefined();
expect(mockRefreshJobList).toHaveBeenCalled();
expect(mockOnSuccess).toHaveBeenCalled();
@@ -509,13 +509,13 @@ describe("CopyJobActions", () => {
jobName: "cross-account-job",
migrationType: "offline" as any,
source: {
subscription: {} as any,
subscriptionId: "sub-123",
account: { id: "account-1", name: "source-account" } as any,
databaseId: "source-db",
containerId: "source-container",
},
target: {
subscriptionId: "sub-456",
subscription: {} as any,
account: { id: "account-2", name: "target-account" } as any,
databaseId: "target-db",
containerId: "target-container",
@@ -528,7 +528,7 @@ describe("CopyJobActions", () => {
await submitCreateCopyJob(mockState, mockOnSuccess);
const callArgs = (dataTransferService.create as jest.Mock).mock.calls[0][4];
expect(callArgs.properties.source.remoteAccountName).toBe("source-account");
expect(callArgs.properties.destination.remoteAccountName).toBe("target-account");
expect(mockOnSuccess).toHaveBeenCalled();
});
@@ -537,13 +537,13 @@ describe("CopyJobActions", () => {
jobName: "failing-job",
migrationType: "online" as any,
source: {
subscription: {} as any,
subscriptionId: "sub-123",
account: { id: "account-1", name: "source-account" } as any,
databaseId: "source-db",
containerId: "source-container",
},
target: {
subscriptionId: "sub-123",
subscription: {} as any,
account: { id: "account-1", name: "target-account" } as any,
databaseId: "target-db",
containerId: "target-container",
@@ -566,13 +566,13 @@ describe("CopyJobActions", () => {
jobName: "test-job",
migrationType: "online" as any,
source: {
subscription: {} as any,
subscriptionId: "sub-123",
account: { id: "account-1", name: "source-account" } as any,
databaseId: "source-db",
containerId: "source-container",
},
target: {
subscriptionId: "sub-123",
subscription: {} as any,
account: { id: "account-1", name: "target-account" } as any,
databaseId: "target-db",
containerId: "target-container",
@@ -1,4 +1,5 @@
import Explorer from "Explorer/Explorer";
import { Keys, t } from "Localization";
import React from "react";
import { userContext } from "UserContext";
import { getDataTransferJobs } from "../../../Common/dataAccess/dataTransfers";
@@ -15,7 +16,6 @@ import {
CreateJobRequest,
DataTransferJobGetResults,
} from "../../../Utils/arm/generatedClients/dataTransferService/types";
import ContainerCopyMessages from "../ContainerCopyMessages";
import {
convertTime,
convertToCamelCase,
@@ -35,7 +35,7 @@ export const openCreateCopyJobPanel = (explorer: Explorer) => {
const sidePanelState = useSidePanel.getState();
sidePanelState.setPanelHasConsole(false);
sidePanelState.openSidePanel(
ContainerCopyMessages.createCopyJobPanelTitle,
t(Keys.containerCopy.createCopyJob.panelTitle),
<CreateCopyJobScreensProvider explorer={explorer} />,
"650px",
);
@@ -45,7 +45,7 @@ export const openCopyJobDetailsPanel = (job: CopyJobType) => {
const sidePanelState = useSidePanel.getState();
sidePanelState.setPanelHasConsole(false);
sidePanelState.openSidePanel(
ContainerCopyMessages.copyJobDetailsPanelTitle(job.Name),
job.Name || t(Keys.containerCopy.jobDetails.panelTitleDefault),
<CopyJobDetails job={job} />,
"650px",
);
@@ -137,12 +137,12 @@ export const submitCreateCopyJob = async (state: CopyJobContextState, onSuccess:
properties: {
source: {
component: "CosmosDBSql",
...(isSameAccount ? {} : { remoteAccountName: source?.account?.name }),
databaseName: source?.databaseId,
containerName: source?.containerId,
},
destination: {
component: "CosmosDBSql",
...(isSameAccount ? {} : { remoteAccountName: target?.account?.name }),
databaseName: target?.databaseId,
containerName: target?.containerId,
},
@@ -193,7 +193,7 @@ export const updateCopyJobStatus = async (job: CopyJobType, action: string): Pro
const pattern = new RegExp(`'(${statusList.join("|")})'`, "g");
const normalizedErrorMessage = errorMessage.replace(
pattern,
`'${ContainerCopyMessages.MonitorJobs.Status.InProgress}'`,
`'${t(Keys.containerCopy.monitorJobs.status.inProgress)}'`,
);
logError(`Error updating copy job status: ${normalizedErrorMessage}`, "CopyJob/CopyJobActions.updateCopyJobStatus");
throw error;