import { DefaultButton, DirectionalHint, Dropdown, IDropdownOption, Icon, IconButton, Link, MessageBar, Stack, Text, TooltipHost, } from "@fluentui/react"; import * as Constants from "Common/Constants"; import { handleError } from "Common/ErrorHandlingUtils"; import { createCollection } from "Common/dataAccess/createCollection"; import { DataTransferParams, initiateDataTransfer } from "Common/dataAccess/dataTransfers"; import * as DataModels from "Contracts/DataModels"; import * as ViewModels from "Contracts/ViewModels"; import { getPartitionKeyName, getPartitionKeyPlaceHolder, getPartitionKeySubtext, getPartitionKeyTooltipText, } from "Explorer/Controls/Settings/SettingsUtils"; import Explorer from "Explorer/Explorer"; import { RightPaneForm } from "Explorer/Panes/RightPaneForm/RightPaneForm"; import { useDatabases } from "Explorer/useDatabases"; import { userContext } from "UserContext"; import { getCollectionName } from "Utils/APITypeUtils"; import { useSidePanel } from "hooks/useSidePanel"; import * as React from "react"; export interface ChangePartitionKeyPaneProps { sourceDatabase: ViewModels.Database; sourceCollection: ViewModels.Collection; explorer: Explorer; onClose: () => Promise; } export const ChangePartitionKeyPane: React.FC = ({ sourceDatabase, sourceCollection, explorer, onClose, }) => { const [targetCollectionId, setTargetCollectionId] = React.useState(); const [createNewContainer, setCreateNewContainer] = React.useState(true); const [formError, setFormError] = React.useState(); const [isExecuting, setIsExecuting] = React.useState(false); const [subPartitionKeys, setSubPartitionKeys] = React.useState([]); const [partitionKey, setPartitionKey] = React.useState(); const getCollectionOptions = (): IDropdownOption[] => { return sourceDatabase .collections() .filter((collection) => collection.id !== sourceCollection.id) .map((collection) => ({ key: collection.id(), text: collection.id(), })); }; const submit = async () => { if (!validateInputs()) { return; } setIsExecuting(true); try { createNewContainer && (await createContainer()); await createDataTransferJob(); await onClose(); } catch (error) { handleError(error, "ChangePartitionKey", "Failed to start data transfer job"); } setIsExecuting(false); useSidePanel.getState().closeSidePanel(); }; const validateInputs = (): boolean => { if (!createNewContainer && !targetCollectionId) { setFormError("Choose an existing container"); return false; } return true; }; const createDataTransferJob = async () => { const jobName = `Portal_${targetCollectionId}_${Math.floor(Date.now() / 1000)}`; const dataTransferParams: DataTransferParams = { jobName, apiType: userContext.apiType, subscriptionId: userContext.subscriptionId, resourceGroupName: userContext.resourceGroup, accountName: userContext.databaseAccount.name, sourceDatabaseName: sourceDatabase.id(), sourceCollectionName: sourceCollection.id(), targetDatabaseName: sourceDatabase.id(), targetCollectionName: targetCollectionId, }; await initiateDataTransfer(dataTransferParams); }; const createContainer = async () => { const partitionKeyString = partitionKey.trim(); const partitionKeyData: DataModels.PartitionKey = partitionKeyString ? { paths: [partitionKeyString, ...(subPartitionKeys.length > 0 ? subPartitionKeys : [])], kind: subPartitionKeys.length > 0 ? "MultiHash" : "Hash", version: 2, } : undefined; const createCollectionParams: DataModels.CreateCollectionParams = { createNewDatabase: false, collectionId: targetCollectionId, databaseId: sourceDatabase.id(), databaseLevelThroughput: isSelectedDatabaseSharedThroughput(), offerThroughput: sourceCollection.offer()?.manualThroughput, autoPilotMaxThroughput: sourceCollection.offer()?.autoscaleMaxThroughput, partitionKey: partitionKeyData, }; await createCollection(createCollectionParams); await explorer.refreshAllDatabases(); }; const isSelectedDatabaseSharedThroughput = (): boolean => { const selectedDatabase = useDatabases .getState() .databases?.find((database) => database.id() === sourceDatabase.id()); return !!selectedDatabase?.offer(); }; return ( When changing a container’s partition key, you will need to create a destination container with the correct partition key. You may also select an existing destination container.  Learn more Database id
setCreateNewContainer(true)} /> New container setCreateNewContainer(false)} /> Existing container
{createNewContainer ? ( All configurations except for unique keys will be copied from the source container {`${getCollectionName()} id`} ) => setTargetCollectionId(event.target.value)} /> {getPartitionKeyName(userContext.apiType)} {getPartitionKeySubtext(userContext.features.partitionKeyDefault, userContext.apiType)} ) => { if (!partitionKey && !event.target.value.startsWith("/")) { setPartitionKey("/" + event.target.value); } else { setPartitionKey(event.target.value); } }} /> {subPartitionKeys.map((subPartitionKey: string, index: number) => { return (
0 ? 1 : 0} className="panelTextField" autoComplete="off" placeholder={getPartitionKeyPlaceHolder(userContext.apiType, index)} aria-label={getPartitionKeyName(userContext.apiType)} pattern={".*"} title={""} value={subPartitionKey} onChange={(event: React.ChangeEvent) => { const keys = [...subPartitionKeys]; if (!keys[index] && !event.target.value.startsWith("/")) { keys[index] = "/" + event.target.value.trim(); setSubPartitionKeys(keys); } else { keys[index] = event.target.value.trim(); setSubPartitionKeys(keys); } }} /> { const keys = subPartitionKeys.filter((uniqueKey, j) => index !== j); setSubPartitionKeys(keys); }} />
); })} = Constants.BackendDefaults.maxNumMultiHashPartition} onClick={() => setSubPartitionKeys([...subPartitionKeys, ""])} > Add hierarchical partition key {subPartitionKeys.length > 0 && ( This feature allows you to partition your data with up to three levels of keys for better data distribution. Requires .NET V3, Java V4 SDK, or preview JavaScript V3 SDK.{" "} Learn more )}
) : ( {`${getCollectionName()}`} , collection: IDropdownOption) => { setTargetCollectionId(collection.key as string); setFormError(""); }} defaultSelectedKey={targetCollectionId} responsiveMode={999} /> )}
); };