fetch account details from account id instead of context

This commit is contained in:
Bikram Choudhury
2025-10-28 16:57:55 +05:30
parent e002a4505c
commit 5ba7ce2f10
11 changed files with 201 additions and 142 deletions

View File

@@ -1,22 +1,28 @@
import { configContext } from "ConfigContext";
import React from "react";
import { userContext } from "UserContext";
import { useSidePanel } from "../../../hooks/useSidePanel";
import { armRequest } from "../../../Utils/arm/request";
import {
cancel,
complete,
create,
listByDatabaseAccount,
pause,
resume
} from "../../../Utils/arm/generatedClients/dataTransferService/dataTransferJobs";
import { CreateJobRequest, DataTransferJobGetResults } from "../../../Utils/arm/generatedClients/dataTransferService/types";
import ContainerCopyMessages from "../ContainerCopyMessages";
import {
buildDataTransferJobPath,
convertTime,
convertToCamelCase,
COPY_JOB_API_VERSION,
COSMOS_SQL_COMPONENT,
extractErrorMessage,
formatUTCDateTime
formatUTCDateTime,
getAccountDetailsFromResourceId
} from "../CopyJobUtils";
import CreateCopyJobScreensProvider from "../CreateCopyJob/Screens/CreateCopyJobScreensProvider";
import { CopyJobStatusType } from "../Enums";
import { CopyJobActions, CopyJobStatusType } from "../Enums";
import { MonitorCopyJobsRefState } from "../MonitorCopyJobs/MonitorCopyJobRefState";
import { CopyJobContextState, CopyJobError, CopyJobType, DataTransferJobType } from "../Types";
import { CopyJobContextState, CopyJobError, CopyJobErrorType, CopyJobType } from "../Types";
export const openCreateCopyJobPanel = () => {
const sidePanelState = useSidePanel.getState()
@@ -37,19 +43,13 @@ export const getCopyJobs = async (): Promise<CopyJobType[]> => {
}
copyJobsAbortController = new AbortController();
try {
const path = buildDataTransferJobPath({
subscriptionId: userContext.subscriptionId,
resourceGroup: userContext.databaseAccount?.resourceGroup || "",
accountName: userContext.databaseAccount?.name || ""
});
const response: { value: DataTransferJobType[] } = await armRequest({
host: configContext.ARM_ENDPOINT,
path,
method: "GET",
apiVersion: COPY_JOB_API_VERSION,
signal: copyJobsAbortController.signal
});
const { subscriptionId, resourceGroup, accountName } = getAccountDetailsFromResourceId(userContext.databaseAccount?.id || "");
const response = await listByDatabaseAccount(
subscriptionId,
resourceGroup,
accountName,
copyJobsAbortController.signal
);
const jobs = response.value || [];
if (!Array.isArray(jobs)) {
@@ -74,14 +74,14 @@ export const getCopyJobs = async (): Promise<CopyJobType[]> => {
};
const formattedJobs: CopyJobType[] = jobs
.filter((job: DataTransferJobType) =>
.filter((job: DataTransferJobGetResults) =>
job.properties?.source?.component === COSMOS_SQL_COMPONENT &&
job.properties?.destination?.component === COSMOS_SQL_COMPONENT
)
.sort((current: DataTransferJobType, next: DataTransferJobType) =>
.sort((current: DataTransferJobGetResults, next: DataTransferJobGetResults) =>
new Date(next.properties.lastUpdatedUtcTime).getTime() - new Date(current.properties.lastUpdatedUtcTime).getTime()
)
.map((job: DataTransferJobType, index: number) => {
.map((job: DataTransferJobGetResults, index: number) => {
const dateTimeObj = formatUTCDateTime(job.properties.lastUpdatedUtcTime);
return {
@@ -93,7 +93,7 @@ export const getCopyJobs = async (): Promise<CopyJobType[]> => {
Duration: convertTime(job.properties.duration),
LastUpdatedTime: dateTimeObj.formattedDateTime,
timestamp: dateTimeObj.timestamp,
Error: job.properties.error ? extractErrorMessage(job.properties.error) : null,
Error: job.properties.error ? extractErrorMessage(job.properties.error as unknown as CopyJobErrorType) : null,
} as CopyJobType;
});
return formattedJobs;
@@ -107,32 +107,31 @@ export const getCopyJobs = async (): Promise<CopyJobType[]> => {
export const submitCreateCopyJob = async (state: CopyJobContextState, onSuccess: () => void) => {
try {
const { source, target, migrationType, jobName } = state;
const path = buildDataTransferJobPath({
subscriptionId: userContext.subscriptionId,
resourceGroup: userContext.databaseAccount?.resourceGroup || "",
accountName: userContext.databaseAccount?.name || "",
jobName
});
const { subscriptionId, resourceGroup, accountName } = getAccountDetailsFromResourceId(userContext.databaseAccount?.id || "");
const body = {
"properties": {
"source": {
"component": "CosmosDBSql",
"remoteAccountName": source?.account?.name,
"databaseName": source?.databaseId,
"containerName": source?.containerId
properties: {
source: {
component: "CosmosDBSql",
remoteAccountName: source?.account?.name,
databaseName: source?.databaseId,
containerName: source?.containerId
},
"destination": {
"component": "CosmosDBSql",
"databaseName": target?.databaseId,
"containerName": target?.containerId
destination: {
component: "CosmosDBSql",
databaseName: target?.databaseId,
containerName: target?.containerId
},
"mode": migrationType
mode: migrationType
}
};
} as unknown as CreateJobRequest;
const response: DataTransferJobType = await armRequest({
host: configContext.ARM_ENDPOINT, path, method: "PUT", body, apiVersion: COPY_JOB_API_VERSION
});
const response = await create(
subscriptionId,
resourceGroup,
accountName,
jobName,
body,
);
MonitorCopyJobsRefState.getState().ref?.refreshJobList();
onSuccess();
return response;
@@ -142,19 +141,29 @@ export const submitCreateCopyJob = async (state: CopyJobContextState, onSuccess:
}
}
export const updateCopyJobStatus = async (job: CopyJobType, action: string): Promise<DataTransferJobType> => {
export const updateCopyJobStatus = async (job: CopyJobType, action: string): Promise<DataTransferJobGetResults> => {
try {
const path = buildDataTransferJobPath({
subscriptionId: userContext.subscriptionId,
resourceGroup: userContext.databaseAccount?.resourceGroup || "",
accountName: userContext.databaseAccount?.name || "",
jobName: job.Name,
action: action
});
const response: DataTransferJobType = await armRequest({
host: configContext.ARM_ENDPOINT, path, method: "POST", apiVersion: COPY_JOB_API_VERSION
});
let updateFn = null;
switch (action.toLowerCase()) {
case CopyJobActions.pause:
updateFn = pause;
break;
case CopyJobActions.resume:
updateFn = resume;
break;
case CopyJobActions.cancel:
updateFn = cancel;
break;
case CopyJobActions.complete:
updateFn = complete;
break;
default:
throw new Error(`Unsupported action: ${action}`);
}
const { subscriptionId, resourceGroup, accountName } = getAccountDetailsFromResourceId(userContext.databaseAccount?.id || "");
const response = await updateFn?.(subscriptionId, resourceGroup, accountName, job.Name);
return response;
} catch (error) {
const errorMessage = JSON.stringify((error as CopyJobError).message || error.content || error);

View File

@@ -97,8 +97,8 @@ export default {
Actions: {
pause: "Pause",
resume: "Resume",
stop: "Stop",
cutover: "Cutover",
cancel: "Cancel",
complete: "Complete",
viewDetails: "View Details",
},
Status: {

View File

@@ -3,6 +3,7 @@ import React, { useCallback } from "react";
import { assignRole } from "../../../../../Utils/arm/RbacUtils";
import ContainerCopyMessages from "../../../ContainerCopyMessages";
import { useCopyJobContext } from "../../../Context/CopyJobContext";
import { getAccountDetailsFromResourceId } from "../../../CopyJobUtils";
import InfoTooltip from "../Components/InfoTooltip";
import PopoverMessage from "../Components/PopoverContainer";
import { PermissionSectionConfig } from "./hooks/usePermissionsSection";
@@ -19,12 +20,19 @@ const AddReadPermissionToDefaultIdentity: React.FC<AddManagedIdentityProps> = ()
const handleAddReadPermission = useCallback(async () => {
const { source, target } = copyJobState;
const selectedSourceAccount = source?.account;
try {
const {
subscriptionId: sourceSubscriptionId,
resourceGroup: sourceResourceGroup,
accountName: sourceAccountName
} = getAccountDetailsFromResourceId(selectedSourceAccount?.id);
setLoading(true);
const assignedRole = await assignRole(
source?.subscription?.subscriptionId,
source?.account?.resourceGroup,
source?.account?.name,
sourceSubscriptionId,
sourceResourceGroup,
sourceAccountName,
target?.account?.identity?.principalId!,
);
if (assignedRole) {

View File

@@ -3,7 +3,7 @@ import React, { useCallback, useState } from "react";
import { fetchDatabaseAccount } from "Utils/arm/databaseAccountUtils";
import ContainerCopyMessages from "../../../ContainerCopyMessages";
import { useCopyJobContext } from "../../../Context/CopyJobContext";
import { buildResourceLink } from "../../../CopyJobUtils";
import { buildResourceLink, getAccountDetailsFromResourceId } from "../../../CopyJobUtils";
import { PermissionSectionConfig } from "./hooks/usePermissionsSection";
import useWindowOpenMonitor from "./hooks/useWindowOpenMonitor";
@@ -16,17 +16,19 @@ const PointInTimeRestore: React.FC<AddManagedIdentityProps> = () => {
const onWindowClosed = useCallback(async () => {
try {
const selectedSourceAccount = source?.account;
const {
subscriptionId: sourceSubscriptionId,
resourceGroup: sourceResourceGroup,
accountName: sourceAccountName
} = getAccountDetailsFromResourceId(selectedSourceAccount?.id);
setLoading(true);
const account = await fetchDatabaseAccount(
source?.subscription?.subscriptionId,
source?.account?.resourceGroup,
source?.account?.name
sourceSubscriptionId,
sourceResourceGroup,
sourceAccountName
);
/* account.properties = {
backupPolicy: {
type: "Continuous"
}
} */
if (account) {
setCopyJobState((prevState) => ({
...prevState,

View File

@@ -1,6 +1,7 @@
import { DatabaseAccount } from "Contracts/DataModels";
import { useCallback, useState } from "react";
import { useCopyJobContext } from "../../../../Context/CopyJobContext";
import { getAccountDetailsFromResourceId } from "../../../../CopyJobUtils";
interface UseManagedIdentityUpdaterParams {
updateIdentityFn: (
@@ -24,11 +25,17 @@ const useManagedIdentity = (
const handleAddSystemIdentity = useCallback(async (): Promise<void> => {
try {
setLoading(true);
const { target } = copyJobState;
const selectedTargetAccount = copyJobState?.target?.account;
const {
subscriptionId: targetSubscriptionId,
resourceGroup: targetResourceGroup,
accountName: targetAccountName
} = getAccountDetailsFromResourceId(selectedTargetAccount?.id);
const updatedAccount = await updateIdentityFn(
target.subscriptionId,
target.account?.resourceGroup,
target.account?.name
targetSubscriptionId,
targetResourceGroup,
targetAccountName
);
if (updatedAccount) {
setCopyJobState((prevState) => ({

View File

@@ -5,6 +5,7 @@ import {
RoleDefinitionType
} from "../../../../../../Utils/arm/RbacUtils";
import ContainerCopyMessages from "../../../../ContainerCopyMessages";
import { getAccountDetailsFromResourceId } from "../../../../CopyJobUtils";
import {
BackupPolicyType,
CopyJobMigrationType,
@@ -68,10 +69,17 @@ const PERMISSION_SECTIONS_CONFIG: PermissionSectionConfig[] = [
disabled: true,
validate: async (state: CopyJobContextState) => {
const principalId = state?.target?.account?.identity?.principalId;
const selectedSourceAccount = state?.source?.account;
const {
subscriptionId: sourceSubscriptionId,
resourceGroup: sourceResourceGroup,
accountName: sourceAccountName
} = getAccountDetailsFromResourceId(selectedSourceAccount?.id);
const rolesAssigned = await fetchRoleAssignments(
state.source?.subscription?.subscriptionId,
state.source?.account?.resourceGroup,
state.source?.account?.name,
sourceSubscriptionId,
sourceResourceGroup,
sourceAccountName,
principalId
);

View File

@@ -34,7 +34,7 @@ export enum CopyJobStatusType {
export enum CopyJobActions {
pause = "pause",
stop = "cancel",
resume = "resume",
cutover = "complete"
cancel = "cancel",
complete = "complete",
}

View File

@@ -23,9 +23,9 @@ const CopyJobActionMenu: React.FC<CopyJobActionMenuProps> = ({ job, handleClick
onClick: () => handleClick(job, CopyJobActions.pause)
},
{
key: CopyJobActions.stop,
text: ContainerCopyMessages.MonitorJobs.Actions.stop,
onClick: () => handleClick(job, CopyJobActions.stop)
key: CopyJobActions.cancel,
text: ContainerCopyMessages.MonitorJobs.Actions.cancel,
onClick: () => handleClick(job, CopyJobActions.cancel)
},
{
key: CopyJobActions.resume,
@@ -50,9 +50,9 @@ const CopyJobActionMenu: React.FC<CopyJobActionMenuProps> = ({ job, handleClick
const filteredItems = baseItems.filter(item => item.key !== CopyJobActions.resume);
if (job.Mode === CopyJobMigrationType.Online) {
filteredItems.push({
key: CopyJobActions.cutover,
text: ContainerCopyMessages.MonitorJobs.Actions.cutover,
onClick: () => handleClick(job, CopyJobActions.cutover)
key: CopyJobActions.complete,
text: ContainerCopyMessages.MonitorJobs.Actions.complete,
onClick: () => handleClick(job, CopyJobActions.complete)
});
}
return filteredItems;

View File

@@ -3,13 +3,13 @@
Run "npm run generateARMClients" to regenerate
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2023-11-15-preview/dataTransferService.json
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/DocumentDB/preview/2025-05-01-preview/dataTransferService.json
*/
import { configContext } from "../../../../ConfigContext";
import { armRequest } from "../../request";
import * as Types from "./types";
const apiVersion = "2023-11-15-preview";
const apiVersion = "2025-05-01-preview";
/* Creates a Data Transfer Job. */
export async function create(
@@ -67,12 +67,24 @@ export async function cancel(
return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "POST", apiVersion });
}
/* Completes a Data Transfer Online Job. */
export async function complete(
subscriptionId: string,
resourceGroupName: string,
accountName: string,
jobName: string,
): Promise<Types.DataTransferJobGetResults> {
const path = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.DocumentDB/databaseAccounts/${accountName}/dataTransferJobs/${jobName}/complete`;
return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "POST", apiVersion });
}
/* Get a list of Data Transfer jobs. */
export async function listByDatabaseAccount(
subscriptionId: string,
resourceGroupName: string,
accountName: string,
signal?: AbortSignal
): Promise<Types.DataTransferJobFeedResults> {
const path = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.DocumentDB/databaseAccounts/${accountName}/dataTransferJobs`;
return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "GET", apiVersion });
return armRequest({ host: configContext.ARM_ENDPOINT, path, method: "GET", apiVersion, signal });
}

View File

@@ -3,99 +3,112 @@
Run "npm run generateARMClients" to regenerate
Edting this file directly should be done with extreme caution as not to diverge from ARM REST specs
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2023-11-15-preview/dataTransferService.json
Generated from: https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/DocumentDB/preview/2025-05-01-preview/dataTransferService.json
*/
/* Base class for all DataTransfer source/sink */
export interface DataTransferDataSourceSink {
/* undocumented */
component: "CosmosDBCassandra" | "CosmosDBMongo" | "CosmosDBSql" | "AzureBlobStorage";
/* undocumented */
component: "CosmosDBCassandra" | "CosmosDBMongo" | "CosmosDBMongoVCore" | "CosmosDBSql" | "AzureBlobStorage";
}
/* A base CosmosDB data source/sink */
export type BaseCosmosDataTransferDataSourceSink = DataTransferDataSourceSink & {
/* undocumented */
remoteAccountName?: string;
/* undocumented */
remoteAccountName?: string;
};
/* A CosmosDB Cassandra API data source/sink */
export type CosmosCassandraDataTransferDataSourceSink = BaseCosmosDataTransferDataSourceSink & {
/* undocumented */
keyspaceName: string;
/* undocumented */
tableName: string;
/* undocumented */
keyspaceName: string;
/* undocumented */
tableName: string;
};
/* A CosmosDB Mongo API data source/sink */
export type CosmosMongoDataTransferDataSourceSink = BaseCosmosDataTransferDataSourceSink & {
/* undocumented */
databaseName: string;
/* undocumented */
collectionName: string;
/* undocumented */
databaseName: string;
/* undocumented */
collectionName: string;
};
/* A CosmosDB Mongo vCore API data source/sink */
export type CosmosMongoVCoreDataTransferDataSourceSink = DataTransferDataSourceSink & {
/* undocumented */
databaseName: string;
/* undocumented */
collectionName: string;
/* undocumented */
hostName?: string;
/* undocumented */
connectionStringKeyVaultUri?: string;
};
/* A CosmosDB No Sql API data source/sink */
export type CosmosSqlDataTransferDataSourceSink = BaseCosmosDataTransferDataSourceSink & {
/* undocumented */
databaseName: string;
/* undocumented */
containerName: string;
/* undocumented */
databaseName: string;
/* undocumented */
containerName: string;
};
/* An Azure Blob Storage data source/sink */
export type AzureBlobDataTransferDataSourceSink = DataTransferDataSourceSink & {
/* undocumented */
containerName: string;
/* undocumented */
endpointUrl?: string;
/* undocumented */
containerName: string;
/* undocumented */
endpointUrl?: string;
};
/* The properties of a DataTransfer Job */
export interface DataTransferJobProperties {
/* Job Name */
readonly jobName?: string;
/* Source DataStore details */
source: DataTransferDataSourceSink;
/* Job Name */
readonly jobName?: string;
/* Source DataStore details */
source: DataTransferDataSourceSink;
/* Destination DataStore details */
destination: DataTransferDataSourceSink;
/* Destination DataStore details */
destination: DataTransferDataSourceSink;
/* Job Status */
readonly status?: string;
/* Processed Count. */
readonly processedCount?: number;
/* Total Count. */
readonly totalCount?: number;
/* Last Updated Time (ISO-8601 format). */
readonly lastUpdatedUtcTime?: string;
/* Worker count */
workerCount?: number;
/* Error response for Faulted job */
readonly error?: unknown;
/* Job Status */
readonly status?: string;
/* Processed Count. */
readonly processedCount?: number
/* Total Count. */
readonly totalCount?: number;
/* Last Updated Time (ISO-8601 format). */
readonly lastUpdatedUtcTime?: string;
/* Worker count */
workerCount?: number;
/* Error response for Faulted job */
readonly error?: unknown;
/* Total Duration of Job */
readonly duration?: string;
/* Mode of job execution */
mode?: "Offline" | "Online";
/* Total Duration of Job */
readonly duration?: string
/* Mode of job execution */
mode?: "Offline" | "Online";
}
/* Parameters to create Data Transfer Job */
export type CreateJobRequest = unknown & {
/* Data Transfer Create Job Properties */
properties: DataTransferJobProperties;
/* Data Transfer Create Job Properties */
properties: DataTransferJobProperties;
};
/* A Cosmos DB Data Transfer Job */
export type DataTransferJobGetResults = unknown & {
/* undocumented */
properties?: DataTransferJobProperties;
/* undocumented */
properties?: DataTransferJobProperties;
};
/* The List operation response, that contains the Data Transfer jobs and their properties. */
export interface DataTransferJobFeedResults {
/* List of Data Transfer jobs and their properties. */
readonly value?: DataTransferJobGetResults[];
/* List of Data Transfer jobs and their properties. */
readonly value?: DataTransferJobGetResults[];
/* URL to get the next set of Data Transfer job list results if there are any. */
readonly nextLink?: string;
/* URL to get the next set of Data Transfer job list results if there are any. */
readonly nextLink?: string;
}

View File

@@ -21,9 +21,9 @@ const version = "2025-05-01-preview";
"cosmos" | "managedCassandra" | "mongorbac" | "notebook" | "privateEndpointConnection" | "privateLinkResources" |
"rbac" | "restorable" | "services" | "dataTransferService"
*/
const githubResourceName = "cosmos-db";
const deResourceName = "cosmos";
const schemaURL = `https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/${version}/${githubResourceName}.json`;
const githubResourceName = "dataTransferService";
const deResourceName = "dataTransferService";
const schemaURL = `https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/DocumentDB/preview/${version}/${githubResourceName}.json`;
const outputDir = path.join(__dirname, `../../src/Utils/arm/generatedClients/${deResourceName}`);
// Array of strings to use for eventual output