import { DefaultButton, FontWeights, Link, MessageBar, MessageBarType, PrimaryButton, ProgressIndicator, Stack, Text, } from "@fluentui/react"; import * as React from "react"; import * as ViewModels from "../../../../Contracts/ViewModels"; import { handleError } from "Common/ErrorHandlingUtils"; import { cancelDataTransferJob, pollDataTransferJob } from "Common/dataAccess/dataTransfers"; import Explorer from "Explorer/Explorer"; import { ChangePartitionKeyPane } from "Explorer/Panes/ChangePartitionKeyPane/ChangePartitionKeyPane"; import { CosmosSqlDataTransferDataSourceSink, DataTransferJobGetResults, } from "Utils/arm/generatedClients/dataTransferService/types"; import { refreshDataTransferJobs, useDataTransferJobs } from "hooks/useDataTransferJobs"; import { useSidePanel } from "hooks/useSidePanel"; import { userContext } from "../../../../UserContext"; export interface PartitionKeyComponentProps { database: ViewModels.Database; collection: ViewModels.Collection; explorer: Explorer; } export const PartitionKeyComponent: React.FC = ({ database, collection, explorer }) => { const { dataTransferJobs } = useDataTransferJobs(); const [portalDataTransferJob, setPortalDataTransferJob] = React.useState(null); React.useEffect(() => { const loadDataTransferJobs = refreshDataTransferOperations; loadDataTransferJobs(); }, []); React.useEffect(() => { const currentJob = findPortalDataTransferJob(); setPortalDataTransferJob(currentJob); startPollingforUpdate(currentJob); }, [dataTransferJobs]); const isHierarchicalPartitionedContainer = (): boolean => collection.partitionKey?.kind === "MultiHash"; const getPartitionKeyValue = (): string => { return (collection.partitionKeyProperties || []).map((property) => "/" + property).join(", "); }; const partitionKeyName = "Partition key"; const partitionKeyValue = getPartitionKeyValue(); const textHeadingStyle = { root: { fontWeight: FontWeights.semibold, fontSize: 16 }, }; const textSubHeadingStyle = { root: { fontWeight: FontWeights.semibold }, }; const startPollingforUpdate = (currentJob: DataTransferJobGetResults) => { if (isCurrentJobInProgress(currentJob)) { const jobName = currentJob?.properties?.jobName; try { pollDataTransferJob( jobName, userContext.subscriptionId, userContext.resourceGroup, userContext.databaseAccount.name, ); } catch (error) { handleError(error, "ChangePartitionKey", `Failed to complete data transfer job ${jobName}`); } } }; const cancelRunningDataTransferJob = async (currentJob: DataTransferJobGetResults) => { await cancelDataTransferJob( userContext.subscriptionId, userContext.resourceGroup, userContext.databaseAccount.name, currentJob?.properties?.jobName, ); }; const isCurrentJobInProgress = (currentJob: DataTransferJobGetResults) => { const jobStatus = currentJob?.properties?.status; return ( jobStatus && jobStatus !== "Completed" && jobStatus !== "Cancelled" && jobStatus !== "Failed" && jobStatus !== "Faulted" ); }; const refreshDataTransferOperations = async () => { await refreshDataTransferJobs( userContext.subscriptionId, userContext.resourceGroup, userContext.databaseAccount.name, ); }; const findPortalDataTransferJob = (): DataTransferJobGetResults => { return dataTransferJobs.find((feed: DataTransferJobGetResults) => { const sourceSink: CosmosSqlDataTransferDataSourceSink = feed?.properties ?.source as CosmosSqlDataTransferDataSourceSink; return sourceSink.databaseName === collection.databaseId && sourceSink.containerName === collection.id(); }); }; const getProgressDescription = (): string => { const processedCount = portalDataTransferJob?.properties?.processedCount; const totalCount = portalDataTransferJob?.properties?.totalCount; const processedCountString = totalCount > 0 ? `(${processedCount} of ${totalCount} documents processed)` : ""; return `${portalDataTransferJob?.properties?.status} ${processedCountString}`; }; const startPartitionkeyChangeWorkflow = () => { useSidePanel .getState() .openSidePanel( "Change partition key", , ); }; const getPercentageComplete = () => { const jobStatus = portalDataTransferJob?.properties?.status; const isCompleted = jobStatus === "Completed"; if (isCompleted) { return 1; } const processedCount = portalDataTransferJob?.properties?.processedCount; const totalCount = portalDataTransferJob?.properties?.totalCount; const isJobInProgress = isCurrentJobInProgress(portalDataTransferJob); return isJobInProgress ? (totalCount > 0 ? processedCount / totalCount : null) : 0; }; return ( Change {partitionKeyName.toLowerCase()} Current {partitionKeyName.toLowerCase()} Partitioning {partitionKeyValue} {isHierarchicalPartitionedContainer() ? "Hierarchical" : "Non-hierarchical"} To safeguard the integrity of the data being copied to the new container, ensure that no updates are made to the source container for the entire duration of the partition key change process. Learn more To change the partition key, a new destination container must be created or an existing destination container selected. Data will then be copied to the destination container. {portalDataTransferJob && ( {partitionKeyName} change job {isCurrentJobInProgress(portalDataTransferJob) && ( cancelRunningDataTransferJob(portalDataTransferJob)} /> )} )} ); };