mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2026-04-24 15:31:50 +01:00
Merge branch 'master' of https://github.com/Azure/cosmos-explorer into users/aisayas/known-authorities
This commit is contained in:
@@ -7,8 +7,7 @@ import {
|
||||
AddGlobalSecondaryIndexPanelProps,
|
||||
} from "Explorer/Panes/AddGlobalSecondaryIndexPanel/AddGlobalSecondaryIndexPanel";
|
||||
import { useDatabases } from "Explorer/useDatabases";
|
||||
import { Keys } from "Localization/Keys.generated";
|
||||
import { t } from "Localization/t";
|
||||
import { Keys, t } from "Localization";
|
||||
import { isFabric, isFabricNative, openRestoreContainerDialog } from "Platform/Fabric/FabricUtil";
|
||||
import { Action } from "Shared/Telemetry/TelemetryConstants";
|
||||
import { traceOpen } from "Shared/Telemetry/TelemetryProcessor";
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
} from "@fluentui/react";
|
||||
import React, { FC, useEffect } from "react";
|
||||
import create, { UseStore } from "zustand";
|
||||
import { Keys, t } from "Localization";
|
||||
|
||||
export interface DialogState {
|
||||
visible: boolean;
|
||||
@@ -88,7 +89,7 @@ export const useDialog: UseStore<DialogState> = create((set, get) => ({
|
||||
isModal: true,
|
||||
title,
|
||||
subText,
|
||||
primaryButtonText: "Close",
|
||||
primaryButtonText: t(Keys.common.close),
|
||||
secondaryButtonText: undefined,
|
||||
onPrimaryButtonClick: () => {
|
||||
get().closeDialog();
|
||||
|
||||
@@ -44,6 +44,7 @@ import { useCommandBar } from "../../Menus/CommandBar/CommandBarComponentAdapter
|
||||
import { SettingsTabV2 } from "../../Tabs/SettingsTabV2";
|
||||
import "./SettingsComponent.less";
|
||||
import { mongoIndexingPolicyAADError } from "./SettingsRenderUtils";
|
||||
import { Keys, t } from "Localization";
|
||||
import {
|
||||
ConflictResolutionComponent,
|
||||
ConflictResolutionComponentProps,
|
||||
@@ -689,12 +690,12 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
||||
private onDataMaskingContentChange = (newDataMasking: DataModels.DataMaskingPolicy): void => {
|
||||
const validationErrors = [];
|
||||
if (newDataMasking.includedPaths === undefined || newDataMasking.includedPaths === null) {
|
||||
validationErrors.push("includedPaths is required");
|
||||
validationErrors.push(t(Keys.controls.settings.dataMasking.includedPathsRequired));
|
||||
} else if (!Array.isArray(newDataMasking.includedPaths)) {
|
||||
validationErrors.push("includedPaths must be an array");
|
||||
validationErrors.push(t(Keys.controls.settings.dataMasking.includedPathsMustBeArray));
|
||||
}
|
||||
if (newDataMasking.excludedPaths !== undefined && !Array.isArray(newDataMasking.excludedPaths)) {
|
||||
validationErrors.push("excludedPaths must be an array if provided");
|
||||
validationErrors.push(t(Keys.controls.settings.dataMasking.excludedPathsMustBeArray));
|
||||
}
|
||||
|
||||
this.setState({
|
||||
@@ -896,7 +897,7 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
||||
const buttons: CommandButtonComponentProps[] = [];
|
||||
const isExecuting = this.props.settingsTab.isExecuting();
|
||||
if (this.saveSettingsButton.isVisible()) {
|
||||
const label = "Save";
|
||||
const label = t(Keys.common.save);
|
||||
buttons.push({
|
||||
iconSrc: SaveIcon,
|
||||
iconAlt: label,
|
||||
@@ -909,7 +910,7 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
||||
}
|
||||
|
||||
if (this.discardSettingsChangesButton.isVisible()) {
|
||||
const label = "Discard";
|
||||
const label = t(Keys.common.discard);
|
||||
buttons.push({
|
||||
iconSrc: DiscardIcon,
|
||||
iconAlt: label,
|
||||
@@ -934,9 +935,10 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
||||
const numberOfRegions = userContext.databaseAccount?.properties.locations?.length || 1;
|
||||
const throughputDelta = (newThroughput - this.offer.autoscaleMaxThroughput) * numberOfRegions;
|
||||
if (throughputCap && throughputCap !== -1 && throughputCap - this.totalThroughputUsed < throughputDelta) {
|
||||
throughputError = `Your account is currently configured with a total throughput limit of ${throughputCap} RU/s. This update isn't possible because it would increase the total throughput to ${
|
||||
this.totalThroughputUsed + throughputDelta
|
||||
} RU/s. Change total throughput limit in cost management.`;
|
||||
throughputError = t(Keys.controls.settings.throughput.throughputCapError, {
|
||||
throughputCap: String(throughputCap),
|
||||
newTotalThroughput: String(this.totalThroughputUsed + throughputDelta),
|
||||
});
|
||||
}
|
||||
this.setState({ autoPilotThroughput: newThroughput, throughputError });
|
||||
};
|
||||
@@ -947,9 +949,10 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
||||
const numberOfRegions = userContext.databaseAccount?.properties.locations?.length || 1;
|
||||
const throughputDelta = (newThroughput - this.offer.manualThroughput) * numberOfRegions;
|
||||
if (throughputCap && throughputCap !== -1 && throughputCap - this.totalThroughputUsed < throughputDelta) {
|
||||
throughputError = `Your account is currently configured with a total throughput limit of ${throughputCap} RU/s. This update isn't possible because it would increase the total throughput to ${
|
||||
this.totalThroughputUsed + throughputDelta
|
||||
} RU/s. Change total throughput limit in cost management.`;
|
||||
throughputError = t(Keys.controls.settings.throughput.throughputCapError, {
|
||||
throughputCap: String(throughputCap),
|
||||
newTotalThroughput: String(this.totalThroughputUsed + throughputDelta),
|
||||
});
|
||||
}
|
||||
this.setState({ throughput: newThroughput, throughputError });
|
||||
};
|
||||
@@ -1560,7 +1563,7 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
||||
}
|
||||
>
|
||||
{this.shouldShowKeyspaceSharedThroughputMessage() && (
|
||||
<div>This table shared throughput is configured at the keyspace</div>
|
||||
<div>{t(Keys.controls.settings.scale.keyspaceSharedThroughput)}</div>
|
||||
)}
|
||||
|
||||
<div
|
||||
|
||||
@@ -22,6 +22,7 @@ import {
|
||||
Stack,
|
||||
Text,
|
||||
} from "@fluentui/react";
|
||||
import { Keys, t } from "Localization";
|
||||
import * as React from "react";
|
||||
import { Urls } from "../../../Common/Constants";
|
||||
import { StyleConstants } from "../../../Common/StyleConstants";
|
||||
@@ -338,10 +339,12 @@ export const getEstimatedSpendingElement = (
|
||||
const ruRange: string = isAutoscale ? throughput / 10 + " RU/s - " : "";
|
||||
return (
|
||||
<Stack>
|
||||
<Text style={{ fontWeight: 600, color: "var(--colorNeutralForeground1)" }}>Cost estimate*</Text>
|
||||
<Text style={{ fontWeight: 600, color: "var(--colorNeutralForeground1)" }}>
|
||||
{t(Keys.controls.settings.costEstimate.title)}
|
||||
</Text>
|
||||
{costElement}
|
||||
<Text style={{ fontWeight: 600, marginTop: 15, color: "var(--colorNeutralForeground1)" }}>
|
||||
How we calculate this
|
||||
{t(Keys.controls.settings.costEstimate.howWeCalculate)}
|
||||
</Text>
|
||||
<Stack id="throughputSpendElement" style={{ marginTop: 5 }}>
|
||||
<span>
|
||||
@@ -353,7 +356,8 @@ export const getEstimatedSpendingElement = (
|
||||
</span>
|
||||
<span>
|
||||
{priceBreakdown.currencySign}
|
||||
{priceBreakdown.pricePerRu}/RU
|
||||
{priceBreakdown.pricePerRu}
|
||||
{t(Keys.controls.settings.costEstimate.perRu)}
|
||||
</span>
|
||||
</Stack>
|
||||
<Text style={{ marginTop: 15, color: "var(--colorNeutralForeground1)" }}>
|
||||
@@ -365,18 +369,16 @@ export const getEstimatedSpendingElement = (
|
||||
|
||||
export const manualToAutoscaleDisclaimerElement: JSX.Element = (
|
||||
<Text styles={infoAndToolTipTextStyle} id="manualToAutoscaleDisclaimerElement">
|
||||
The starting autoscale max RU/s will be determined by the system, based on the current manual throughput settings
|
||||
and storage of your resource. After autoscale has been enabled, you can change the max RU/s.{" "}
|
||||
<Link href={Urls.autoscaleMigration}>Learn more</Link>
|
||||
{t(Keys.controls.settings.throughput.manualToAutoscaleDisclaimer)}{" "}
|
||||
<Link href={Urls.autoscaleMigration}>{t(Keys.common.learnMore)}</Link>
|
||||
</Text>
|
||||
);
|
||||
|
||||
export const ttlWarning: JSX.Element = (
|
||||
<Text styles={infoAndToolTipTextStyle}>
|
||||
The system will automatically delete items based on the TTL value (in seconds) you provide, without needing a delete
|
||||
operation explicitly issued by a client application. For more information see,{" "}
|
||||
{t(Keys.controls.settings.throughput.ttlWarningText)}{" "}
|
||||
<Link target="_blank" href="https://aka.ms/cosmos-db-ttl">
|
||||
Time to Live (TTL) in Azure Cosmos DB
|
||||
{t(Keys.controls.settings.throughput.ttlWarningLinkText)}
|
||||
</Link>
|
||||
.
|
||||
</Text>
|
||||
@@ -384,29 +386,28 @@ export const ttlWarning: JSX.Element = (
|
||||
|
||||
export const unsavedEditorWarningMessage = (editor: editorType): JSX.Element => (
|
||||
<Text styles={infoAndToolTipTextStyle}>
|
||||
You have not saved the latest changes made to your{" "}
|
||||
{t(Keys.controls.settings.throughput.unsavedEditorWarningPrefix)}{" "}
|
||||
{editor === "indexPolicy"
|
||||
? "indexing policy"
|
||||
? t(Keys.controls.settings.throughput.unsavedIndexingPolicy)
|
||||
: editor === "dataMasking"
|
||||
? "data masking policy"
|
||||
: "computed properties"}
|
||||
. Please click save to confirm the changes.
|
||||
? t(Keys.controls.settings.throughput.unsavedDataMaskingPolicy)
|
||||
: t(Keys.controls.settings.throughput.unsavedComputedProperties)}
|
||||
{t(Keys.controls.settings.throughput.unsavedEditorWarningSuffix)}
|
||||
</Text>
|
||||
);
|
||||
|
||||
export const updateThroughputDelayedApplyWarningMessage: JSX.Element = (
|
||||
<Text styles={infoAndToolTipTextStyle} id="updateThroughputDelayedApplyWarningMessage">
|
||||
You are about to request an increase in throughput beyond the pre-allocated capacity. This operation will take some
|
||||
time to complete.
|
||||
{t(Keys.controls.settings.throughput.updateDelayedApplyWarning)}
|
||||
</Text>
|
||||
);
|
||||
|
||||
export const getUpdateThroughputBeyondInstantLimitMessage = (instantMaximumThroughput: number): JSX.Element => {
|
||||
return (
|
||||
<Text id="updateThroughputDelayedApplyWarningMessage">
|
||||
Scaling up will take 4-6 hours as it exceeds what Azure Cosmos DB can instantly support currently based on your
|
||||
number of physical partitions. You can increase your throughput to {instantMaximumThroughput} instantly or proceed
|
||||
with this value and wait until the scale-up is completed.
|
||||
{t(Keys.controls.settings.throughput.scalingUpDelayMessage, {
|
||||
instantMaximumThroughput: String(instantMaximumThroughput),
|
||||
})}
|
||||
</Text>
|
||||
);
|
||||
};
|
||||
@@ -418,22 +419,26 @@ export const getUpdateThroughputBeyondSupportLimitMessage = (
|
||||
return (
|
||||
<>
|
||||
<Text styles={infoAndToolTipTextStyle} id="updateThroughputDelayedApplyWarningMessage">
|
||||
Your request to increase throughput exceeds the pre-allocated capacity which may take longer than expected.
|
||||
There are three options you can choose from to proceed:
|
||||
{t(Keys.controls.settings.throughput.exceedPreAllocatedMessage)}
|
||||
</Text>
|
||||
<ol style={{ fontSize: 14, color: "var(--colorNeutralForeground1)", marginTop: "5px" }}>
|
||||
<li>You can instantly scale up to {instantMaximumThroughput} RU/s.</li>
|
||||
<li>
|
||||
{t(Keys.controls.settings.throughput.instantScaleOption, {
|
||||
instantMaximumThroughput: String(instantMaximumThroughput),
|
||||
})}
|
||||
</li>
|
||||
{instantMaximumThroughput < maximumThroughput && (
|
||||
<li>You can asynchronously scale up to any value under {maximumThroughput} RU/s in 4-6 hours.</li>
|
||||
<li>
|
||||
{t(Keys.controls.settings.throughput.asyncScaleOption, { maximumThroughput: String(maximumThroughput) })}
|
||||
</li>
|
||||
)}
|
||||
<li>
|
||||
Your current quota max is {maximumThroughput} RU/s. To go over this limit, you must request a quota increase
|
||||
and the Azure Cosmos DB team will review.
|
||||
{t(Keys.controls.settings.throughput.quotaMaxOption, { maximumThroughput: String(maximumThroughput) })}
|
||||
<Link
|
||||
href="https://learn.microsoft.com/en-us/azure/cosmos-db/nosql/create-support-request-quota-increase"
|
||||
target="_blank"
|
||||
>
|
||||
Learn more
|
||||
{t(Keys.common.learnMore)}
|
||||
</Link>
|
||||
</li>
|
||||
</ol>
|
||||
@@ -444,23 +449,19 @@ export const getUpdateThroughputBeyondSupportLimitMessage = (
|
||||
export const getUpdateThroughputBelowMinimumMessage = (minimum: number): JSX.Element => {
|
||||
return (
|
||||
<Text styles={infoAndToolTipTextStyle}>
|
||||
You are not able to lower throughput below your current minimum of {minimum} RU/s. For more information on this
|
||||
limit, please refer to our service quote documentation.
|
||||
{t(Keys.controls.settings.throughput.belowMinimumMessage, { minimum: String(minimum) })}
|
||||
<Link
|
||||
href="https://learn.microsoft.com/en-us/azure/cosmos-db/concepts-limits#minimum-throughput-limits"
|
||||
target="_blank"
|
||||
>
|
||||
Learn more
|
||||
{t(Keys.common.learnMore)}
|
||||
</Link>
|
||||
</Text>
|
||||
);
|
||||
};
|
||||
|
||||
export const saveThroughputWarningMessage: JSX.Element = (
|
||||
<Text>
|
||||
Your bill will be affected as you update your throughput settings. Please review the updated cost estimate below
|
||||
before saving your changes
|
||||
</Text>
|
||||
<Text>{t(Keys.controls.settings.throughput.saveThroughputWarning)}</Text>
|
||||
);
|
||||
|
||||
const getCurrentThroughput = (
|
||||
@@ -472,23 +473,29 @@ const getCurrentThroughput = (
|
||||
if (targetThroughput) {
|
||||
if (throughput) {
|
||||
return isAutoscale
|
||||
? `, Current autoscale throughput: ${Math.round(
|
||||
? `, ${t(Keys.controls.settings.throughput.currentAutoscaleThroughput)} ${Math.round(
|
||||
throughput / 10,
|
||||
)} - ${throughput} ${throughputUnit}, Target autoscale throughput: ${Math.round(
|
||||
targetThroughput / 10,
|
||||
)} - ${targetThroughput} ${throughputUnit}`
|
||||
: `, Current manual throughput: ${throughput} ${throughputUnit}, Target manual throughput: ${targetThroughput}`;
|
||||
)} - ${throughput} ${throughputUnit}, ${t(
|
||||
Keys.controls.settings.throughput.targetAutoscaleThroughput,
|
||||
)} ${Math.round(targetThroughput / 10)} - ${targetThroughput} ${throughputUnit}`
|
||||
: `, ${t(Keys.controls.settings.throughput.currentManualThroughput)} ${throughput} ${throughputUnit}, ${t(
|
||||
Keys.controls.settings.throughput.targetManualThroughput,
|
||||
)} ${targetThroughput}`;
|
||||
} else {
|
||||
return isAutoscale
|
||||
? `, Target autoscale throughput: ${Math.round(targetThroughput / 10)} - ${targetThroughput} ${throughputUnit}`
|
||||
: `, Target manual throughput: ${targetThroughput} ${throughputUnit}`;
|
||||
? `, ${t(Keys.controls.settings.throughput.targetAutoscaleThroughput)} ${Math.round(
|
||||
targetThroughput / 10,
|
||||
)} - ${targetThroughput} ${throughputUnit}`
|
||||
: `, ${t(Keys.controls.settings.throughput.targetManualThroughput)} ${targetThroughput} ${throughputUnit}`;
|
||||
}
|
||||
}
|
||||
|
||||
if (!targetThroughput && throughput) {
|
||||
return isAutoscale
|
||||
? `, Current autoscale throughput: ${Math.round(throughput / 10)} - ${throughput} ${throughputUnit}`
|
||||
: `, Current manual throughput: ${throughput} ${throughputUnit}`;
|
||||
? `, ${t(Keys.controls.settings.throughput.currentAutoscaleThroughput)} ${Math.round(
|
||||
throughput / 10,
|
||||
)} - ${throughput} ${throughputUnit}`
|
||||
: `, ${t(Keys.controls.settings.throughput.currentManualThroughput)} ${throughput} ${throughputUnit}`;
|
||||
}
|
||||
|
||||
return "";
|
||||
@@ -503,10 +510,10 @@ export const getThroughputApplyDelayedMessage = (
|
||||
requestedThroughput: number,
|
||||
): JSX.Element => (
|
||||
<Text styles={infoAndToolTipTextStyle}>
|
||||
The request to increase the throughput has successfully been submitted. This operation will take 1-3 business days
|
||||
to complete. View the latest status in Notifications.
|
||||
{t(Keys.controls.settings.throughput.applyDelayedMessage)}
|
||||
<br />
|
||||
Database: {databaseName}, Container: {collectionName}{" "}
|
||||
{t(Keys.controls.settings.throughput.databaseLabel)} {databaseName},{" "}
|
||||
{t(Keys.controls.settings.throughput.containerLabel)} {collectionName}{" "}
|
||||
{getCurrentThroughput(isAutoscale, throughput, throughputUnit, requestedThroughput)}
|
||||
</Text>
|
||||
);
|
||||
@@ -519,9 +526,13 @@ export const getThroughputApplyShortDelayMessage = (
|
||||
collectionName: string,
|
||||
): JSX.Element => (
|
||||
<Text styles={infoAndToolTipTextStyle} id="throughputApplyShortDelayMessage">
|
||||
A request to increase the throughput is currently in progress. This operation will take some time to complete.
|
||||
{t(Keys.controls.settings.throughput.applyShortDelayMessage)}
|
||||
<br />
|
||||
{collectionName ? `Database: ${databaseName}, Container: ${collectionName} ` : `Database: ${databaseName} `}
|
||||
{collectionName
|
||||
? `${t(Keys.controls.settings.throughput.databaseLabel)} ${databaseName}, ${t(
|
||||
Keys.controls.settings.throughput.containerLabel,
|
||||
)} ${collectionName} `
|
||||
: `${t(Keys.controls.settings.throughput.databaseLabel)} ${databaseName} `}
|
||||
{getCurrentThroughput(isAutoscale, throughput, throughputUnit)}
|
||||
</Text>
|
||||
);
|
||||
@@ -535,10 +546,13 @@ export const getThroughputApplyLongDelayMessage = (
|
||||
requestedThroughput: number,
|
||||
): JSX.Element => (
|
||||
<Text styles={infoAndToolTipTextStyle} id="throughputApplyLongDelayMessage">
|
||||
A request to increase the throughput is currently in progress. This operation will take 1-3 business days to
|
||||
complete. View the latest status in Notifications.
|
||||
{t(Keys.controls.settings.throughput.applyLongDelayMessage)}
|
||||
<br />
|
||||
{collectionName ? `Database: ${databaseName}, Container: ${collectionName} ` : `Database: ${databaseName} `}
|
||||
{collectionName
|
||||
? `${t(Keys.controls.settings.throughput.databaseLabel)} ${databaseName}, ${t(
|
||||
Keys.controls.settings.throughput.containerLabel,
|
||||
)} ${collectionName} `
|
||||
: `${t(Keys.controls.settings.throughput.databaseLabel)} ${databaseName} `}
|
||||
{getCurrentThroughput(isAutoscale, throughput, throughputUnit, requestedThroughput)}
|
||||
</Text>
|
||||
);
|
||||
@@ -547,63 +561,49 @@ export const getToolTipContainer = (content: string | JSX.Element): JSX.Element
|
||||
content ? <Text styles={infoAndToolTipTextStyle}>{content}</Text> : undefined;
|
||||
|
||||
export const conflictResolutionLwwTooltip: JSX.Element = (
|
||||
<Text styles={infoAndToolTipTextStyle}>
|
||||
Gets or sets the name of a integer property in your documents which is used for the Last Write Wins (LWW) based
|
||||
conflict resolution scheme. By default, the system uses the system defined timestamp property, _ts to decide the
|
||||
winner for the conflicting versions of the document. Specify your own integer property if you want to override the
|
||||
default timestamp based conflict resolution.
|
||||
</Text>
|
||||
<Text styles={infoAndToolTipTextStyle}>{t(Keys.controls.settings.conflictResolution.lwwTooltip)}</Text>
|
||||
);
|
||||
|
||||
export const conflictResolutionCustomToolTip: JSX.Element = (
|
||||
<Text styles={infoAndToolTipTextStyle}>
|
||||
Gets or sets the name of a stored procedure (aka merge procedure) for resolving the conflicts. You can write
|
||||
application defined logic to determine the winner of the conflicting versions of a document. The stored procedure
|
||||
will get executed transactionally, exactly once, on the server side. If you do not provide a stored procedure, the
|
||||
conflicts will be populated in the
|
||||
{t(Keys.controls.settings.conflictResolution.customTooltip)}
|
||||
<Link className="linkDarkBackground" href="https://aka.ms/dataexplorerconflics" target="_blank">
|
||||
{` conflicts feed`}
|
||||
{t(Keys.controls.settings.conflictResolution.customTooltipConflictsFeed)}
|
||||
</Link>
|
||||
. You can update/re-register the stored procedure at any time.
|
||||
{t(Keys.controls.settings.conflictResolution.customTooltipSuffix)}
|
||||
</Text>
|
||||
);
|
||||
|
||||
export const changeFeedPolicyToolTip: JSX.Element = (
|
||||
<Text styles={infoAndToolTipTextStyle}>
|
||||
Enable change feed log retention policy to retain last 10 minutes of history for items in the container by default.
|
||||
To support this, the request unit (RU) charge for this container will be multiplied by a factor of two for writes.
|
||||
Reads are unaffected.
|
||||
</Text>
|
||||
<Text styles={infoAndToolTipTextStyle}>{t(Keys.controls.settings.changeFeed.tooltip)}</Text>
|
||||
);
|
||||
|
||||
export const mongoIndexingPolicyDisclaimer: JSX.Element = (
|
||||
<Text style={{ color: "var(--colorNeutralForeground1)" }}>
|
||||
For queries that filter on multiple properties, create multiple single field indexes instead of a compound index.
|
||||
{t(Keys.controls.settings.mongoIndexing.disclaimer)}
|
||||
<Link
|
||||
href="https://docs.microsoft.com/azure/cosmos-db/mongodb-indexing#index-types"
|
||||
target="_blank"
|
||||
style={{ color: "var(--colorBrandForeground1)" }}
|
||||
>
|
||||
{` Compound indexes `}
|
||||
{t(Keys.controls.settings.mongoIndexing.disclaimerCompoundIndexesLink)}
|
||||
</Link>
|
||||
are only used for sorting query results. If you need to add a compound index, you can create one using the Mongo
|
||||
shell.
|
||||
{t(Keys.controls.settings.mongoIndexing.disclaimerSuffix)}
|
||||
</Text>
|
||||
);
|
||||
|
||||
export const mongoCompoundIndexNotSupportedMessage: JSX.Element = (
|
||||
<Text style={{ color: "var(--colorNeutralForeground1)" }}>
|
||||
Collections with compound indexes are not yet supported in the indexing editor. To modify indexing policy for this
|
||||
collection, use the Mongo Shell.
|
||||
{t(Keys.controls.settings.mongoIndexing.compoundNotSupported)}
|
||||
</Text>
|
||||
);
|
||||
|
||||
export const mongoIndexingPolicyAADError: JSX.Element = (
|
||||
<MessageBar messageBarType={MessageBarType.error}>
|
||||
<Text>
|
||||
To use the indexing policy editor, please login to the
|
||||
{t(Keys.controls.settings.mongoIndexing.aadError)}
|
||||
<Link target="_blank" href="https://portal.azure.com">
|
||||
{"azure portal."}
|
||||
{t(Keys.controls.settings.mongoIndexing.aadErrorLink)}
|
||||
</Link>
|
||||
</Text>
|
||||
</MessageBar>
|
||||
@@ -611,7 +611,7 @@ export const mongoIndexingPolicyAADError: JSX.Element = (
|
||||
|
||||
export const mongoIndexTransformationRefreshingMessage: JSX.Element = (
|
||||
<Stack horizontal {...mongoWarningStackProps}>
|
||||
<Text styles={infoAndToolTipTextStyle}>Refreshing index transformation progress</Text>
|
||||
<Text styles={infoAndToolTipTextStyle}>{t(Keys.controls.settings.mongoIndexing.refreshingProgress)}</Text>
|
||||
<Spinner size={SpinnerSize.small} />
|
||||
</Stack>
|
||||
);
|
||||
@@ -623,15 +623,18 @@ export const renderMongoIndexTransformationRefreshMessage = (
|
||||
if (progress === 0) {
|
||||
return (
|
||||
<Text styles={infoAndToolTipTextStyle}>
|
||||
{"You can make more indexing changes once the current index transformation is complete. "}
|
||||
<Link onClick={performRefresh}>{"Refresh to check if it has completed."}</Link>
|
||||
{t(Keys.controls.settings.mongoIndexing.canMakeMoreChangesZero)}
|
||||
<Link onClick={performRefresh}>{t(Keys.controls.settings.mongoIndexing.refreshToCheck)}</Link>
|
||||
</Text>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<Text styles={infoAndToolTipTextStyle}>
|
||||
{`You can make more indexing changes once the current index transformation has completed. It is ${progress}% complete. `}
|
||||
<Link onClick={performRefresh}>{"Refresh to check the progress."}</Link>
|
||||
{`${t(Keys.controls.settings.mongoIndexing.canMakeMoreChangesProgress).replace(
|
||||
"{{progress}}",
|
||||
String(progress),
|
||||
)} `}
|
||||
<Link onClick={performRefresh}>{t(Keys.controls.settings.mongoIndexing.refreshToCheckProgress)}</Link>
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import { titleAndInputStackProps, unsavedEditorWarningMessage } from "Explorer/C
|
||||
import { isDirty } from "Explorer/Controls/Settings/SettingsUtils";
|
||||
import { loadMonaco } from "Explorer/LazyMonaco";
|
||||
import { monacoTheme, useThemeStore } from "hooks/useTheme";
|
||||
import { Keys, t } from "Localization";
|
||||
import * as monaco from "monaco-editor";
|
||||
import * as React from "react";
|
||||
export interface ComputedPropertiesComponentProps {
|
||||
@@ -107,7 +108,7 @@ export class ComputedPropertiesComponent extends React.Component<
|
||||
this.computedPropertiesEditor = monaco.editor.create(this.computedPropertiesDiv.current, {
|
||||
value: value,
|
||||
language: "json",
|
||||
ariaLabel: "Computed properties",
|
||||
ariaLabel: t(Keys.controls.settings.computedProperties.ariaLabel),
|
||||
theme: monacoTheme(),
|
||||
});
|
||||
if (this.computedPropertiesEditor) {
|
||||
@@ -151,9 +152,9 @@ export class ComputedPropertiesComponent extends React.Component<
|
||||
)}
|
||||
<Text style={{ marginLeft: "30px", marginBottom: "10px", color: "var(--colorNeutralForeground1)" }}>
|
||||
<Link target="_blank" href="https://aka.ms/computed-properties-preview/">
|
||||
{"Learn more"} <FontIcon iconName="NavigateExternalInline" />
|
||||
{t(Keys.common.learnMore)} <FontIcon iconName="NavigateExternalInline" />
|
||||
</Link>
|
||||
  about how to define computed properties and how to use them.
|
||||
  {t(Keys.controls.settings.computedProperties.learnMorePrefix)}
|
||||
</Text>
|
||||
<div
|
||||
className="settingsV2Editor"
|
||||
|
||||
@@ -2,6 +2,7 @@ import { ChoiceGroup, IChoiceGroupOption, ITextFieldProps, Stack, TextField } fr
|
||||
import * as React from "react";
|
||||
import * as DataModels from "../../../../Contracts/DataModels";
|
||||
import * as ViewModels from "../../../../Contracts/ViewModels";
|
||||
import { Keys, t } from "Localization";
|
||||
import {
|
||||
conflictResolutionCustomToolTip,
|
||||
conflictResolutionLwwTooltip,
|
||||
@@ -32,9 +33,12 @@ export class ConflictResolutionComponent extends React.Component<ConflictResolut
|
||||
private conflictResolutionChoiceGroupOptions: IChoiceGroupOption[] = [
|
||||
{
|
||||
key: DataModels.ConflictResolutionMode.LastWriterWins,
|
||||
text: "Last Write Wins (default)",
|
||||
text: t(Keys.controls.settings.conflictResolution.lwwDefault),
|
||||
},
|
||||
{
|
||||
key: DataModels.ConflictResolutionMode.Custom,
|
||||
text: t(Keys.controls.settings.conflictResolution.customMergeProcedure),
|
||||
},
|
||||
{ key: DataModels.ConflictResolutionMode.Custom, text: "Merge Procedure (custom)" },
|
||||
];
|
||||
|
||||
componentDidMount(): void {
|
||||
@@ -85,7 +89,7 @@ export class ConflictResolutionComponent extends React.Component<ConflictResolut
|
||||
|
||||
private getConflictResolutionModeComponent = (): JSX.Element => (
|
||||
<ChoiceGroup
|
||||
label="Mode"
|
||||
label={t(Keys.controls.settings.conflictResolution.mode)}
|
||||
selectedKey={this.props.conflictResolutionPolicyMode}
|
||||
options={this.conflictResolutionChoiceGroupOptions}
|
||||
onChange={this.onConflictResolutionPolicyModeChange}
|
||||
@@ -103,7 +107,7 @@ export class ConflictResolutionComponent extends React.Component<ConflictResolut
|
||||
private getConflictResolutionLWWComponent = (): JSX.Element => (
|
||||
<TextField
|
||||
id="conflictResolutionLwwTextField"
|
||||
label={"Conflict Resolver Property"}
|
||||
label={t(Keys.controls.settings.conflictResolution.conflictResolverProperty)}
|
||||
onRenderLabel={this.onRenderLwwComponentTextField}
|
||||
styles={{
|
||||
fieldGroup: {
|
||||
@@ -158,7 +162,7 @@ export class ConflictResolutionComponent extends React.Component<ConflictResolut
|
||||
return (
|
||||
<TextField
|
||||
id="conflictResolutionCustomTextField"
|
||||
label="Stored procedure"
|
||||
label={t(Keys.controls.settings.conflictResolution.storedProcedure)}
|
||||
onRenderLabel={this.onRenderCustomComponentTextField}
|
||||
styles={{
|
||||
fieldGroup: {
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
import { titleAndInputStackProps } from "Explorer/Controls/Settings/SettingsRenderUtils";
|
||||
import { ContainerPolicyTabTypes, isDirty } from "Explorer/Controls/Settings/SettingsUtils";
|
||||
import { VectorEmbeddingPoliciesComponent } from "Explorer/Controls/VectorSearch/VectorEmbeddingPoliciesComponent";
|
||||
import { Keys, t } from "Localization";
|
||||
import React from "react";
|
||||
|
||||
export interface ContainerPolicyComponentProps {
|
||||
@@ -153,7 +154,7 @@ export const ContainerPolicyComponent: React.FC<ContainerPolicyComponentProps> =
|
||||
<PivotItem
|
||||
itemKey={ContainerPolicyTabTypes[ContainerPolicyTabTypes.VectorPolicyTab]}
|
||||
style={{ marginTop: 20, color: "var(--colorNeutralForeground1)" }}
|
||||
headerText="Vector Policy"
|
||||
headerText={t(Keys.controls.settings.containerPolicy.vectorPolicy)}
|
||||
>
|
||||
<Stack {...titleAndInputStackProps} styles={{ root: { position: "relative", maxWidth: "400px" } }}>
|
||||
{vectorEmbeddings && (
|
||||
@@ -175,7 +176,7 @@ export const ContainerPolicyComponent: React.FC<ContainerPolicyComponentProps> =
|
||||
<PivotItem
|
||||
itemKey={ContainerPolicyTabTypes[ContainerPolicyTabTypes.FullTextPolicyTab]}
|
||||
style={{ marginTop: 20, color: "var(--colorNeutralForeground1)" }}
|
||||
headerText="Full Text Policy"
|
||||
headerText={t(Keys.controls.settings.containerPolicy.fullTextPolicy)}
|
||||
>
|
||||
<Stack {...titleAndInputStackProps} styles={{ root: { position: "relative", maxWidth: "400px" } }}>
|
||||
{fullTextSearchPolicy ? (
|
||||
@@ -218,7 +219,7 @@ export const ContainerPolicyComponent: React.FC<ContainerPolicyComponentProps> =
|
||||
});
|
||||
}}
|
||||
>
|
||||
Create new full text search policy
|
||||
{t(Keys.controls.settings.containerPolicy.createFullTextPolicy)}
|
||||
</DefaultButton>
|
||||
)}
|
||||
</Stack>
|
||||
|
||||
@@ -2,6 +2,7 @@ import { MessageBar, MessageBarType, Stack } from "@fluentui/react";
|
||||
import * as monaco from "monaco-editor";
|
||||
import * as React from "react";
|
||||
import * as DataModels from "../../../../Contracts/DataModels";
|
||||
import { Keys, t } from "Localization";
|
||||
import { loadMonaco } from "../../../LazyMonaco";
|
||||
import { titleAndInputStackProps, unsavedEditorWarningMessage } from "../SettingsRenderUtils";
|
||||
import { isDirty as isContentDirty, isDataMaskingEnabled } from "../SettingsUtils";
|
||||
@@ -89,7 +90,7 @@ export class DataMaskingComponent extends React.Component<DataMaskingComponentPr
|
||||
value: value,
|
||||
language: "json",
|
||||
automaticLayout: true,
|
||||
ariaLabel: "Data Masking Policy",
|
||||
ariaLabel: t(Keys.controls.settings.dataMasking.ariaLabel),
|
||||
fontSize: 13,
|
||||
minimap: { enabled: false },
|
||||
wordWrap: "off",
|
||||
@@ -142,7 +143,7 @@ export class DataMaskingComponent extends React.Component<DataMaskingComponentPr
|
||||
)}
|
||||
{this.props.validationErrors.length > 0 && (
|
||||
<MessageBar messageBarType={MessageBarType.error}>
|
||||
Validation failed: {this.props.validationErrors.join(", ")}
|
||||
{t(Keys.controls.settings.dataMasking.validationFailed)} {this.props.validationErrors.join(", ")}
|
||||
</MessageBar>
|
||||
)}
|
||||
<div className="settingsV2Editor" tabIndex={0} ref={this.dataMaskingDiv}></div>
|
||||
|
||||
@@ -2,6 +2,7 @@ import { FontIcon, Link, Stack, Text } from "@fluentui/react";
|
||||
import Explorer from "Explorer/Explorer";
|
||||
import React from "react";
|
||||
import * as ViewModels from "../../../../Contracts/ViewModels";
|
||||
import { Keys, t } from "Localization";
|
||||
import { GlobalSecondaryIndexSourceComponent } from "./GlobalSecondaryIndexSourceComponent";
|
||||
import { GlobalSecondaryIndexTargetComponent } from "./GlobalSecondaryIndexTargetComponent";
|
||||
|
||||
@@ -21,7 +22,9 @@ export const GlobalSecondaryIndexComponent: React.FC<GlobalSecondaryIndexCompone
|
||||
<Stack tokens={{ childrenGap: 8 }} styles={{ root: { maxWidth: 600 } }}>
|
||||
<Stack horizontal verticalAlign="center" wrap tokens={{ childrenGap: 8 }}>
|
||||
{isSourceContainer && (
|
||||
<Text styles={{ root: { fontWeight: 600 } }}>This container has the following indexes defined for it.</Text>
|
||||
<Text styles={{ root: { fontWeight: 600 } }}>
|
||||
{t(Keys.controls.settings.globalSecondaryIndex.indexesDefined)}
|
||||
</Text>
|
||||
)}
|
||||
<Text>
|
||||
<Link
|
||||
@@ -31,7 +34,7 @@ export const GlobalSecondaryIndexComponent: React.FC<GlobalSecondaryIndexCompone
|
||||
Learn more
|
||||
<FontIcon iconName="NavigateExternalInline" style={{ marginLeft: "4px" }} />
|
||||
</Link>{" "}
|
||||
about how to define global secondary indexes and how to use them.
|
||||
{t(Keys.controls.settings.globalSecondaryIndex.learnMoreSuffix)}
|
||||
</Text>
|
||||
</Stack>
|
||||
{isSourceContainer && <GlobalSecondaryIndexSourceComponent collection={collection} explorer={explorer} />}
|
||||
|
||||
@@ -9,6 +9,7 @@ import { useSidePanel } from "hooks/useSidePanel";
|
||||
import * as monaco from "monaco-editor";
|
||||
import React, { useEffect, useRef } from "react";
|
||||
import * as ViewModels from "../../../../Contracts/ViewModels";
|
||||
import { Keys, t } from "Localization";
|
||||
|
||||
export interface GlobalSecondaryIndexSourceComponentProps {
|
||||
collection: ViewModels.Collection;
|
||||
@@ -67,7 +68,7 @@ export const GlobalSecondaryIndexSourceComponent: React.FC<GlobalSecondaryIndexS
|
||||
editorRef.current = monacoInstance.editor.create(editorContainerRef.current, {
|
||||
value: jsonValue,
|
||||
language: "json",
|
||||
ariaLabel: "Global Secondary Index JSON",
|
||||
ariaLabel: t(Keys.controls.settings.globalSecondaryIndex.jsonAriaLabel),
|
||||
readOnly: true,
|
||||
});
|
||||
};
|
||||
@@ -98,7 +99,7 @@ export const GlobalSecondaryIndexSourceComponent: React.FC<GlobalSecondaryIndexS
|
||||
}}
|
||||
/>
|
||||
<PrimaryButton
|
||||
text="Add index"
|
||||
text={t(Keys.controls.settings.globalSecondaryIndex.addIndex)}
|
||||
styles={{ root: { width: "fit-content", marginTop: 12 } }}
|
||||
onClick={() =>
|
||||
useSidePanel
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Stack, Text } from "@fluentui/react";
|
||||
import * as React from "react";
|
||||
import * as ViewModels from "../../../../Contracts/ViewModels";
|
||||
import { Keys, t } from "Localization";
|
||||
|
||||
export interface GlobalSecondaryIndexTargetComponentProps {
|
||||
collection: ViewModels.Collection;
|
||||
@@ -25,17 +26,21 @@ export const GlobalSecondaryIndexTargetComponent: React.FC<GlobalSecondaryIndexT
|
||||
|
||||
return (
|
||||
<Stack tokens={{ childrenGap: 15 }} styles={{ root: { maxWidth: 600 } }}>
|
||||
<Text styles={textHeadingStyle}>Global Secondary Index Settings</Text>
|
||||
<Text styles={textHeadingStyle}>{t(Keys.controls.settings.globalSecondaryIndex.settingsTitle)}</Text>
|
||||
|
||||
<Stack tokens={{ childrenGap: 5 }}>
|
||||
<Text styles={{ root: { fontWeight: "600" } }}>Source container</Text>
|
||||
<Text styles={{ root: { fontWeight: "600" } }}>
|
||||
{t(Keys.controls.settings.globalSecondaryIndex.sourceContainer)}
|
||||
</Text>
|
||||
<Stack styles={valueBoxStyle}>
|
||||
<Text>{globalSecondaryIndexDefinition?.sourceCollectionId}</Text>
|
||||
</Stack>
|
||||
</Stack>
|
||||
|
||||
<Stack tokens={{ childrenGap: 5 }}>
|
||||
<Text styles={{ root: { fontWeight: "600" } }}>Global secondary index definition</Text>
|
||||
<Text styles={{ root: { fontWeight: "600" } }}>
|
||||
{t(Keys.controls.settings.globalSecondaryIndex.indexDefinition)}
|
||||
</Text>
|
||||
<Stack styles={valueBoxStyle}>
|
||||
<Text>{globalSecondaryIndexDefinition?.definition}</Text>
|
||||
</Stack>
|
||||
|
||||
@@ -3,6 +3,7 @@ import { monacoTheme, useThemeStore } from "hooks/useTheme";
|
||||
import * as monaco from "monaco-editor";
|
||||
import * as React from "react";
|
||||
import * as DataModels from "../../../../Contracts/DataModels";
|
||||
import { Keys, t } from "Localization";
|
||||
import { loadMonaco } from "../../../LazyMonaco";
|
||||
import { titleAndInputStackProps, unsavedEditorWarningMessage } from "../SettingsRenderUtils";
|
||||
import { isDirty, isIndexTransforming } from "../SettingsUtils";
|
||||
@@ -119,7 +120,7 @@ export class IndexingPolicyComponent extends React.Component<
|
||||
value: value,
|
||||
language: "json",
|
||||
readOnly: isIndexTransforming(this.props.indexTransformationProgress),
|
||||
ariaLabel: "Indexing Policy",
|
||||
ariaLabel: t(Keys.controls.settings.indexingPolicy.ariaLabel),
|
||||
theme: monacoTheme(),
|
||||
});
|
||||
if (this.indexingPolicyEditor) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { MessageBar, MessageBarType } from "@fluentui/react";
|
||||
import * as React from "react";
|
||||
import { handleError } from "../../../../../Common/ErrorHandlingUtils";
|
||||
import { Keys, t } from "Localization";
|
||||
import {
|
||||
mongoIndexTransformationRefreshingMessage,
|
||||
renderMongoIndexTransformationRefreshMessage,
|
||||
@@ -46,7 +47,11 @@ export class IndexingPolicyRefreshComponent extends React.Component<
|
||||
try {
|
||||
await this.props.refreshIndexTransformationProgress();
|
||||
} catch (error) {
|
||||
handleError(error, "RefreshIndexTransformationProgress", "Refreshing index transformation progress failed");
|
||||
handleError(
|
||||
error,
|
||||
"RefreshIndexTransformationProgress",
|
||||
t(Keys.controls.settings.indexingPolicyRefresh.refreshFailed),
|
||||
);
|
||||
} finally {
|
||||
this.setState({ isRefreshing: false });
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ exports[`IndexingPolicyRefreshComponent renders 1`] = `
|
||||
}
|
||||
}
|
||||
>
|
||||
You can make more indexing changes once the current index transformation has completed. It is 90% complete.
|
||||
You can make more indexing changes once the current index transformation has completed. It is 90% complete.
|
||||
<StyledLinkBase
|
||||
onClick={[Function]}
|
||||
>
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
IDropdownOption,
|
||||
ITextField,
|
||||
} from "@fluentui/react";
|
||||
import { Keys, t } from "Localization";
|
||||
import {
|
||||
addMongoIndexSubElementsTokens,
|
||||
mongoErrorMessageStyles,
|
||||
@@ -66,7 +67,7 @@ export class AddMongoIndexComponent extends React.Component<AddMongoIndexCompone
|
||||
<Stack {...mongoWarningStackProps}>
|
||||
<Stack horizontal tokens={addMongoIndexSubElementsTokens}>
|
||||
<TextField
|
||||
ariaLabel={"Index Field Name " + this.props.position}
|
||||
ariaLabel={t(Keys.controls.settings.mongoIndexing.indexFieldName) + " " + this.props.position}
|
||||
disabled={this.props.disabled}
|
||||
styles={shortWidthTextFieldStyles}
|
||||
componentRef={this.setRef}
|
||||
@@ -76,17 +77,17 @@ export class AddMongoIndexComponent extends React.Component<AddMongoIndexCompone
|
||||
/>
|
||||
|
||||
<Dropdown
|
||||
ariaLabel={"Index Type " + this.props.position}
|
||||
ariaLabel={t(Keys.controls.settings.mongoIndexing.indexType) + " " + this.props.position}
|
||||
disabled={this.props.disabled}
|
||||
styles={shortWidthDropDownStyles}
|
||||
placeholder="Select an index type"
|
||||
placeholder={t(Keys.controls.settings.mongoIndexing.selectIndexType)}
|
||||
selectedKey={this.props.type}
|
||||
options={this.indexTypes}
|
||||
onChange={this.onTypeChange}
|
||||
/>
|
||||
|
||||
<IconButton
|
||||
ariaLabel={"Undo Button " + this.props.position}
|
||||
ariaLabel={t(Keys.controls.settings.mongoIndexing.undoButton) + " " + this.props.position}
|
||||
iconProps={{ iconName: "Undo" }}
|
||||
disabled={!this.props.description && !this.props.type}
|
||||
onClick={() => this.props.onDiscard()}
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
} from "@fluentui/react";
|
||||
import * as React from "react";
|
||||
import { MongoIndex } from "../../../../../Utils/arm/generatedClients/cosmos/types";
|
||||
import { Keys, t } from "Localization";
|
||||
import { CollapsibleSectionComponent } from "../../../CollapsiblePanel/CollapsibleSectionComponent";
|
||||
import {
|
||||
addMongoIndexStackProps,
|
||||
@@ -83,11 +84,25 @@ export class MongoIndexingPolicyComponent extends React.Component<MongoIndexingP
|
||||
};
|
||||
|
||||
private initialIndexesColumns: IColumn[] = [
|
||||
{ key: "definition", name: "Definition", fieldName: "definition", minWidth: 100, maxWidth: 200, isResizable: true },
|
||||
{ key: "type", name: "Type", fieldName: "type", minWidth: 100, maxWidth: 200, isResizable: true },
|
||||
{
|
||||
key: "definition",
|
||||
name: t(Keys.controls.settings.mongoIndexing.definitionColumn),
|
||||
fieldName: "definition",
|
||||
minWidth: 100,
|
||||
maxWidth: 200,
|
||||
isResizable: true,
|
||||
},
|
||||
{
|
||||
key: "type",
|
||||
name: t(Keys.controls.settings.mongoIndexing.typeColumn),
|
||||
fieldName: "type",
|
||||
minWidth: 100,
|
||||
maxWidth: 200,
|
||||
isResizable: true,
|
||||
},
|
||||
{
|
||||
key: "actionButton",
|
||||
name: "Drop Index",
|
||||
name: t(Keys.controls.settings.mongoIndexing.dropIndexColumn),
|
||||
fieldName: "actionButton",
|
||||
minWidth: 100,
|
||||
maxWidth: 200,
|
||||
@@ -96,11 +111,25 @@ export class MongoIndexingPolicyComponent extends React.Component<MongoIndexingP
|
||||
];
|
||||
|
||||
private indexesToBeDroppedColumns: IColumn[] = [
|
||||
{ key: "definition", name: "Definition", fieldName: "definition", minWidth: 100, maxWidth: 200, isResizable: true },
|
||||
{ key: "type", name: "Type", fieldName: "type", minWidth: 100, maxWidth: 200, isResizable: true },
|
||||
{
|
||||
key: "definition",
|
||||
name: t(Keys.controls.settings.mongoIndexing.definitionColumn),
|
||||
fieldName: "definition",
|
||||
minWidth: 100,
|
||||
maxWidth: 200,
|
||||
isResizable: true,
|
||||
},
|
||||
{
|
||||
key: "type",
|
||||
name: t(Keys.controls.settings.mongoIndexing.typeColumn),
|
||||
fieldName: "type",
|
||||
minWidth: 100,
|
||||
maxWidth: 200,
|
||||
isResizable: true,
|
||||
},
|
||||
{
|
||||
key: "actionButton",
|
||||
name: "Add index back",
|
||||
name: t(Keys.controls.settings.mongoIndexing.addIndexBackColumn),
|
||||
fieldName: "actionButton",
|
||||
minWidth: 100,
|
||||
maxWidth: 200,
|
||||
@@ -161,7 +190,7 @@ export class MongoIndexingPolicyComponent extends React.Component<MongoIndexingP
|
||||
private getActionButton = (arrayPosition: number, isCurrentIndex: boolean): JSX.Element => {
|
||||
return isCurrentIndex ? (
|
||||
<IconButton
|
||||
ariaLabel="Delete index Button"
|
||||
ariaLabel={t(Keys.controls.settings.mongoIndexing.deleteIndexButton)}
|
||||
iconProps={{ iconName: "Delete" }}
|
||||
disabled={isIndexTransforming(this.props.indexTransformationProgress)}
|
||||
onClick={() => {
|
||||
@@ -170,7 +199,7 @@ export class MongoIndexingPolicyComponent extends React.Component<MongoIndexingP
|
||||
/>
|
||||
) : (
|
||||
<IconButton
|
||||
ariaLabel="Add back Index Button"
|
||||
ariaLabel={t(Keys.controls.settings.mongoIndexing.addBackIndexButton)}
|
||||
iconProps={{ iconName: "Add" }}
|
||||
onClick={() => {
|
||||
this.props.onRevertIndexDrop(arrayPosition);
|
||||
@@ -258,7 +287,10 @@ export class MongoIndexingPolicyComponent extends React.Component<MongoIndexingP
|
||||
|
||||
return (
|
||||
<Stack {...createAndAddMongoIndexStackProps} styles={mediumWidthStackStyles}>
|
||||
<CollapsibleSectionComponent title="Current index(es)" isExpandedByDefault={true}>
|
||||
<CollapsibleSectionComponent
|
||||
title={t(Keys.controls.settings.mongoIndexing.currentIndexes)}
|
||||
isExpandedByDefault={true}
|
||||
>
|
||||
{
|
||||
<>
|
||||
<DetailsList
|
||||
@@ -285,7 +317,10 @@ export class MongoIndexingPolicyComponent extends React.Component<MongoIndexingP
|
||||
|
||||
return (
|
||||
<Stack styles={mediumWidthStackStyles}>
|
||||
<CollapsibleSectionComponent title="Index(es) to be dropped" isExpandedByDefault={true}>
|
||||
<CollapsibleSectionComponent
|
||||
title={t(Keys.controls.settings.mongoIndexing.indexesToBeDropped)}
|
||||
isExpandedByDefault={true}
|
||||
>
|
||||
{indexesToBeDropped.length > 0 && (
|
||||
<DetailsList
|
||||
styles={customDetailsListStyles}
|
||||
|
||||
@@ -18,6 +18,7 @@ import { cancelDataTransferJob, pollDataTransferJob } from "Common/dataAccess/da
|
||||
import { Platform, configContext } from "ConfigContext";
|
||||
import Explorer from "Explorer/Explorer";
|
||||
import { ChangePartitionKeyPane } from "Explorer/Panes/ChangePartitionKeyPane/ChangePartitionKeyPane";
|
||||
import { Keys, t } from "Localization";
|
||||
import {
|
||||
CosmosSqlDataTransferDataSourceSink,
|
||||
DataTransferJobGetResults,
|
||||
@@ -80,7 +81,7 @@ export const PartitionKeyComponent: React.FC<PartitionKeyComponentProps> = ({
|
||||
return (collection.partitionKeyProperties || []).map((property) => "/" + property).join(", ");
|
||||
};
|
||||
|
||||
const partitionKeyName = "Partition key";
|
||||
const partitionKeyName = t(Keys.controls.settings.partitionKey.partitionKey);
|
||||
const partitionKeyValue = getPartitionKeyValue();
|
||||
|
||||
const textHeadingStyle = {
|
||||
@@ -148,22 +149,28 @@ export const PartitionKeyComponent: React.FC<PartitionKeyComponentProps> = ({
|
||||
const getProgressDescription = (): string => {
|
||||
const processedCount = portalDataTransferJob?.properties?.processedCount;
|
||||
const totalCount = portalDataTransferJob?.properties?.totalCount;
|
||||
const processedCountString = totalCount > 0 ? `(${processedCount} of ${totalCount} documents processed)` : "";
|
||||
const processedCountString =
|
||||
totalCount > 0
|
||||
? t(Keys.controls.settings.partitionKeyEditor.documentsProcessed, {
|
||||
processedCount: String(processedCount),
|
||||
totalCount: String(totalCount),
|
||||
})
|
||||
: "";
|
||||
return `${portalDataTransferJob?.properties?.status} ${processedCountString}`;
|
||||
};
|
||||
|
||||
const startPartitionkeyChangeWorkflow = () => {
|
||||
useSidePanel
|
||||
.getState()
|
||||
.openSidePanel(
|
||||
"Change partition key",
|
||||
<ChangePartitionKeyPane
|
||||
sourceDatabase={database}
|
||||
sourceCollection={collection}
|
||||
explorer={explorer}
|
||||
onClose={refreshDataTransferOperations}
|
||||
/>,
|
||||
);
|
||||
useSidePanel.getState().openSidePanel(
|
||||
t(Keys.controls.settings.partitionKeyEditor.changePartitionKey, {
|
||||
partitionKeyName: t(Keys.controls.settings.partitionKey.partitionKey).toLowerCase(),
|
||||
}),
|
||||
<ChangePartitionKeyPane
|
||||
sourceDatabase={database}
|
||||
sourceCollection={collection}
|
||||
explorer={explorer}
|
||||
onClose={refreshDataTransferOperations}
|
||||
/>,
|
||||
);
|
||||
};
|
||||
|
||||
const getPercentageComplete = () => {
|
||||
@@ -181,16 +188,28 @@ export const PartitionKeyComponent: React.FC<PartitionKeyComponentProps> = ({
|
||||
return (
|
||||
<Stack tokens={{ childrenGap: 20 }} styles={{ root: { maxWidth: 600 } }}>
|
||||
<Stack tokens={{ childrenGap: 10 }}>
|
||||
{!isReadOnly && <Text styles={textHeadingStyle}>Change {partitionKeyName.toLowerCase()}</Text>}
|
||||
{!isReadOnly && (
|
||||
<Text styles={textHeadingStyle}>
|
||||
{t(Keys.controls.settings.partitionKeyEditor.changePartitionKey, {
|
||||
partitionKeyName: partitionKeyName.toLowerCase(),
|
||||
})}
|
||||
</Text>
|
||||
)}
|
||||
<Stack horizontal tokens={{ childrenGap: 20 }}>
|
||||
<Stack tokens={{ childrenGap: 5 }}>
|
||||
<Text styles={textSubHeadingStyle}>Current {partitionKeyName.toLowerCase()}</Text>
|
||||
<Text styles={textSubHeadingStyle}>Partitioning</Text>
|
||||
<Text styles={textSubHeadingStyle}>
|
||||
{t(Keys.controls.settings.partitionKeyEditor.currentPartitionKey, {
|
||||
partitionKeyName: partitionKeyName.toLowerCase(),
|
||||
})}
|
||||
</Text>
|
||||
<Text styles={textSubHeadingStyle}>{t(Keys.controls.settings.partitionKeyEditor.partitioning)}</Text>
|
||||
</Stack>
|
||||
<Stack tokens={{ childrenGap: 5 }} data-test="partition-key-values">
|
||||
<Text styles={textSubHeadingStyle1}>{partitionKeyValue}</Text>
|
||||
<Text styles={textSubHeadingStyle1}>
|
||||
{isHierarchicalPartitionedContainer() ? "Hierarchical" : "Non-hierarchical"}
|
||||
{isHierarchicalPartitionedContainer()
|
||||
? t(Keys.controls.settings.partitionKeyEditor.hierarchical)
|
||||
: t(Keys.controls.settings.partitionKeyEditor.nonHierarchical)}
|
||||
</Text>
|
||||
</Stack>
|
||||
</Stack>
|
||||
@@ -204,33 +223,33 @@ export const PartitionKeyComponent: React.FC<PartitionKeyComponentProps> = ({
|
||||
messageBarIconProps={{ iconName: "WarningSolid", className: "messageBarWarningIcon" }}
|
||||
styles={darkThemeMessageBarStyles}
|
||||
>
|
||||
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.
|
||||
{t(Keys.controls.settings.partitionKeyEditor.safeguardWarning)}
|
||||
<Link
|
||||
href="https://learn.microsoft.com/azure/cosmos-db/container-copy#how-does-container-copy-work"
|
||||
target="_blank"
|
||||
underline
|
||||
style={{ color: "var(--colorBrandForeground1)" }}
|
||||
>
|
||||
Learn more
|
||||
{t(Keys.common.learnMore)}
|
||||
</Link>
|
||||
</MessageBar>
|
||||
<Text styles={{ root: { color: "var(--colorNeutralForeground1)" } }}>
|
||||
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.
|
||||
{t(Keys.controls.settings.partitionKeyEditor.changeDescription)}
|
||||
</Text>
|
||||
{configContext.platform !== Platform.Emulator && (
|
||||
<PrimaryButton
|
||||
data-test="change-partition-key-button"
|
||||
styles={{ root: { width: "fit-content" } }}
|
||||
text="Change"
|
||||
text={t(Keys.controls.settings.partitionKeyEditor.changeButton)}
|
||||
onClick={startPartitionkeyChangeWorkflow}
|
||||
disabled={isCurrentJobInProgress(portalDataTransferJob)}
|
||||
/>
|
||||
)}
|
||||
{portalDataTransferJob && (
|
||||
<Stack>
|
||||
<Text styles={textHeadingStyle}>{partitionKeyName} change job</Text>
|
||||
<Text styles={textHeadingStyle}>
|
||||
{t(Keys.controls.settings.partitionKeyEditor.changeJob, { partitionKeyName })}
|
||||
</Text>
|
||||
<Stack
|
||||
horizontal
|
||||
tokens={{ childrenGap: 20 }}
|
||||
@@ -251,7 +270,10 @@ export const PartitionKeyComponent: React.FC<PartitionKeyComponentProps> = ({
|
||||
}}
|
||||
></ProgressIndicator>
|
||||
{isCurrentJobInProgress(portalDataTransferJob) && (
|
||||
<DefaultButton text="Cancel" onClick={() => cancelRunningDataTransferJob(portalDataTransferJob)} />
|
||||
<DefaultButton
|
||||
text={t(Keys.controls.settings.partitionKeyEditor.cancelButton)}
|
||||
onClick={() => cancelRunningDataTransferJob(portalDataTransferJob)}
|
||||
/>
|
||||
)}
|
||||
</Stack>
|
||||
</Stack>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Link, MessageBar, MessageBarType, Stack, Text, TextField } from "@fluentui/react";
|
||||
import { Keys, t } from "Localization";
|
||||
import * as React from "react";
|
||||
import * as Constants from "../../../../Common/Constants";
|
||||
import { Platform, configContext } from "../../../../ConfigContext";
|
||||
@@ -92,8 +93,10 @@ export class ScaleComponent extends React.Component<ScaleComponentProps> {
|
||||
}
|
||||
|
||||
const minThroughput: string = this.getMinRUs().toLocaleString();
|
||||
const maxThroughput: string = !this.props.isFixedContainer ? "unlimited" : this.getMaxRUs().toLocaleString();
|
||||
return `Throughput (${minThroughput} - ${maxThroughput} RU/s)`;
|
||||
const maxThroughput: string = !this.props.isFixedContainer
|
||||
? t(Keys.controls.settings.scale.unlimited)
|
||||
: this.getMaxRUs().toLocaleString();
|
||||
return t(Keys.controls.settings.scale.throughputRangeLabel, { min: minThroughput, max: maxThroughput });
|
||||
};
|
||||
|
||||
public canThroughputExceedMaximumValue = (): boolean => {
|
||||
@@ -156,14 +159,12 @@ export class ScaleComponent extends React.Component<ScaleComponentProps> {
|
||||
const freeTierLimits = SharedConstants.FreeTierLimits;
|
||||
return (
|
||||
<Text>
|
||||
With free tier, you will get the first {freeTierLimits.RU} RU/s and {freeTierLimits.Storage} GB of storage in
|
||||
this account for free. To keep your account free, keep the total RU/s across all resources in the account to{" "}
|
||||
{freeTierLimits.RU} RU/s.
|
||||
{t(Keys.controls.settings.scale.freeTierInfo, { ru: freeTierLimits.RU, storage: freeTierLimits.Storage })}
|
||||
<Link
|
||||
href="https://docs.microsoft.com/en-us/azure/cosmos-db/understand-your-bill#billing-examples-with-free-tier-accounts"
|
||||
target="_blank"
|
||||
>
|
||||
Learn more.
|
||||
{t(Keys.controls.settings.scale.freeTierLearnMore)}
|
||||
</Link>
|
||||
</Text>
|
||||
);
|
||||
@@ -188,12 +189,9 @@ export class ScaleComponent extends React.Component<ScaleComponentProps> {
|
||||
{/* TODO: Replace link with call to the Azure Support blade */}
|
||||
{this.isAutoScaleEnabled() && (
|
||||
<Stack {...titleAndInputStackProps}>
|
||||
<Text>Throughput (RU/s)</Text>
|
||||
<Text>{t(Keys.controls.settings.scale.throughputRuS)}</Text>
|
||||
<TextField disabled styles={getTextFieldStyles(undefined, undefined)} />
|
||||
<Text>
|
||||
Your account has custom settings that prevents setting throughput at the container level. Please work with
|
||||
your Cosmos DB engineering team point of contact to make changes.
|
||||
</Text>
|
||||
<Text>{t(Keys.controls.settings.scale.autoScaleCustomSettings)}</Text>
|
||||
</Stack>
|
||||
)}
|
||||
</Stack>
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
} from "@fluentui/react";
|
||||
import * as React from "react";
|
||||
import * as ViewModels from "../../../../Contracts/ViewModels";
|
||||
import { Keys, t } from "Localization";
|
||||
import { userContext } from "../../../../UserContext";
|
||||
import { Int32 } from "../../../Panes/Tables/Validators/EntityPropertyValidationCommon";
|
||||
import {
|
||||
@@ -85,9 +86,12 @@ export class SubSettingsComponent extends React.Component<SubSettingsComponentPr
|
||||
constructor(props: SubSettingsComponentProps) {
|
||||
super(props);
|
||||
this.geospatialVisible = userContext.apiType === "SQL";
|
||||
this.partitionKeyName = userContext.apiType === "Mongo" ? "Shard key" : "Partition key";
|
||||
this.partitionKeyName =
|
||||
userContext.apiType === "Mongo"
|
||||
? t(Keys.controls.settings.partitionKey.shardKey)
|
||||
: t(Keys.controls.settings.partitionKey.partitionKey);
|
||||
this.partitionKeyValue = this.getPartitionKeyValue();
|
||||
this.uniqueKeyName = "Unique keys";
|
||||
this.uniqueKeyName = t(Keys.controls.settings.subSettings.uniqueKeys);
|
||||
this.uniqueKeyValue = this.getUniqueKeyValue();
|
||||
}
|
||||
|
||||
@@ -143,9 +147,13 @@ export class SubSettingsComponent extends React.Component<SubSettingsComponentPr
|
||||
};
|
||||
|
||||
private ttlChoiceGroupOptions: IChoiceGroupOption[] = [
|
||||
{ key: TtlType.Off, text: "Off", ariaLabel: "ttl-off-option" },
|
||||
{ key: TtlType.OnNoDefault, text: "On (no default)", ariaLabel: "ttl-on-no-default-option" },
|
||||
{ key: TtlType.On, text: "On", ariaLabel: "ttl-on-option" },
|
||||
{ key: TtlType.Off, text: t(Keys.controls.settings.subSettings.ttlOff), ariaLabel: "ttl-off-option" },
|
||||
{
|
||||
key: TtlType.OnNoDefault,
|
||||
text: t(Keys.controls.settings.subSettings.ttlOnNoDefault),
|
||||
ariaLabel: "ttl-on-no-default-option",
|
||||
},
|
||||
{ key: TtlType.On, text: t(Keys.controls.settings.subSettings.ttlOn), ariaLabel: "ttl-on-option" },
|
||||
];
|
||||
|
||||
public getTtlValue = (value: string): TtlType => {
|
||||
@@ -216,13 +224,13 @@ export class SubSettingsComponent extends React.Component<SubSettingsComponentPr
|
||||
}}
|
||||
>
|
||||
<Text style={{ color: "var(--colorNeutralForeground1)" }}>
|
||||
To enable time-to-live (TTL) for your collection/documents,{" "}
|
||||
{t(Keys.controls.settings.subSettings.mongoTtlMessage)}{" "}
|
||||
<Link
|
||||
href="https://docs.microsoft.com/en-us/azure/cosmos-db/mongodb-time-to-live"
|
||||
target="_blank"
|
||||
style={{ color: "var(--colorBrandForeground1)" }}
|
||||
>
|
||||
create a TTL index
|
||||
{t(Keys.controls.settings.subSettings.mongoTtlLinkText)}
|
||||
</Link>
|
||||
.
|
||||
</Text>
|
||||
@@ -231,7 +239,7 @@ export class SubSettingsComponent extends React.Component<SubSettingsComponentPr
|
||||
<Stack {...titleAndInputStackProps}>
|
||||
<ChoiceGroup
|
||||
id="timeToLive"
|
||||
label="Time to Live"
|
||||
label={t(Keys.controls.settings.subSettings.timeToLive)}
|
||||
selectedKey={this.props.timeToLive}
|
||||
options={this.ttlChoiceGroupOptions}
|
||||
onChange={this.onTtlChange}
|
||||
@@ -255,8 +263,8 @@ export class SubSettingsComponent extends React.Component<SubSettingsComponentPr
|
||||
max={Int32.Max}
|
||||
value={this.props.displayedTtlSeconds}
|
||||
onChange={this.onTimeToLiveSecondsChange}
|
||||
suffix="second(s)"
|
||||
ariaLabel={`Time to live in seconds`}
|
||||
suffix={t(Keys.controls.settings.subSettings.seconds)}
|
||||
ariaLabel={t(Keys.controls.settings.subSettings.timeToLiveInSeconds)}
|
||||
data-test="ttl-input"
|
||||
/>
|
||||
)}
|
||||
@@ -264,16 +272,16 @@ export class SubSettingsComponent extends React.Component<SubSettingsComponentPr
|
||||
);
|
||||
|
||||
private analyticalTtlChoiceGroupOptions: IChoiceGroupOption[] = [
|
||||
{ key: TtlType.Off, text: "Off", disabled: true },
|
||||
{ key: TtlType.OnNoDefault, text: "On (no default)" },
|
||||
{ key: TtlType.On, text: "On" },
|
||||
{ key: TtlType.Off, text: t(Keys.controls.settings.subSettings.ttlOff), disabled: true },
|
||||
{ key: TtlType.OnNoDefault, text: t(Keys.controls.settings.subSettings.ttlOnNoDefault) },
|
||||
{ key: TtlType.On, text: t(Keys.controls.settings.subSettings.ttlOn) },
|
||||
];
|
||||
|
||||
private getAnalyticalStorageTtlComponent = (): JSX.Element => (
|
||||
<Stack {...titleAndInputStackProps}>
|
||||
<ChoiceGroup
|
||||
id="analyticalStorageTimeToLive"
|
||||
label="Analytical Storage Time to Live"
|
||||
label={t(Keys.controls.settings.subSettings.analyticalStorageTtl)}
|
||||
selectedKey={this.props.analyticalStorageTtlSelection}
|
||||
options={this.analyticalTtlChoiceGroupOptions}
|
||||
onChange={this.onAnalyticalStorageTtlSelectionChange}
|
||||
@@ -294,7 +302,7 @@ export class SubSettingsComponent extends React.Component<SubSettingsComponentPr
|
||||
min={1}
|
||||
max={Int32.Max}
|
||||
value={this.props.analyticalStorageTtlSeconds?.toString()}
|
||||
suffix="second(s)"
|
||||
suffix={t(Keys.controls.settings.subSettings.seconds)}
|
||||
onChange={this.onAnalyticalStorageTtlSecondsChange}
|
||||
/>
|
||||
)}
|
||||
@@ -302,14 +310,22 @@ export class SubSettingsComponent extends React.Component<SubSettingsComponentPr
|
||||
);
|
||||
|
||||
private geoSpatialConfigTypeChoiceGroupOptions: IChoiceGroupOption[] = [
|
||||
{ key: GeospatialConfigType.Geography, text: "Geography", ariaLabel: "geography-option" },
|
||||
{ key: GeospatialConfigType.Geometry, text: "Geometry", ariaLabel: "geometry-option" },
|
||||
{
|
||||
key: GeospatialConfigType.Geography,
|
||||
text: t(Keys.controls.settings.subSettings.geography),
|
||||
ariaLabel: "geography-option",
|
||||
},
|
||||
{
|
||||
key: GeospatialConfigType.Geometry,
|
||||
text: t(Keys.controls.settings.subSettings.geometry),
|
||||
ariaLabel: "geometry-option",
|
||||
},
|
||||
];
|
||||
|
||||
private getGeoSpatialComponent = (): JSX.Element => (
|
||||
<ChoiceGroup
|
||||
id="geoSpatialConfig"
|
||||
label="Geospatial Configuration"
|
||||
label={t(Keys.controls.settings.subSettings.geospatialConfiguration)}
|
||||
selectedKey={this.props.geospatialConfigType}
|
||||
options={this.geoSpatialConfigTypeChoiceGroupOptions}
|
||||
onChange={this.onGeoSpatialConfigTypeChange}
|
||||
@@ -318,8 +334,8 @@ export class SubSettingsComponent extends React.Component<SubSettingsComponentPr
|
||||
);
|
||||
|
||||
private changeFeedChoiceGroupOptions: IChoiceGroupOption[] = [
|
||||
{ key: ChangeFeedPolicyState.Off, text: "Off" },
|
||||
{ key: ChangeFeedPolicyState.On, text: "On" },
|
||||
{ key: ChangeFeedPolicyState.Off, text: t(Keys.controls.settings.subSettings.ttlOff) },
|
||||
{ key: ChangeFeedPolicyState.On, text: t(Keys.controls.settings.subSettings.ttlOn) },
|
||||
];
|
||||
|
||||
private getChangeFeedComponent = (): JSX.Element => {
|
||||
@@ -328,7 +344,10 @@ export class SubSettingsComponent extends React.Component<SubSettingsComponentPr
|
||||
return (
|
||||
<Stack>
|
||||
<Label id={labelId}>
|
||||
<ToolTipLabelComponent label="Change feed log retention policy" toolTipElement={changeFeedPolicyToolTip} />
|
||||
<ToolTipLabelComponent
|
||||
label={t(Keys.controls.settings.changeFeed.label)}
|
||||
toolTipElement={changeFeedPolicyToolTip}
|
||||
/>
|
||||
</Label>
|
||||
<ChoiceGroup
|
||||
id="changeFeedPolicy"
|
||||
@@ -354,9 +373,10 @@ export class SubSettingsComponent extends React.Component<SubSettingsComponentPr
|
||||
<Stack {...titleAndInputStackProps}>
|
||||
{this.getPartitionKeyVisible() && (
|
||||
<TooltipHost
|
||||
content={`This ${this.partitionKeyName.toLowerCase()} is used to distribute data across multiple partitions for scalability. The value "${
|
||||
this.partitionKeyValue
|
||||
}" determines how documents are partitioned.`}
|
||||
content={t(Keys.controls.settings.subSettings.partitionKeyTooltipTemplate, {
|
||||
partitionKeyName: this.partitionKeyName.toLowerCase(),
|
||||
partitionKeyValue: this.partitionKeyValue,
|
||||
})}
|
||||
styles={{
|
||||
root: {
|
||||
display: "block",
|
||||
@@ -373,14 +393,20 @@ export class SubSettingsComponent extends React.Component<SubSettingsComponentPr
|
||||
)}
|
||||
|
||||
{userContext.apiType === "SQL" && this.isLargePartitionKeyEnabled() && (
|
||||
<Text className={classNames.hintText}>Large {this.partitionKeyName.toLowerCase()} has been enabled.</Text>
|
||||
<Text className={classNames.hintText}>
|
||||
{t(Keys.controls.settings.subSettings.largePartitionKeyEnabled, {
|
||||
partitionKeyName: this.partitionKeyName.toLowerCase(),
|
||||
})}
|
||||
</Text>
|
||||
)}
|
||||
|
||||
{userContext.apiType === "SQL" &&
|
||||
(this.isHierarchicalPartitionedContainer() ? (
|
||||
<Text className={classNames.hintText}>Hierarchically partitioned container.</Text>
|
||||
<Text className={classNames.hintText}>{t(Keys.controls.settings.subSettings.hierarchicalPartitioned)}</Text>
|
||||
) : (
|
||||
<Text className={classNames.hintText}>Non-hierarchically partitioned container.</Text>
|
||||
<Text className={classNames.hintText}>
|
||||
{t(Keys.controls.settings.subSettings.nonHierarchicalPartitioned)}
|
||||
</Text>
|
||||
))}
|
||||
</Stack>
|
||||
);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Label, Slider, Stack, TextField, Toggle } from "@fluentui/react";
|
||||
import { ThroughputBucket } from "Contracts/DataModels";
|
||||
import { Keys, t } from "Localization";
|
||||
import React, { FC, useEffect, useState } from "react";
|
||||
import { isDirty } from "../../SettingsUtils";
|
||||
|
||||
@@ -65,7 +66,9 @@ export const ThroughputBucketsComponent: FC<ThroughputBucketsComponentProps> = (
|
||||
|
||||
return (
|
||||
<Stack tokens={{ childrenGap: "m" }} styles={{ root: { width: "70%", maxWidth: 700 } }}>
|
||||
<Label styles={{ root: { color: "var(--colorNeutralForeground1)" } }}>Throughput Buckets</Label>
|
||||
<Label styles={{ root: { color: "var(--colorNeutralForeground1)" } }}>
|
||||
{t(Keys.controls.settings.throughputBuckets.label)}
|
||||
</Label>
|
||||
<Stack>
|
||||
{throughputBuckets?.map((bucket) => (
|
||||
<Stack key={bucket.id} horizontal tokens={{ childrenGap: 8 }} verticalAlign="center">
|
||||
@@ -76,7 +79,9 @@ export const ThroughputBucketsComponent: FC<ThroughputBucketsComponentProps> = (
|
||||
value={bucket.maxThroughputPercentage}
|
||||
onChange={(newValue) => handleBucketChange(bucket.id, newValue)}
|
||||
showValue={false}
|
||||
label={`Bucket ${bucket.id}${bucket.id === 1 ? " (Data Explorer Query Bucket)" : ""}`}
|
||||
label={`${t(Keys.controls.settings.throughputBuckets.bucketLabel, { id: String(bucket.id) })}${
|
||||
bucket.id === 1 ? t(Keys.controls.settings.throughputBuckets.dataExplorerQueryBucket) : ""
|
||||
}`}
|
||||
styles={{
|
||||
root: { flex: 2, maxWidth: 400 },
|
||||
titleLabel: {
|
||||
@@ -99,8 +104,8 @@ export const ThroughputBucketsComponent: FC<ThroughputBucketsComponentProps> = (
|
||||
disabled={bucket.maxThroughputPercentage === 100}
|
||||
/>
|
||||
<Toggle
|
||||
onText="Active"
|
||||
offText="Inactive"
|
||||
onText={t(Keys.controls.settings.throughputBuckets.active)}
|
||||
offText={t(Keys.controls.settings.throughputBuckets.inactive)}
|
||||
checked={bucket.maxThroughputPercentage !== 100}
|
||||
onChange={(event, checked) => onToggle(bucket.id, checked)}
|
||||
styles={{
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
Text,
|
||||
TextField,
|
||||
} from "@fluentui/react";
|
||||
import { Keys, t } from "Localization";
|
||||
import React from "react";
|
||||
import * as DataModels from "../../../../../Contracts/DataModels";
|
||||
import * as SharedConstants from "../../../../../Shared/Constants";
|
||||
@@ -97,8 +98,8 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
|
||||
private throughputInputMaxValue: number;
|
||||
private autoPilotInputMaxValue: number;
|
||||
private options: IChoiceGroupOption[] = [
|
||||
{ key: "true", text: "Autoscale" },
|
||||
{ key: "false", text: "Manual" },
|
||||
{ key: "true", text: t(Keys.controls.settings.throughputInput.autoscale) },
|
||||
{ key: "false", text: t(Keys.controls.settings.throughputInput.manual) },
|
||||
];
|
||||
|
||||
// Style constants for theme-aware colors and layout
|
||||
@@ -244,7 +245,7 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
|
||||
return (
|
||||
<div>
|
||||
<Text style={{ fontWeight: 600, color: ThroughputInputAutoPilotV3Component.TEXT_COLOR_PRIMARY }}>
|
||||
Updated cost per month
|
||||
{t(Keys.controls.settings.costEstimate.updatedCostPerMonth)}
|
||||
</Text>
|
||||
<Stack horizontal style={{ marginTop: 5, marginBottom: 10 }}>
|
||||
<Text
|
||||
@@ -253,7 +254,8 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
|
||||
color: ThroughputInputAutoPilotV3Component.TEXT_COLOR_PRIMARY,
|
||||
}}
|
||||
>
|
||||
{newPrices.currencySign} {calculateEstimateNumber(newPrices.monthlyPrice / 10)} min
|
||||
{newPrices.currencySign} {calculateEstimateNumber(newPrices.monthlyPrice / 10)}{" "}
|
||||
{t(Keys.controls.settings.throughputInput.min)}
|
||||
</Text>
|
||||
<Text
|
||||
style={{
|
||||
@@ -261,7 +263,8 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
|
||||
color: ThroughputInputAutoPilotV3Component.TEXT_COLOR_PRIMARY,
|
||||
}}
|
||||
>
|
||||
{newPrices.currencySign} {calculateEstimateNumber(newPrices.monthlyPrice)} max
|
||||
{newPrices.currencySign} {calculateEstimateNumber(newPrices.monthlyPrice)}{" "}
|
||||
{t(Keys.controls.settings.throughputInput.max)}
|
||||
</Text>
|
||||
</Stack>
|
||||
</div>
|
||||
@@ -274,7 +277,7 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
|
||||
<Stack {...checkBoxAndInputStackProps} style={{ marginTop: 15 }}>
|
||||
{newThroughput && newThroughputCostElement()}
|
||||
<Text style={{ fontWeight: 600, color: ThroughputInputAutoPilotV3Component.TEXT_COLOR_PRIMARY }}>
|
||||
Current cost per month
|
||||
{t(Keys.controls.settings.costEstimate.currentCostPerMonth)}
|
||||
</Text>
|
||||
<Stack horizontal style={{ marginTop: 5, color: ThroughputInputAutoPilotV3Component.TEXT_COLOR_PRIMARY }}>
|
||||
<Text
|
||||
@@ -283,7 +286,8 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
|
||||
color: ThroughputInputAutoPilotV3Component.TEXT_COLOR_PRIMARY,
|
||||
}}
|
||||
>
|
||||
{prices.currencySign} {calculateEstimateNumber(prices.monthlyPrice / 10)} min
|
||||
{prices.currencySign} {calculateEstimateNumber(prices.monthlyPrice / 10)}{" "}
|
||||
{t(Keys.controls.settings.throughputInput.min)}
|
||||
</Text>
|
||||
<Text
|
||||
style={{
|
||||
@@ -291,7 +295,8 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
|
||||
color: ThroughputInputAutoPilotV3Component.TEXT_COLOR_PRIMARY,
|
||||
}}
|
||||
>
|
||||
{prices.currencySign} {calculateEstimateNumber(prices.monthlyPrice)} max
|
||||
{prices.currencySign} {calculateEstimateNumber(prices.monthlyPrice)}{" "}
|
||||
{t(Keys.controls.settings.throughputInput.max)}
|
||||
</Text>
|
||||
</Stack>
|
||||
</Stack>
|
||||
@@ -326,17 +331,20 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
|
||||
return (
|
||||
<div>
|
||||
<Text style={{ fontWeight: 600, color: ThroughputInputAutoPilotV3Component.TEXT_COLOR_PRIMARY }}>
|
||||
Updated cost per month
|
||||
{t(Keys.controls.settings.costEstimate.updatedCostPerMonth)}
|
||||
</Text>
|
||||
<Stack horizontal style={{ marginTop: 5, marginBottom: 10 }}>
|
||||
<Text style={this.settingsAndScaleStyle.root}>
|
||||
{newPrices.currencySign} {calculateEstimateNumber(newPrices.hourlyPrice)}/hr
|
||||
{newPrices.currencySign} {calculateEstimateNumber(newPrices.hourlyPrice)}
|
||||
{t(Keys.controls.settings.costEstimate.perHour)}
|
||||
</Text>
|
||||
<Text style={this.settingsAndScaleStyle.root}>
|
||||
{newPrices.currencySign} {calculateEstimateNumber(newPrices.dailyPrice)}/day
|
||||
{newPrices.currencySign} {calculateEstimateNumber(newPrices.dailyPrice)}
|
||||
{t(Keys.controls.settings.costEstimate.perDay)}
|
||||
</Text>
|
||||
<Text style={this.settingsAndScaleStyle.root}>
|
||||
{newPrices.currencySign} {calculateEstimateNumber(newPrices.monthlyPrice)}/mo
|
||||
{newPrices.currencySign} {calculateEstimateNumber(newPrices.monthlyPrice)}
|
||||
{t(Keys.controls.settings.costEstimate.perMonth)}
|
||||
</Text>
|
||||
</Stack>
|
||||
</div>
|
||||
@@ -349,17 +357,20 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
|
||||
<Stack {...checkBoxAndInputStackProps} style={{ marginTop: 15 }}>
|
||||
{newThroughput && newThroughputCostElement()}
|
||||
<Text style={{ fontWeight: 600, color: ThroughputInputAutoPilotV3Component.TEXT_COLOR_PRIMARY }}>
|
||||
Current cost per month
|
||||
{t(Keys.controls.settings.costEstimate.currentCostPerMonth)}
|
||||
</Text>
|
||||
<Stack horizontal style={{ marginTop: 5 }}>
|
||||
<Text style={this.settingsAndScaleStyle.root}>
|
||||
{prices.currencySign} {calculateEstimateNumber(prices.hourlyPrice)}/hr
|
||||
{prices.currencySign} {calculateEstimateNumber(prices.hourlyPrice)}
|
||||
{t(Keys.controls.settings.costEstimate.perHour)}
|
||||
</Text>
|
||||
<Text style={this.settingsAndScaleStyle.root}>
|
||||
{prices.currencySign} {calculateEstimateNumber(prices.dailyPrice)}/day
|
||||
{prices.currencySign} {calculateEstimateNumber(prices.dailyPrice)}
|
||||
{t(Keys.controls.settings.costEstimate.perDay)}
|
||||
</Text>
|
||||
<Text style={this.settingsAndScaleStyle.root}>
|
||||
{prices.currencySign} {calculateEstimateNumber(prices.monthlyPrice)}/mo
|
||||
{prices.currencySign} {calculateEstimateNumber(prices.monthlyPrice)}
|
||||
{t(Keys.controls.settings.costEstimate.perMonth)}
|
||||
</Text>
|
||||
</Stack>
|
||||
</Stack>
|
||||
@@ -444,10 +455,14 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
|
||||
this.setState({ spendAckChecked: checked });
|
||||
|
||||
private getStorageCapacityTitle = (): JSX.Element => {
|
||||
const capacity: string = this.props.isFixed ? "Fixed" : "Unlimited";
|
||||
const capacity: string = this.props.isFixed
|
||||
? t(Keys.controls.settings.throughputInput.fixed)
|
||||
: t(Keys.controls.settings.throughputInput.unlimited);
|
||||
return (
|
||||
<Stack {...titleAndInputStackProps}>
|
||||
<Label style={{ color: "var(--colorNeutralForeground1)" }}>Storage capacity</Label>
|
||||
<Label style={{ color: "var(--colorNeutralForeground1)" }}>
|
||||
{t(Keys.controls.settings.throughputInput.storageCapacity)}
|
||||
</Label>
|
||||
<Text style={{ color: "var(--colorNeutralForeground1)" }}>{capacity}</Text>
|
||||
</Stack>
|
||||
);
|
||||
@@ -555,10 +570,14 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
|
||||
/>
|
||||
<Stack horizontal>
|
||||
<Stack.Item style={{ width: "34%", paddingRight: "5px" }}>
|
||||
<Separator styles={this.thoughputRangeSeparatorStyles}>Instant</Separator>
|
||||
<Separator styles={this.thoughputRangeSeparatorStyles}>
|
||||
{t(Keys.controls.settings.throughputInput.instant)}
|
||||
</Separator>
|
||||
</Stack.Item>
|
||||
<Stack.Item style={{ width: "66%", paddingLeft: "5px" }}>
|
||||
<Separator styles={this.thoughputRangeSeparatorStyles}>4-6 hrs</Separator>
|
||||
<Separator styles={this.thoughputRangeSeparatorStyles}>
|
||||
{t(Keys.controls.settings.throughputInput.fourToSixHrs)}
|
||||
</Separator>
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
</Stack>
|
||||
@@ -638,7 +657,7 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
|
||||
variant="small"
|
||||
style={{ lineHeight: "20px", fontWeight: 600, color: "var(--colorNeutralForeground1)" }}
|
||||
>
|
||||
Minimum RU/s
|
||||
{t(Keys.controls.settings.throughputInput.minimumRuS)}
|
||||
</Text>
|
||||
<FontIcon iconName="Info" style={{ fontSize: 12, color: "var(--colorNeutralForeground2)" }} />
|
||||
</Stack>
|
||||
@@ -672,7 +691,7 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
|
||||
color: "var(--colorNeutralForeground1)",
|
||||
}}
|
||||
>
|
||||
x 10 =
|
||||
{t(Keys.controls.settings.throughputInput.x10Equals)}
|
||||
</Text>
|
||||
|
||||
{/* Column 3: Maximum RU/s */}
|
||||
@@ -682,7 +701,7 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
|
||||
variant="small"
|
||||
style={{ lineHeight: "20px", fontWeight: 600, color: "var(--colorNeutralForeground1)" }}
|
||||
>
|
||||
Maximum RU/s
|
||||
{t(Keys.controls.settings.throughputInput.maximumRuS)}
|
||||
</Text>
|
||||
<FontIcon iconName="Info" style={{ fontSize: 12, color: "var(--colorNeutralForeground2)" }} />
|
||||
</Stack>
|
||||
@@ -723,7 +742,9 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
|
||||
onGetErrorMessage={(value: string) => {
|
||||
const sanitizedValue = getSanitizedInputValue(value);
|
||||
const errorMessage: string =
|
||||
sanitizedValue % 1000 ? "Throughput value must be in increments of 1000" : this.props.throughputError;
|
||||
sanitizedValue % 1000
|
||||
? t(Keys.controls.settings.throughput.throughputIncrementError)
|
||||
: this.props.throughputError;
|
||||
return <span data-test="autopilot-throughput-input-error">{errorMessage}</span>;
|
||||
}}
|
||||
validateOnLoad={false}
|
||||
@@ -769,7 +790,9 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
|
||||
)}
|
||||
{this.props.isAutoPilotSelected ? (
|
||||
<Text style={{ marginTop: "40px", color: "var(--colorNeutralForeground1)" }}>
|
||||
Based on usage, your {this.props.collectionName ? "container" : "database"} throughput will scale from{" "}
|
||||
{t(Keys.controls.settings.throughputInput.autoscaleDescription, {
|
||||
resourceType: this.props.collectionName ? "container" : "database",
|
||||
})}{" "}
|
||||
<b>
|
||||
{AutoPilotUtils.getMinRUsBasedOnUserInput(this.props.maxAutoPilotThroughput)} RU/s (10% of max RU/s) -{" "}
|
||||
{this.props.maxAutoPilotThroughput} RU/s
|
||||
@@ -784,16 +807,19 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
|
||||
styles={this.darkThemeMessageBarStyles}
|
||||
style={{ marginTop: "40px" }}
|
||||
>
|
||||
{`Billing will apply if you provision more than ${SharedConstants.FreeTierLimits.RU} RU/s of manual throughput, or if the resource scales beyond ${SharedConstants.FreeTierLimits.RU} RU/s with autoscale.`}
|
||||
{t(Keys.controls.settings.throughputInput.freeTierWarning, {
|
||||
ru: String(SharedConstants.FreeTierLimits.RU),
|
||||
})}
|
||||
</MessageBar>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{!this.overrideWithProvisionedThroughputSettings() && (
|
||||
<Text style={{ color: "var(--colorNeutralForeground1)" }}>
|
||||
Estimate your required RU/s with
|
||||
{t(Keys.controls.settings.throughputInput.capacityCalculator)}
|
||||
<Link target="_blank" href="https://cosmos.azure.com/capacitycalculator/">
|
||||
{` capacity calculator`} <FontIcon iconName="NavigateExternalInline" />
|
||||
{t(Keys.controls.settings.throughputInput.capacityCalculatorLink)}{" "}
|
||||
<FontIcon iconName="NavigateExternalInline" />
|
||||
</Link>
|
||||
</Text>
|
||||
)}
|
||||
@@ -806,9 +832,7 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
|
||||
onChange={this.onSpendAckChecked}
|
||||
/>
|
||||
)}
|
||||
{this.props.isFixed && (
|
||||
<p>When using a collection with fixed storage capacity, you can set up to 10,000 RU/s.</p>
|
||||
)}
|
||||
{this.props.isFixed && <p>{t(Keys.controls.settings.throughputInput.fixedStorageNote)}</p>}
|
||||
{this.props.collectionName && (
|
||||
<Stack.Item style={{ marginTop: "40px" }}>{this.getStorageCapacityTitle()}</Stack.Item>
|
||||
)}
|
||||
|
||||
@@ -552,9 +552,7 @@ exports[`ThroughputInputAutoPilotV3Component autopilot input visible 1`] = `
|
||||
}
|
||||
}
|
||||
>
|
||||
You are not able to lower throughput below your current minimum of
|
||||
10000
|
||||
RU/s. For more information on this limit, please refer to our service quote documentation.
|
||||
You are not able to lower throughput below your current minimum of 10000 RU/s. For more information on this limit, please refer to our service quote documentation.
|
||||
<StyledLinkBase
|
||||
href="https://learn.microsoft.com/en-us/azure/cosmos-db/concepts-limits#minimum-throughput-limits"
|
||||
target="_blank"
|
||||
@@ -572,9 +570,7 @@ exports[`ThroughputInputAutoPilotV3Component autopilot input visible 1`] = `
|
||||
}
|
||||
}
|
||||
>
|
||||
Based on usage, your
|
||||
container
|
||||
throughput will scale from
|
||||
Based on usage, your container throughput will scale from
|
||||
|
||||
<b>
|
||||
400
|
||||
@@ -683,7 +679,8 @@ exports[`ThroughputInputAutoPilotV3Component autopilot input visible 1`] = `
|
||||
$
|
||||
|
||||
35.04
|
||||
min
|
||||
|
||||
min
|
||||
</Text>
|
||||
<Text
|
||||
style={
|
||||
@@ -696,7 +693,8 @@ exports[`ThroughputInputAutoPilotV3Component autopilot input visible 1`] = `
|
||||
$
|
||||
|
||||
350.40
|
||||
max
|
||||
|
||||
max
|
||||
</Text>
|
||||
</Stack>
|
||||
</div>
|
||||
@@ -730,7 +728,8 @@ exports[`ThroughputInputAutoPilotV3Component autopilot input visible 1`] = `
|
||||
$
|
||||
|
||||
35.04
|
||||
min
|
||||
|
||||
min
|
||||
</Text>
|
||||
<Text
|
||||
style={
|
||||
@@ -743,7 +742,8 @@ exports[`ThroughputInputAutoPilotV3Component autopilot input visible 1`] = `
|
||||
$
|
||||
|
||||
350.40
|
||||
max
|
||||
|
||||
max
|
||||
</Text>
|
||||
</Stack>
|
||||
</Stack>
|
||||
@@ -1151,9 +1151,7 @@ exports[`ThroughputInputAutoPilotV3Component spendAck checkbox visible 1`] = `
|
||||
}
|
||||
}
|
||||
>
|
||||
You are not able to lower throughput below your current minimum of
|
||||
10000
|
||||
RU/s. For more information on this limit, please refer to our service quote documentation.
|
||||
You are not able to lower throughput below your current minimum of 10000 RU/s. For more information on this limit, please refer to our service quote documentation.
|
||||
<StyledLinkBase
|
||||
href="https://learn.microsoft.com/en-us/azure/cosmos-db/concepts-limits#minimum-throughput-limits"
|
||||
target="_blank"
|
||||
@@ -1728,9 +1726,7 @@ exports[`ThroughputInputAutoPilotV3Component throughput input visible 1`] = `
|
||||
}
|
||||
}
|
||||
>
|
||||
You are not able to lower throughput below your current minimum of
|
||||
10000
|
||||
RU/s. For more information on this limit, please refer to our service quote documentation.
|
||||
You are not able to lower throughput below your current minimum of 10000 RU/s. For more information on this limit, please refer to our service quote documentation.
|
||||
<StyledLinkBase
|
||||
href="https://learn.microsoft.com/en-us/azure/cosmos-db/concepts-limits#minimum-throughput-limits"
|
||||
target="_blank"
|
||||
|
||||
@@ -27,7 +27,8 @@ exports[`ComputedPropertiesComponent renders 1`] = `
|
||||
iconName="NavigateExternalInline"
|
||||
/>
|
||||
</StyledLinkBase>
|
||||
about how to define computed properties and how to use them.
|
||||
|
||||
about how to define computed properties and how to use them.
|
||||
</Text>
|
||||
<div
|
||||
className="settingsV2Editor"
|
||||
|
||||
@@ -33,8 +33,7 @@ exports[`PartitionKeyComponent renders default component and matches snapshot 1`
|
||||
}
|
||||
}
|
||||
>
|
||||
Change
|
||||
partition key
|
||||
Change partition key
|
||||
</Text>
|
||||
<Stack
|
||||
horizontal={true}
|
||||
@@ -61,8 +60,7 @@ exports[`PartitionKeyComponent renders default component and matches snapshot 1`
|
||||
}
|
||||
}
|
||||
>
|
||||
Current
|
||||
partition key
|
||||
Current partition key
|
||||
</Text>
|
||||
<Text
|
||||
styles={
|
||||
@@ -223,8 +221,7 @@ exports[`PartitionKeyComponent renders read-only component and matches snapshot
|
||||
}
|
||||
}
|
||||
>
|
||||
Current
|
||||
partition key
|
||||
Current partition key
|
||||
</Text>
|
||||
<Text
|
||||
styles={
|
||||
|
||||
@@ -410,9 +410,7 @@ exports[`SubSettingsComponent analyticalTimeToLive hidden 1`] = `
|
||||
<Text
|
||||
className="hintText-115"
|
||||
>
|
||||
Large
|
||||
partition key
|
||||
has been enabled.
|
||||
Large partition key has been enabled.
|
||||
</Text>
|
||||
<Text
|
||||
className="hintText-115"
|
||||
@@ -984,9 +982,7 @@ exports[`SubSettingsComponent analyticalTimeToLiveSeconds hidden 1`] = `
|
||||
<Text
|
||||
className="hintText-115"
|
||||
>
|
||||
Large
|
||||
partition key
|
||||
has been enabled.
|
||||
Large partition key has been enabled.
|
||||
</Text>
|
||||
<Text
|
||||
className="hintText-115"
|
||||
@@ -1522,9 +1518,7 @@ exports[`SubSettingsComponent changeFeedPolicy hidden 1`] = `
|
||||
<Text
|
||||
className="hintText-115"
|
||||
>
|
||||
Large
|
||||
partition key
|
||||
has been enabled.
|
||||
Large partition key has been enabled.
|
||||
</Text>
|
||||
<Text
|
||||
className="hintText-115"
|
||||
@@ -2157,9 +2151,7 @@ exports[`SubSettingsComponent renders 1`] = `
|
||||
<Text
|
||||
className="hintText-115"
|
||||
>
|
||||
Large
|
||||
partition key
|
||||
has been enabled.
|
||||
Large partition key has been enabled.
|
||||
</Text>
|
||||
<Text
|
||||
className="hintText-115"
|
||||
@@ -2729,9 +2721,7 @@ exports[`SubSettingsComponent timeToLiveSeconds hidden 1`] = `
|
||||
<Text
|
||||
className="hintText-115"
|
||||
>
|
||||
Large
|
||||
partition key
|
||||
has been enabled.
|
||||
Large partition key has been enabled.
|
||||
</Text>
|
||||
<Text
|
||||
className="hintText-115"
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import * as Constants from "../../../Common/Constants";
|
||||
import * as DataModels from "../../../Contracts/DataModels";
|
||||
import * as ViewModels from "../../../Contracts/ViewModels";
|
||||
import { Keys, t } from "Localization";
|
||||
import { isFabricNative } from "../../../Platform/Fabric/FabricUtil";
|
||||
import { userContext } from "../../../UserContext";
|
||||
import { isCapabilityEnabled } from "../../../Utils/CapabilityUtils";
|
||||
@@ -175,25 +176,27 @@ const getStringValue = (value: isDirtyTypes, type: string): string => {
|
||||
export const getTabTitle = (tab: SettingsV2TabTypes): string => {
|
||||
switch (tab) {
|
||||
case SettingsV2TabTypes.ScaleTab:
|
||||
return "Scale";
|
||||
return t(Keys.controls.settings.tabTitles.scale);
|
||||
case SettingsV2TabTypes.ConflictResolutionTab:
|
||||
return "Conflict Resolution";
|
||||
return t(Keys.controls.settings.tabTitles.conflictResolution);
|
||||
case SettingsV2TabTypes.SubSettingsTab:
|
||||
return "Settings";
|
||||
return t(Keys.controls.settings.tabTitles.settings);
|
||||
case SettingsV2TabTypes.IndexingPolicyTab:
|
||||
return "Indexing Policy";
|
||||
return t(Keys.controls.settings.tabTitles.indexingPolicy);
|
||||
case SettingsV2TabTypes.PartitionKeyTab:
|
||||
return isFabricNative() ? "Partition Keys" : "Partition Keys (preview)";
|
||||
return isFabricNative()
|
||||
? t(Keys.controls.settings.tabTitles.partitionKeys)
|
||||
: t(Keys.controls.settings.tabTitles.partitionKeysPreview);
|
||||
case SettingsV2TabTypes.ComputedPropertiesTab:
|
||||
return "Computed Properties";
|
||||
return t(Keys.controls.settings.tabTitles.computedProperties);
|
||||
case SettingsV2TabTypes.ContainerVectorPolicyTab:
|
||||
return "Container Policies";
|
||||
return t(Keys.controls.settings.tabTitles.containerPolicies);
|
||||
case SettingsV2TabTypes.ThroughputBucketsTab:
|
||||
return "Throughput Buckets";
|
||||
return t(Keys.controls.settings.tabTitles.throughputBuckets);
|
||||
case SettingsV2TabTypes.GlobalSecondaryIndexTab:
|
||||
return "Global Secondary Index (Preview)";
|
||||
return t(Keys.controls.settings.tabTitles.globalSecondaryIndexPreview);
|
||||
case SettingsV2TabTypes.DataMaskingTab:
|
||||
return "Masking Policy (preview)";
|
||||
return t(Keys.controls.settings.tabTitles.maskingPolicyPreview);
|
||||
default:
|
||||
throw new Error(`Unknown tab ${tab}`);
|
||||
}
|
||||
@@ -203,19 +206,19 @@ export const getMongoNotification = (description: string, type: MongoIndexTypes)
|
||||
if (description && !type) {
|
||||
return {
|
||||
type: MongoNotificationType.Warning,
|
||||
message: "Please select a type for each index.",
|
||||
message: t(Keys.controls.settings.mongoNotifications.selectTypeWarning),
|
||||
};
|
||||
}
|
||||
|
||||
if (type && (!description || description.trim().length === 0)) {
|
||||
return {
|
||||
type: MongoNotificationType.Error,
|
||||
message: "Please enter a field name.",
|
||||
message: t(Keys.controls.settings.mongoNotifications.enterFieldNameError),
|
||||
};
|
||||
} else if (type === MongoIndexTypes.Wildcard && description?.indexOf("$**") === -1) {
|
||||
return {
|
||||
type: MongoNotificationType.Error,
|
||||
message: "Wildcard path is not present in the field name. Use a pattern like " + MongoWildcardPlaceHolder,
|
||||
message: t(Keys.controls.settings.mongoNotifications.wildcardPathError) + MongoWildcardPlaceHolder,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -249,28 +252,29 @@ export const isIndexTransforming = (indexTransformationProgress: number): boolea
|
||||
indexTransformationProgress !== undefined && indexTransformationProgress !== 100;
|
||||
|
||||
export const getPartitionKeyName = (apiType: string, isLowerCase?: boolean): string => {
|
||||
const partitionKeyName = apiType === "Mongo" ? "Shard key" : "Partition key";
|
||||
const partitionKeyName =
|
||||
apiType === "Mongo"
|
||||
? t(Keys.controls.settings.partitionKey.shardKey)
|
||||
: t(Keys.controls.settings.partitionKey.partitionKey);
|
||||
return isLowerCase ? partitionKeyName.toLocaleLowerCase() : partitionKeyName;
|
||||
};
|
||||
|
||||
export const getPartitionKeyTooltipText = (apiType: string): string => {
|
||||
if (apiType === "Mongo") {
|
||||
return "The shard key (field) is used to split your data across many replica sets (shards) to achieve unlimited scalability. It’s critical to choose a field that will evenly distribute your data.";
|
||||
return t(Keys.controls.settings.partitionKey.shardKeyTooltip);
|
||||
}
|
||||
let tooltipText = `The ${getPartitionKeyName(
|
||||
apiType,
|
||||
true,
|
||||
)} is used to automatically distribute data across partitions for scalability. Choose a property in your JSON document that has a wide range of values and evenly distributes request volume.`;
|
||||
let tooltipText = `The ${getPartitionKeyName(apiType, true)} ${t(
|
||||
Keys.controls.settings.partitionKey.partitionKeyTooltip,
|
||||
)}`;
|
||||
if (apiType === "SQL") {
|
||||
tooltipText += " For small read-heavy workloads or write-heavy workloads of any size, id is often a good choice.";
|
||||
tooltipText += t(Keys.controls.settings.partitionKey.sqlPartitionKeyTooltipSuffix);
|
||||
}
|
||||
return tooltipText;
|
||||
};
|
||||
|
||||
export const getPartitionKeySubtext = (partitionKeyDefault: boolean, apiType: string): string => {
|
||||
if (partitionKeyDefault && (apiType === "SQL" || apiType === "Mongo")) {
|
||||
const subtext = "For small workloads, the item ID is a suitable choice for the partition key.";
|
||||
return subtext;
|
||||
return t(Keys.controls.settings.partitionKey.partitionKeySubtext);
|
||||
}
|
||||
return "";
|
||||
};
|
||||
@@ -278,18 +282,18 @@ export const getPartitionKeySubtext = (partitionKeyDefault: boolean, apiType: st
|
||||
export const getPartitionKeyPlaceHolder = (apiType: string, index?: number): string => {
|
||||
switch (apiType) {
|
||||
case "Mongo":
|
||||
return "e.g., categoryId";
|
||||
return t(Keys.controls.settings.partitionKey.mongoPlaceholder);
|
||||
case "Gremlin":
|
||||
return "e.g., /address";
|
||||
return t(Keys.controls.settings.partitionKey.gremlinPlaceholder);
|
||||
case "SQL":
|
||||
return `${
|
||||
index === undefined
|
||||
? "Required - first partition key e.g., /TenantId"
|
||||
? t(Keys.controls.settings.partitionKey.sqlFirstPartitionKey)
|
||||
: index === 0
|
||||
? "second partition key e.g., /UserId"
|
||||
: "third partition key e.g., /SessionId"
|
||||
? t(Keys.controls.settings.partitionKey.sqlSecondPartitionKey)
|
||||
: t(Keys.controls.settings.partitionKey.sqlThirdPartitionKey)
|
||||
}`;
|
||||
default:
|
||||
return "e.g., /address/zipCode";
|
||||
return t(Keys.controls.settings.partitionKey.defaultPlaceholder);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -127,9 +127,13 @@ exports[`SettingsUtils functions render 1`] = `
|
||||
>
|
||||
The request to increase the throughput has successfully been submitted. This operation will take 1-3 business days to complete. View the latest status in Notifications.
|
||||
<br />
|
||||
Database:
|
||||
Database:
|
||||
|
||||
sampleDb
|
||||
, Container:
|
||||
,
|
||||
|
||||
Container:
|
||||
|
||||
sampleCollection
|
||||
|
||||
, Current manual throughput: 1000 RU/s, Target manual throughput: 2000
|
||||
@@ -309,7 +313,7 @@ exports[`SettingsUtils functions render 1`] = `
|
||||
}
|
||||
}
|
||||
>
|
||||
You can make more indexing changes once the current index transformation has completed. It is 90% complete.
|
||||
You can make more indexing changes once the current index transformation has completed. It is 90% complete.
|
||||
<StyledLinkBase
|
||||
onClick={[Function]}
|
||||
>
|
||||
|
||||
@@ -42,8 +42,7 @@ import {
|
||||
} from "Explorer/Panes/AddCollectionPanel/AddCollectionPanelUtility";
|
||||
import { useSidePanel } from "hooks/useSidePanel";
|
||||
import { useTeachingBubble } from "hooks/useTeachingBubble";
|
||||
import { Keys } from "Localization/Keys.generated";
|
||||
import { t } from "Localization/t";
|
||||
import { Keys, t } from "Localization";
|
||||
import { DEFAULT_FABRIC_NATIVE_CONTAINER_THROUGHPUT, isFabricNative } from "Platform/Fabric/FabricUtil";
|
||||
import React from "react";
|
||||
import { CollectionCreation } from "Shared/Constants";
|
||||
@@ -185,25 +184,25 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
||||
|
||||
{this.state.teachingBubbleStep === 1 && (
|
||||
<TeachingBubble
|
||||
headline="Create sample database"
|
||||
headline={t(Keys.panes.addCollection.teachingBubble.step1Headline)}
|
||||
target={"#newDatabaseId"}
|
||||
calloutProps={{ gapSpace: 16 }}
|
||||
primaryButtonProps={{ text: "Next", onClick: () => this.setState({ teachingBubbleStep: 2 }) }}
|
||||
secondaryButtonProps={{ text: "Cancel", onClick: () => this.setState({ teachingBubbleStep: 0 }) }}
|
||||
primaryButtonProps={{ text: t(Keys.common.next), onClick: () => this.setState({ teachingBubbleStep: 2 }) }}
|
||||
secondaryButtonProps={{
|
||||
text: t(Keys.common.cancel),
|
||||
onClick: () => this.setState({ teachingBubbleStep: 0 }),
|
||||
}}
|
||||
onDismiss={() => this.setState({ teachingBubbleStep: 0 })}
|
||||
footerContent="Step 1 of 4"
|
||||
footerContent={t(Keys.panes.addCollection.teachingBubble.stepOfTotal, { current: "1", total: "4" })}
|
||||
>
|
||||
<Stack>
|
||||
<Text style={{ color: "white" }}>
|
||||
Database is the parent of a container. You can create a new database or use an existing one. In this
|
||||
tutorial we are creating a new database named SampleDB.
|
||||
</Text>
|
||||
<Text style={{ color: "white" }}>{t(Keys.panes.addCollection.teachingBubble.step1Body)}</Text>
|
||||
<Link
|
||||
style={{ color: "white", fontWeight: 600 }}
|
||||
target="_blank"
|
||||
href="https://aka.ms/TeachingbubbleResources"
|
||||
>
|
||||
Learn more about resources.
|
||||
{t(Keys.panes.addCollection.teachingBubble.step1LearnMore)}
|
||||
</Link>
|
||||
</Stack>
|
||||
</TeachingBubble>
|
||||
@@ -211,21 +210,21 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
||||
|
||||
{this.state.teachingBubbleStep === 2 && (
|
||||
<TeachingBubble
|
||||
headline="Setting throughput"
|
||||
headline={t(Keys.panes.addCollection.teachingBubble.step2Headline)}
|
||||
target={"#autoscaleRUValueField"}
|
||||
calloutProps={{ gapSpace: 16 }}
|
||||
primaryButtonProps={{ text: "Next", onClick: () => this.setState({ teachingBubbleStep: 3 }) }}
|
||||
secondaryButtonProps={{ text: "Previous", onClick: () => this.setState({ teachingBubbleStep: 1 }) }}
|
||||
primaryButtonProps={{ text: t(Keys.common.next), onClick: () => this.setState({ teachingBubbleStep: 3 }) }}
|
||||
secondaryButtonProps={{
|
||||
text: t(Keys.common.previous),
|
||||
onClick: () => this.setState({ teachingBubbleStep: 1 }),
|
||||
}}
|
||||
onDismiss={() => this.setState({ teachingBubbleStep: 0 })}
|
||||
footerContent="Step 2 of 4"
|
||||
footerContent={t(Keys.panes.addCollection.teachingBubble.stepOfTotal, { current: "2", total: "4" })}
|
||||
>
|
||||
<Stack>
|
||||
<Text style={{ color: "white" }}>
|
||||
Cosmos DB recommends sharing throughput across database. Autoscale will give you a flexible amount of
|
||||
throughput based on the max RU/s set (Request Units).
|
||||
</Text>
|
||||
<Text style={{ color: "white" }}>{t(Keys.panes.addCollection.teachingBubble.step2Body)}</Text>
|
||||
<Link style={{ color: "white", fontWeight: 600 }} target="_blank" href="https://aka.ms/teachingbubbleRU">
|
||||
Learn more about RU/s.
|
||||
{t(Keys.panes.addCollection.teachingBubble.step2LearnMore)}
|
||||
</Link>
|
||||
</Stack>
|
||||
</TeachingBubble>
|
||||
@@ -233,36 +232,41 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
||||
|
||||
{this.state.teachingBubbleStep === 3 && (
|
||||
<TeachingBubble
|
||||
headline="Naming container"
|
||||
headline={t(Keys.panes.addCollection.teachingBubble.step3Headline)}
|
||||
target={"#collectionId"}
|
||||
calloutProps={{ gapSpace: 16 }}
|
||||
primaryButtonProps={{ text: "Next", onClick: () => this.setState({ teachingBubbleStep: 4 }) }}
|
||||
secondaryButtonProps={{ text: "Previous", onClick: () => this.setState({ teachingBubbleStep: 2 }) }}
|
||||
primaryButtonProps={{ text: t(Keys.common.next), onClick: () => this.setState({ teachingBubbleStep: 4 }) }}
|
||||
secondaryButtonProps={{
|
||||
text: t(Keys.common.previous),
|
||||
onClick: () => this.setState({ teachingBubbleStep: 2 }),
|
||||
}}
|
||||
onDismiss={() => this.setState({ teachingBubbleStep: 0 })}
|
||||
footerContent="Step 3 of 4"
|
||||
footerContent={t(Keys.panes.addCollection.teachingBubble.stepOfTotal, { current: "3", total: "4" })}
|
||||
>
|
||||
Name your container
|
||||
{t(Keys.panes.addCollection.teachingBubble.step3Body)}
|
||||
</TeachingBubble>
|
||||
)}
|
||||
|
||||
{this.state.teachingBubbleStep === 4 && (
|
||||
<TeachingBubble
|
||||
headline="Setting partition key"
|
||||
headline={t(Keys.panes.addCollection.teachingBubble.step4Headline)}
|
||||
target={"#addCollection-partitionKeyValue"}
|
||||
calloutProps={{ gapSpace: 16 }}
|
||||
primaryButtonProps={{
|
||||
text: "Create container",
|
||||
text: t(Keys.panes.addCollection.teachingBubble.step4CreateContainer),
|
||||
onClick: () => {
|
||||
this.setState({ teachingBubbleStep: 5 });
|
||||
this.submit();
|
||||
},
|
||||
}}
|
||||
secondaryButtonProps={{ text: "Previous", onClick: () => this.setState({ teachingBubbleStep: 2 }) }}
|
||||
secondaryButtonProps={{
|
||||
text: t(Keys.common.previous),
|
||||
onClick: () => this.setState({ teachingBubbleStep: 2 }),
|
||||
}}
|
||||
onDismiss={() => this.setState({ teachingBubbleStep: 0 })}
|
||||
footerContent="Step 4 of 4"
|
||||
footerContent={t(Keys.panes.addCollection.teachingBubble.stepOfTotal, { current: "4", total: "4" })}
|
||||
>
|
||||
Last step - you will need to define a partition key for your collection. /address was chosen for this
|
||||
particular example. A good partition key should have a wide range of possible value
|
||||
{t(Keys.panes.addCollection.teachingBubble.step4Body)}
|
||||
</TeachingBubble>
|
||||
)}
|
||||
|
||||
@@ -272,7 +276,9 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
||||
<Stack horizontal>
|
||||
<span className="mandatoryStar">* </span>
|
||||
<Text className="panelTextBold" variant="small">
|
||||
Database {userContext.apiType === "Mongo" ? "name" : "id"}
|
||||
{userContext.apiType === "Mongo"
|
||||
? t(Keys.panes.addCollection.databaseFieldLabelName)
|
||||
: t(Keys.panes.addCollection.databaseFieldLabelId)}
|
||||
</Text>
|
||||
<TooltipHost
|
||||
directionalHint={DirectionalHint.bottomLeftEdge}
|
||||
@@ -297,7 +303,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
||||
<input
|
||||
className="panelRadioBtn"
|
||||
checked={this.state.createNewDatabase}
|
||||
aria-label="Create new database"
|
||||
aria-label={t(Keys.panes.addCollection.createNewDatabaseAriaLabel)}
|
||||
aria-checked={this.state.createNewDatabase}
|
||||
name="databaseType"
|
||||
type="radio"
|
||||
@@ -311,7 +317,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
||||
<input
|
||||
className="panelRadioBtn"
|
||||
checked={!this.state.createNewDatabase}
|
||||
aria-label="Use existing database"
|
||||
aria-label={t(Keys.panes.addCollection.useExistingDatabaseAriaLabel)}
|
||||
aria-checked={!this.state.createNewDatabase}
|
||||
name="databaseType"
|
||||
type="radio"
|
||||
@@ -335,10 +341,10 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
||||
autoComplete="off"
|
||||
pattern={ValidCosmosDbIdInputPattern.source}
|
||||
title={ValidCosmosDbIdDescription}
|
||||
placeholder="Type a new database id"
|
||||
placeholder={t(Keys.panes.addCollection.newDatabaseIdPlaceholder)}
|
||||
size={40}
|
||||
className="panelTextField"
|
||||
aria-label="New database id, Type a new database id"
|
||||
aria-label={t(Keys.panes.addCollection.newDatabaseIdAriaLabel)}
|
||||
tabIndex={0}
|
||||
value={this.state.newDatabaseId}
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
|
||||
@@ -404,10 +410,10 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
||||
)}
|
||||
{!this.state.createNewDatabase && (
|
||||
<Dropdown
|
||||
ariaLabel="Choose an existing database"
|
||||
ariaLabel={t(Keys.panes.addCollection.chooseExistingDatabase)}
|
||||
styles={{ title: { height: 27, lineHeight: 27 }, dropdownItem: { fontSize: 12 } }}
|
||||
style={{ width: 300, fontSize: 12 }}
|
||||
placeholder="Choose an existing database"
|
||||
placeholder={t(Keys.panes.addCollection.chooseExistingDatabase)}
|
||||
options={this.getDatabaseOptions()}
|
||||
onChange={(event: React.FormEvent<HTMLDivElement>, database: IDropdownOption) =>
|
||||
this.setState({ selectedDatabaseId: database.key as string })
|
||||
@@ -1027,16 +1033,15 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
||||
<PanelLoadingScreen />
|
||||
{this.state.teachingBubbleStep === 5 && (
|
||||
<TeachingBubble
|
||||
headline="Creating sample container"
|
||||
headline={t(Keys.panes.addCollection.teachingBubble.step5Headline)}
|
||||
target={"#loadingScreen"}
|
||||
onDismiss={() => this.setState({ teachingBubbleStep: 0 })}
|
||||
styles={{ footer: { width: "100%" } }}
|
||||
>
|
||||
A sample container is now being created and we are adding sample data for you. It should take about 1
|
||||
minute.
|
||||
{t(Keys.panes.addCollection.teachingBubble.step5Body)}
|
||||
<br />
|
||||
<br />
|
||||
Once the sample container is created, review your sample dataset and follow next steps
|
||||
{t(Keys.panes.addCollection.teachingBubble.step5BodyFollowUp)}
|
||||
<br />
|
||||
<br />
|
||||
<ProgressIndicator
|
||||
|
||||
@@ -3,8 +3,7 @@ import * as Constants from "Common/Constants";
|
||||
import { configContext, Platform } from "ConfigContext";
|
||||
import * as DataModels from "Contracts/DataModels";
|
||||
import { getFullTextLanguageOptions } from "Explorer/Controls/FullTextSeach/FullTextPoliciesComponent";
|
||||
import { Keys } from "Localization/Keys.generated";
|
||||
import { t } from "Localization/t";
|
||||
import { Keys, t } from "Localization";
|
||||
import { isFabricNative } from "Platform/Fabric/FabricUtil";
|
||||
import React from "react";
|
||||
import { userContext } from "UserContext";
|
||||
|
||||
@@ -29,8 +29,7 @@ exports[`AddCollectionPanel should render Default properly 1`] = `
|
||||
className="panelTextBold"
|
||||
variant="small"
|
||||
>
|
||||
Database
|
||||
id
|
||||
Database id
|
||||
</Text>
|
||||
<StyledTooltipHostBase
|
||||
content="A database is analogous to a namespace. It is the unit of management for a set of containers."
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Checkbox, Stack, Text, TextField } from "@fluentui/react";
|
||||
import { getNewDatabaseSharedThroughputDefault } from "Common/DatabaseUtility";
|
||||
import { Keys } from "Localization/Keys.generated";
|
||||
import { t } from "Localization/t";
|
||||
import { Keys, t } from "Localization";
|
||||
import { ValidCosmosDbIdDescription, ValidCosmosDbIdInputPattern } from "Utils/ValidationUtils";
|
||||
import React, { FunctionComponent, useEffect, useState } from "react";
|
||||
import * as Constants from "../../../Common/Constants";
|
||||
@@ -156,7 +155,9 @@ export const AddDatabasePanel: FunctionComponent<AddDatabasePaneProps> = ({
|
||||
|
||||
if (throughput > SharedConstants.CollectionCreation.DefaultCollectionRUs100K && !isCostAcknowledged) {
|
||||
setFormErrors(
|
||||
t(Keys.panes.addDatabase.acknowledgeSpendError, { period: isAutoscaleSelected ? "monthly" : "daily" }),
|
||||
isAutoscaleSelected
|
||||
? t(Keys.panes.addDatabase.acknowledgeSpendErrorMonthly)
|
||||
: t(Keys.panes.addDatabase.acknowledgeSpendErrorDaily),
|
||||
);
|
||||
return false;
|
||||
}
|
||||
@@ -227,7 +228,7 @@ export const AddDatabasePanel: FunctionComponent<AddDatabasePaneProps> = ({
|
||||
{!isServerlessAccount() && (
|
||||
<Stack horizontal>
|
||||
<Checkbox
|
||||
title="Provision shared throughput"
|
||||
title={t(Keys.panes.addDatabase.provisionSharedThroughputTitle)}
|
||||
styles={{
|
||||
text: { fontSize: 12, color: "var(--colorNeutralForeground1)" },
|
||||
checkbox: { width: 12, height: 12 },
|
||||
@@ -238,7 +239,7 @@ export const AddDatabasePanel: FunctionComponent<AddDatabasePaneProps> = ({
|
||||
},
|
||||
},
|
||||
}}
|
||||
label="Provision throughput"
|
||||
label={t(Keys.panes.addDatabase.provisionThroughputLabel)}
|
||||
checked={databaseCreateNewShared}
|
||||
onChange={() => setDatabaseCreateNewShared(!databaseCreateNewShared)}
|
||||
/>
|
||||
|
||||
@@ -40,8 +40,7 @@ import { PanelInfoErrorComponent } from "Explorer/Panes/PanelInfoErrorComponent"
|
||||
import { PanelLoadingScreen } from "Explorer/Panes/PanelLoadingScreen";
|
||||
import { useDatabases } from "Explorer/useDatabases";
|
||||
import { useSidePanel } from "hooks/useSidePanel";
|
||||
import { Keys } from "Localization/Keys.generated";
|
||||
import { t } from "Localization/t";
|
||||
import { Keys, t } from "Localization";
|
||||
import React, { MutableRefObject, useEffect, useRef, useState } from "react";
|
||||
import { CollectionCreation } from "Shared/Constants";
|
||||
import { Action } from "Shared/Telemetry/TelemetryConstants";
|
||||
|
||||
@@ -3,8 +3,7 @@ import * as Constants from "Common/Constants";
|
||||
import { getErrorMessage, getErrorStack } from "Common/ErrorHandlingUtils";
|
||||
import { InfoTooltip } from "Common/Tooltip/InfoTooltip";
|
||||
import { useSidePanel } from "hooks/useSidePanel";
|
||||
import { Keys } from "Localization/Keys.generated";
|
||||
import { t } from "Localization/t";
|
||||
import { Keys, t } from "Localization";
|
||||
import React, { FunctionComponent, useState } from "react";
|
||||
import * as SharedConstants from "Shared/Constants";
|
||||
import { Action } from "Shared/Telemetry/TelemetryConstants";
|
||||
|
||||
@@ -26,8 +26,7 @@ import {
|
||||
import Explorer from "Explorer/Explorer";
|
||||
import { RightPaneForm } from "Explorer/Panes/RightPaneForm/RightPaneForm";
|
||||
import { useDatabases } from "Explorer/useDatabases";
|
||||
import { Keys } from "Localization/Keys.generated";
|
||||
import { t } from "Localization/t";
|
||||
import { Keys, t } from "Localization";
|
||||
import { userContext } from "UserContext";
|
||||
import { getCollectionName } from "Utils/APITypeUtils";
|
||||
import { ValidCosmosDbIdDescription, ValidCosmosDbIdInputPattern } from "Utils/ValidationUtils";
|
||||
|
||||
@@ -4,8 +4,7 @@ import { HttpStatusCodes, PoolIdType } from "../../../Common/Constants";
|
||||
import { getErrorMessage, handleError } from "../../../Common/ErrorHandlingUtils";
|
||||
import { GitHubOAuthService } from "../../../GitHub/GitHubOAuthService";
|
||||
import { IPinnedRepo, JunoClient } from "../../../Juno/JunoClient";
|
||||
import { Keys } from "../../../Localization/Keys.generated";
|
||||
import { t } from "../../../Localization/t";
|
||||
import { Keys, t } from "Localization";
|
||||
import * as GitHubUtils from "../../../Utils/GitHubUtils";
|
||||
import * as NotificationConsoleUtils from "../../../Utils/NotificationConsoleUtils";
|
||||
import { useSidePanel } from "../../../hooks/useSidePanel";
|
||||
|
||||
@@ -12,8 +12,7 @@ import {
|
||||
import { GitHubReposTitle } from "Explorer/Tree/ResourceTree";
|
||||
import React, { FormEvent, FunctionComponent } from "react";
|
||||
import { IPinnedRepo } from "../../../Juno/JunoClient";
|
||||
import { Keys } from "../../../Localization/Keys.generated";
|
||||
import { t } from "../../../Localization/t";
|
||||
import { Keys, t } from "Localization";
|
||||
import * as GitHubUtils from "../../../Utils/GitHubUtils";
|
||||
import { useNotebook } from "../../Notebook/useNotebook";
|
||||
|
||||
|
||||
@@ -4,8 +4,7 @@ import DeleteFeedback from "Common/DeleteFeedback";
|
||||
import { getErrorMessage, getErrorStack } from "Common/ErrorHandlingUtils";
|
||||
import { deleteCollection } from "Common/dataAccess/deleteCollection";
|
||||
import { Collection } from "Contracts/ViewModels";
|
||||
import { Keys } from "Localization/Keys.generated";
|
||||
import { t } from "Localization/t";
|
||||
import { Keys, t } from "Localization";
|
||||
import { DefaultExperienceUtility } from "Shared/DefaultExperienceUtility";
|
||||
import { Action, ActionModifiers } from "Shared/Telemetry/TelemetryConstants";
|
||||
import * as TelemetryProcessor from "Shared/Telemetry/TelemetryProcessor";
|
||||
|
||||
@@ -5,8 +5,7 @@ import DeleteFeedback from "Common/DeleteFeedback";
|
||||
import { getErrorMessage, getErrorStack } from "Common/ErrorHandlingUtils";
|
||||
import { deleteDatabase } from "Common/dataAccess/deleteDatabase";
|
||||
import { Collection, Database } from "Contracts/ViewModels";
|
||||
import { Keys } from "Localization/Keys.generated";
|
||||
import { t } from "Localization/t";
|
||||
import { Keys, t } from "Localization";
|
||||
import { DefaultExperienceUtility } from "Shared/DefaultExperienceUtility";
|
||||
import { Action, ActionModifiers } from "Shared/Telemetry/TelemetryConstants";
|
||||
import * as TelemetryProcessor from "Shared/Telemetry/TelemetryProcessor";
|
||||
|
||||
@@ -3,8 +3,7 @@ import { useBoolean } from "@fluentui/react-hooks";
|
||||
import React, { FunctionComponent, useRef, useState } from "react";
|
||||
import AddPropertyIcon from "../../../../images/Add-property.svg";
|
||||
import { useSidePanel } from "../../../hooks/useSidePanel";
|
||||
import { Keys } from "../../../Localization/Keys.generated";
|
||||
import { t } from "../../../Localization/t";
|
||||
import { Keys, t } from "Localization";
|
||||
import { logConsoleError } from "../../../Utils/NotificationConsoleUtils";
|
||||
import StoredProcedure from "../../Tree/StoredProcedure";
|
||||
import { RightPaneForm, RightPaneFormProps } from "../RightPaneForm/RightPaneForm";
|
||||
|
||||
@@ -11,8 +11,7 @@ import {
|
||||
import React, { FunctionComponent } from "react";
|
||||
import AddPropertyIcon from "../../../../images/Add-property.svg";
|
||||
import EntityCancelIcon from "../../../../images/Entity_cancel.svg";
|
||||
import { Keys } from "../../../Localization/Keys.generated";
|
||||
import { t } from "../../../Localization/t";
|
||||
import { Keys, t } from "Localization";
|
||||
|
||||
const dropdownStyles: Partial<IDropdownStyles> = { dropdown: { width: 100 } };
|
||||
const options = [
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import React, { FunctionComponent } from "react";
|
||||
import * as ViewModels from "../../../Contracts/ViewModels";
|
||||
import { Keys } from "../../../Localization/Keys.generated";
|
||||
import { t } from "../../../Localization/t";
|
||||
import { Keys, t } from "Localization";
|
||||
import { useSidePanel } from "../../../hooks/useSidePanel";
|
||||
import { GraphStyleComponent } from "../../Graph/GraphStyleComponent/GraphStyleComponent";
|
||||
import { IGraphConfig } from "../../Tabs/GraphTab";
|
||||
|
||||
@@ -5,8 +5,7 @@ import folderIcon from "../../../../images/folder_16x16.svg";
|
||||
import { logError } from "../../../Common/Logger";
|
||||
import { Collection } from "../../../Contracts/ViewModels";
|
||||
import { useSidePanel } from "../../../hooks/useSidePanel";
|
||||
import { Keys } from "../../../Localization/Keys.generated";
|
||||
import { t } from "../../../Localization/t";
|
||||
import { Keys, t } from "Localization";
|
||||
import { userContext } from "../../../UserContext";
|
||||
import { logConsoleError, logConsoleInfo, logConsoleProgress } from "../../../Utils/NotificationConsoleUtils";
|
||||
import { useSelectedNode } from "../../useSelectedNode";
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { useBoolean } from "@fluentui/react-hooks";
|
||||
import React, { FunctionComponent, useState } from "react";
|
||||
import * as ViewModels from "../../../Contracts/ViewModels";
|
||||
import { Keys } from "../../../Localization/Keys.generated";
|
||||
import { t } from "../../../Localization/t";
|
||||
import { Keys, t } from "Localization";
|
||||
import { useSidePanel } from "../../../hooks/useSidePanel";
|
||||
import { NewVertexComponent } from "../../Graph/NewVertexComponent/NewVertexComponent";
|
||||
import { RightPaneForm, RightPaneFormProps } from "../RightPaneForm/RightPaneForm";
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { Icon, Link, Stack, Text } from "@fluentui/react";
|
||||
import { Keys } from "Localization/Keys.generated";
|
||||
import { t } from "Localization/t";
|
||||
import { Keys, t } from "Localization";
|
||||
import React from "react";
|
||||
import { useNotificationConsole } from "../../hooks/useNotificationConsole";
|
||||
|
||||
|
||||
@@ -5,8 +5,7 @@ import { getErrorMessage, getErrorStack, handleError } from "../../../Common/Err
|
||||
import { useNotebookSnapshotStore } from "../../../hooks/useNotebookSnapshotStore";
|
||||
import { useSidePanel } from "../../../hooks/useSidePanel";
|
||||
import { JunoClient } from "../../../Juno/JunoClient";
|
||||
import { Keys } from "../../../Localization/Keys.generated";
|
||||
import { t } from "../../../Localization/t";
|
||||
import { Keys, t } from "Localization";
|
||||
import { Action } from "../../../Shared/Telemetry/TelemetryConstants";
|
||||
import { traceFailure, traceStart, traceSuccess } from "../../../Shared/Telemetry/TelemetryProcessor";
|
||||
import * as NotificationConsoleUtils from "../../../Utils/NotificationConsoleUtils";
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { Dropdown, IDropdownProps, ITextFieldProps, Stack, Text, TextField } from "@fluentui/react";
|
||||
import { ImmutableNotebook } from "@nteract/commutable";
|
||||
import React, { FunctionComponent, useState } from "react";
|
||||
import { Keys } from "../../../Localization/Keys.generated";
|
||||
import { t } from "../../../Localization/t";
|
||||
import { Keys, t } from "Localization";
|
||||
import { GalleryCardComponent } from "../../Controls/NotebookGallery/Cards/GalleryCardComponent";
|
||||
import * as FileSystemUtil from "../../Notebook/FileSystemUtil";
|
||||
import { SnapshotRequest } from "../../Notebook/NotebookComponent/types";
|
||||
|
||||
@@ -4,8 +4,7 @@ import React, { FunctionComponent, useState } from "react";
|
||||
import { Areas, SavedQueries } from "../../../Common/Constants";
|
||||
import { getErrorMessage, getErrorStack } from "../../../Common/ErrorHandlingUtils";
|
||||
import { Query } from "../../../Contracts/DataModels";
|
||||
import { Keys } from "../../../Localization/Keys.generated";
|
||||
import { t } from "../../../Localization/t";
|
||||
import { Keys, t } from "Localization";
|
||||
import { Action } from "../../../Shared/Telemetry/TelemetryConstants";
|
||||
import { traceFailure, traceStart, traceSuccess } from "../../../Shared/Telemetry/TelemetryProcessor";
|
||||
import { logConsoleError } from "../../../Utils/NotificationConsoleUtils";
|
||||
|
||||
@@ -24,8 +24,7 @@ import { InfoTooltip } from "Common/Tooltip/InfoTooltip";
|
||||
import { Platform, configContext } from "ConfigContext";
|
||||
import { useDialog } from "Explorer/Controls/Dialog";
|
||||
import { useDatabases } from "Explorer/useDatabases";
|
||||
import { Keys } from "Localization/Keys.generated";
|
||||
import { t } from "Localization/t";
|
||||
import { Keys, t } from "Localization";
|
||||
import { isFabric, isFabricNative } from "Platform/Fabric/FabricUtil";
|
||||
import {
|
||||
AppStateComponentNames,
|
||||
|
||||
@@ -11,8 +11,7 @@ import {
|
||||
import { configContext } from "ConfigContext";
|
||||
import { ColumnDefinition } from "Explorer/Tabs/DocumentsTabV2/DocumentsTableComponent";
|
||||
import { CosmosFluentProvider, getPlatformTheme } from "Explorer/Theme/ThemeUtil";
|
||||
import { Keys } from "Localization/Keys.generated";
|
||||
import { t } from "Localization/t";
|
||||
import { Keys, t } from "Localization";
|
||||
import React from "react";
|
||||
import { useSidePanel } from "../../../hooks/useSidePanel";
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { IDropdownOption, Image, Label, Stack, Text, TextField } from "@fluentui/react";
|
||||
import { useBoolean } from "@fluentui/react-hooks";
|
||||
import { Keys } from "Localization/Keys.generated";
|
||||
import { t } from "Localization/t";
|
||||
import { Keys, t } from "Localization";
|
||||
import { logConsoleError } from "Utils/NotificationConsoleUtils";
|
||||
import React, { FunctionComponent, useEffect, useState } from "react";
|
||||
import * as _ from "underscore";
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { IDropdownOption, Image, Label, Stack, Text, TextField } from "@fluentui/react";
|
||||
import { useBoolean } from "@fluentui/react-hooks";
|
||||
import { Keys } from "Localization/Keys.generated";
|
||||
import { t } from "Localization/t";
|
||||
import { Keys, t } from "Localization";
|
||||
import { logConsoleError } from "Utils/NotificationConsoleUtils";
|
||||
import React, { FunctionComponent, useEffect, useState } from "react";
|
||||
import * as _ from "underscore";
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { Checkbox, Text } from "@fluentui/react";
|
||||
import { Keys } from "Localization/Keys.generated";
|
||||
import { t } from "Localization/t";
|
||||
import { Keys, t } from "Localization";
|
||||
import React, { FunctionComponent, useEffect, useState } from "react";
|
||||
import { userContext } from "../../../../UserContext";
|
||||
import { useSidePanel } from "../../../../hooks/useSidePanel";
|
||||
|
||||
@@ -13,8 +13,7 @@ import {
|
||||
} from "@fluentui/react";
|
||||
import { Upload } from "Common/Upload/Upload";
|
||||
import { UploadDetailsRecord } from "Contracts/ViewModels";
|
||||
import { Keys } from "Localization/Keys.generated";
|
||||
import { t } from "Localization/t";
|
||||
import { Keys, t } from "Localization";
|
||||
import { logConsoleError } from "Utils/NotificationConsoleUtils";
|
||||
import React, { ChangeEvent, FunctionComponent, useReducer, useState } from "react";
|
||||
import { getErrorMessage } from "../../Tables/Utilities";
|
||||
|
||||
@@ -6,8 +6,7 @@ import { DocumentAddRegular, LinkMultipleRegular, OpenRegular } from "@fluentui/
|
||||
import { SampleDataConfiguration, SampleDataImportDialog } from "Explorer/SplashScreen/SampleDataImportDialog";
|
||||
import { SampleDataFile } from "Explorer/SplashScreen/SampleUtil";
|
||||
import { CosmosFluentProvider } from "Explorer/Theme/ThemeUtil";
|
||||
import { Keys } from "Localization/Keys.generated";
|
||||
import { t } from "Localization/t";
|
||||
import { Keys, t } from "Localization";
|
||||
import { isFabricNative, isFabricNativeReadOnly } from "Platform/Fabric/FabricUtil";
|
||||
import * as React from "react";
|
||||
import { userContext } from "UserContext";
|
||||
|
||||
@@ -12,8 +12,7 @@ import {
|
||||
} from "@fluentui/react-components";
|
||||
import Explorer from "Explorer/Explorer";
|
||||
import { checkContainerExists, createContainer, importData, SampleDataFile } from "Explorer/SplashScreen/SampleUtil";
|
||||
import { Keys } from "Localization/Keys.generated";
|
||||
import { t } from "Localization/t";
|
||||
import { Keys, t } from "Localization";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import * as ViewModels from "../../Contracts/ViewModels";
|
||||
|
||||
|
||||
@@ -16,8 +16,7 @@ import { sendMessage } from "Common/MessageHandler";
|
||||
import { MessageTypes } from "Contracts/ExplorerContracts";
|
||||
import { TerminalKind } from "Contracts/ViewModels";
|
||||
import { SplashScreenButton } from "Explorer/SplashScreen/SplashScreenButton";
|
||||
import { Keys } from "Localization/Keys.generated";
|
||||
import { t } from "Localization/t";
|
||||
import { Keys, t } from "Localization";
|
||||
import { Action } from "Shared/Telemetry/TelemetryConstants";
|
||||
import { traceOpen } from "Shared/Telemetry/TelemetryProcessor";
|
||||
import { useCarousel } from "hooks/useCarousel";
|
||||
|
||||
@@ -18,8 +18,7 @@ import { queryConflicts } from "../../Common/dataAccess/queryConflicts";
|
||||
import { updateDocument } from "../../Common/dataAccess/updateDocument";
|
||||
import * as DataModels from "../../Contracts/DataModels";
|
||||
import * as ViewModels from "../../Contracts/ViewModels";
|
||||
import { Keys } from "../../Localization/Keys.generated";
|
||||
import { t } from "../../Localization/t";
|
||||
import { Keys, t } from "Localization";
|
||||
import { Action } from "../../Shared/Telemetry/TelemetryConstants";
|
||||
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
||||
import { CommandButtonComponentProps } from "../Controls/CommandButton/CommandButtonComponent";
|
||||
|
||||
@@ -41,8 +41,7 @@ import { usePrevious } from "Explorer/Tabs/DocumentsTabV2/SelectionHelper";
|
||||
import { CosmosFluentProvider, LayoutConstants, cosmosShorthands, tokens } from "Explorer/Theme/ThemeUtil";
|
||||
import { useSelectedNode } from "Explorer/useSelectedNode";
|
||||
import { KeyboardAction, KeyboardActionGroup, useKeyboardActionGroup } from "KeyboardShortcuts";
|
||||
import { Keys } from "Localization/Keys.generated";
|
||||
import { t } from "Localization/t";
|
||||
import { Keys, t } from "Localization";
|
||||
import { isFabric } from "Platform/Fabric/FabricUtil";
|
||||
import { QueryConstants } from "Shared/Constants";
|
||||
import { LocalStorageUtility, StorageKey } from "Shared/StorageUtility";
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import React, { Component } from "react";
|
||||
import { configContext } from "../../../ConfigContext";
|
||||
import * as ViewModels from "../../../Contracts/ViewModels";
|
||||
import { Keys } from "../../../Localization/Keys.generated";
|
||||
import { t } from "../../../Localization/t";
|
||||
import { Keys, t } from "Localization";
|
||||
import { Action, ActionModifiers } from "../../../Shared/Telemetry/TelemetryConstants";
|
||||
import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
|
||||
import { userContext } from "../../../UserContext";
|
||||
|
||||
@@ -17,8 +17,7 @@ import { QueryTabStyles, useQueryTabStyles } from "Explorer/Tabs/QueryTab/Styles
|
||||
import { CosmosFluentProvider } from "Explorer/Theme/ThemeUtil";
|
||||
import { useSelectedNode } from "Explorer/useSelectedNode";
|
||||
import { KeyboardAction } from "KeyboardShortcuts";
|
||||
import { Keys } from "Localization/Keys.generated";
|
||||
import { t } from "Localization/t";
|
||||
import { Keys, t } from "Localization";
|
||||
import { QueryConstants } from "Shared/Constants";
|
||||
import { LocalStorageUtility, StorageKey } from "Shared/StorageUtility";
|
||||
import { Action } from "Shared/Telemetry/TelemetryConstants";
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Resource, StoredProcedureDefinition } from "@azure/cosmos";
|
||||
import { Pivot, PivotItem } from "@fluentui/react";
|
||||
import { KeyboardAction } from "KeyboardShortcuts";
|
||||
import { Keys, t } from "Localization";
|
||||
import { ValidCosmosDbIdDescription, ValidCosmosDbIdInputPattern } from "Utils/ValidationUtils";
|
||||
import React from "react";
|
||||
import ExecuteQueryIcon from "../../../../images/ExecuteQuery.svg";
|
||||
@@ -11,8 +12,6 @@ import { createStoredProcedure } from "../../../Common/dataAccess/createStoredPr
|
||||
import { ExecuteSprocResult } from "../../../Common/dataAccess/executeStoredProcedure";
|
||||
import { updateStoredProcedure } from "../../../Common/dataAccess/updateStoredProcedure";
|
||||
import * as ViewModels from "../../../Contracts/ViewModels";
|
||||
import { Keys } from "../../../Localization/Keys.generated";
|
||||
import { t } from "../../../Localization/t";
|
||||
import { useNotificationConsole } from "../../../hooks/useNotificationConsole";
|
||||
import { useTabs } from "../../../hooks/useTabs";
|
||||
import { CommandButtonComponentProps } from "../../Controls/CommandButton/CommandButtonComponent";
|
||||
|
||||
@@ -2,6 +2,7 @@ import { TriggerDefinition } from "@azure/cosmos";
|
||||
import { IDropdownOption, IDropdownStyles, Label, TextField } from "@fluentui/react";
|
||||
import { Dropdown } from "@fluentui/react/lib/Dropdown";
|
||||
import { KeyboardAction } from "KeyboardShortcuts";
|
||||
import { Keys, t } from "Localization";
|
||||
import { ValidCosmosDbIdDescription, ValidCosmosDbIdInputPattern } from "Utils/ValidationUtils";
|
||||
import React, { Component } from "react";
|
||||
import DiscardIcon from "../../../images/discard.svg";
|
||||
@@ -11,8 +12,6 @@ import { getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils"
|
||||
import { createTrigger } from "../../Common/dataAccess/createTrigger";
|
||||
import { updateTrigger } from "../../Common/dataAccess/updateTrigger";
|
||||
import * as ViewModels from "../../Contracts/ViewModels";
|
||||
import { Keys } from "../../Localization/Keys.generated";
|
||||
import { t } from "../../Localization/t";
|
||||
import { Action } from "../../Shared/Telemetry/TelemetryConstants";
|
||||
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
||||
import { SqlTriggerResource } from "../../Utils/arm/generatedClients/cosmos/types";
|
||||
|
||||
@@ -2,6 +2,7 @@ import { UserDefinedFunctionDefinition } from "@azure/cosmos";
|
||||
import { Label, TextField } from "@fluentui/react";
|
||||
import { FluentProvider, webDarkTheme, webLightTheme } from "@fluentui/react-components";
|
||||
import { KeyboardAction } from "KeyboardShortcuts";
|
||||
import { Keys, t } from "Localization";
|
||||
import { ValidCosmosDbIdDescription, ValidCosmosDbIdInputPattern } from "Utils/ValidationUtils";
|
||||
import { useThemeStore } from "hooks/useTheme";
|
||||
import React, { Component } from "react";
|
||||
@@ -12,8 +13,6 @@ import { getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils"
|
||||
import { createUserDefinedFunction } from "../../Common/dataAccess/createUserDefinedFunction";
|
||||
import { updateUserDefinedFunction } from "../../Common/dataAccess/updateUserDefinedFunction";
|
||||
import * as ViewModels from "../../Contracts/ViewModels";
|
||||
import { Keys } from "../../Localization/Keys.generated";
|
||||
import { t } from "../../Localization/t";
|
||||
import { Action } from "../../Shared/Telemetry/TelemetryConstants";
|
||||
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
||||
import { CommandButtonComponentProps } from "../Controls/CommandButton/CommandButtonComponent";
|
||||
|
||||
Reference in New Issue
Block a user