diff --git a/src/Explorer/Controls/Settings/SettingsSubComponents/ThroughputInputComponents/ThroughputInputAutoPilotV3Component.tsx b/src/Explorer/Controls/Settings/SettingsSubComponents/ThroughputInputComponents/ThroughputInputAutoPilotV3Component.tsx index e3c114d04..1ffd0d459 100644 --- a/src/Explorer/Controls/Settings/SettingsSubComponents/ThroughputInputComponents/ThroughputInputAutoPilotV3Component.tsx +++ b/src/Explorer/Controls/Settings/SettingsSubComponents/ThroughputInputComponents/ThroughputInputAutoPilotV3Component.tsx @@ -1,6 +1,7 @@ import { Checkbox, ChoiceGroup, + DefaultButton, FontIcon, IChoiceGroupOption, IMessageBarStyles, @@ -18,6 +19,7 @@ import { TextField, Toggle, } from "@fluentui/react"; +import { useDialog } from "Explorer/Controls/Dialog"; import { Keys, t } from "Localization"; import React from "react"; import * as DataModels from "../../../../../Contracts/DataModels"; @@ -90,10 +92,15 @@ export interface ThroughputInputAutoPilotV3Props { onHotPartitionKeyRateLimitingPolicyChange: (newPolicy: DataModels.HotPartitionKeyRateLimitingPolicy) => void; } +interface IsThroughputComponentDirtyResult extends IsComponentDirtyResult { + priceHasChanged: boolean; +} + interface ThroughputInputAutoPilotV3State { spendAckChecked: boolean; exceedFreeTierThroughput: boolean; } + export class ThroughputInputAutoPilotV3Component extends React.Component< ThroughputInputAutoPilotV3Props, ThroughputInputAutoPilotV3State @@ -135,14 +142,16 @@ export class ThroughputInputAutoPilotV3Component extends React.Component< this.shouldCheckComponentIsDirty = false; }; - public IsComponentDirty = (): IsComponentDirtyResult => { + public IsComponentDirty = (): IsThroughputComponentDirtyResult => { let isSaveable = false; let isDiscardable = false; + let priceHasChanged = false; if (this.props.isEnabled) { if (this.hasProvisioningTypeChanged()) { isSaveable = true; isDiscardable = true; + priceHasChanged = true; } else if ( isHotPartitionKeyThrottlingEnabled() && isDirty(this.props.hotPartitionKeyRateLimitingPolicy, this.props.hotPartitionKeyRateLimitingPolicyBaseline) @@ -152,6 +161,7 @@ export class ThroughputInputAutoPilotV3Component extends React.Component< } else if (this.props.isAutoPilotSelected) { if (isDirty(this.props.maxAutoPilotThroughput, this.props.maxAutoPilotThroughputBaseline)) { isDiscardable = true; + priceHasChanged = true; if ( this.props.softAllowedMaximumThroughput ? this.props.maxAutoPilotThroughput <= this.props.softAllowedMaximumThroughput && @@ -165,6 +175,7 @@ export class ThroughputInputAutoPilotV3Component extends React.Component< if (isDirty(this.props.throughput, this.props.throughputBaseline)) { isDiscardable = true; isSaveable = true; + priceHasChanged = true; if ( !this.props.throughput || this.props.throughput < this.props.minimum || @@ -177,7 +188,7 @@ export class ThroughputInputAutoPilotV3Component extends React.Component< } } } - return { isSaveable, isDiscardable }; + return { isSaveable, isDiscardable, priceHasChanged }; }; public constructor(props: ThroughputInputAutoPilotV3Props) { @@ -210,7 +221,7 @@ export class ThroughputInputAutoPilotV3Component extends React.Component< return <>; } - const isDirty: boolean = this.IsComponentDirty().isDiscardable; + const isDirty: boolean = this.IsComponentDirty().priceHasChanged; const regions = account?.properties?.readLocations?.length || 1; const multimaster = account?.properties?.enableMultipleWriteLocations || false; @@ -858,7 +869,7 @@ export class ThroughputInputAutoPilotV3Component extends React.Component< private renderWarningMessage = (): JSX.Element => { let warningMessage: JSX.Element; - if (this.IsComponentDirty().isDiscardable) { + if (this.IsComponentDirty().priceHasChanged) { warningMessage = saveThroughputWarningMessage; } @@ -889,34 +900,60 @@ export class ThroughputInputAutoPilotV3Component extends React.Component< onText={t(Keys.common.on)} offText={t(Keys.common.off)} checked={!!this.props.hotPartitionKeyRateLimitingPolicy} + disabled={!!this.props.hotPartitionKeyRateLimitingPolicy} onChange={(_ev, checked) => { if (checked) { - this.props.onHotPartitionKeyRateLimitingPolicyChange({ - maximumPerPartitionKeyThroughputUtilizationPercent: - this.props.hotPartitionKeyRateLimitingPolicyBaseline - ?.maximumPerPartitionKeyThroughputUtilizationPercent ?? 75, - }); - } else { - this.props.onHotPartitionKeyRateLimitingPolicyChange(null); + useDialog.getState().showOkCancelModalDialog( + t(Keys.controls.settings.scale.rateLimitConfirmOverride), + "", + t(Keys.common.yes), + () => + this.props.onHotPartitionKeyRateLimitingPolicyChange({ + maximumPerPartitionKeyThroughputUtilizationPercent: + this.props.hotPartitionKeyRateLimitingPolicyBaseline + ?.maximumPerPartitionKeyThroughputUtilizationPercent ?? 75, //CTODO: move default to common const when we get final default value from backend team + }), + t(Keys.common.no), + undefined, + <> + {t(Keys.controls.settings.scale.rateLimitOverrideWarning1)} +
+
+ {t(Keys.controls.settings.scale.rateLimitOverrideWarning2)} + , + ); } }} /> - `${value} percent`} - valueFormat={(value: number) => `${value}%`} - showValue - value={this.props.hotPartitionKeyRateLimitingPolicy?.maximumPerPartitionKeyThroughputUtilizationPercent ?? 75} - onChange={(value: number) => - this.props.onHotPartitionKeyRateLimitingPolicyChange({ - maximumPerPartitionKeyThroughputUtilizationPercent: value, - }) - } - /> + + `${value} percent`} + valueFormat={(value: number) => `${value}%`} + showValue + value={ + this.props.hotPartitionKeyRateLimitingPolicy?.maximumPerPartitionKeyThroughputUtilizationPercent ?? 75 + } + onChange={(value: number) => + this.props.onHotPartitionKeyRateLimitingPolicyChange({ + maximumPerPartitionKeyThroughputUtilizationPercent: value, + }) + } + styles={{ root: { width: "75%" } }} + /> + + this.props.onHotPartitionKeyRateLimitingPolicyChange({ + maximumPerPartitionKeyThroughputUtilizationPercent: 75, + }) + } + /> + ); }; diff --git a/src/Localization/en/Resources.json b/src/Localization/en/Resources.json index 60b50f63d..a4660775f 100644 --- a/src/Localization/en/Resources.json +++ b/src/Localization/en/Resources.json @@ -895,8 +895,11 @@ "keyspaceSharedThroughput": "This table shared throughput is configured at the keyspace", "throughputRangeLabel": "Throughput ({{min}} - {{max}} RU/s)", "unlimited": "unlimited", - "rateLimitingPolicyTitle": "Rate limiting policy", - "rateLimitPolicyMaxThroughputUtilizationLabel": "Max per partition key Throughput utilization" + "rateLimitingPolicyTitle": "Override default rate limiting policy", + "rateLimitPolicyMaxThroughputUtilizationLabel": "Max partition key throughput utilization", + "rateLimitOverrideWarning1": "Overriding the default rate limiting is irreversible. Though you will be able to manually reset the value to its default, the policy will remain overriden at the value you set.", + "rateLimitOverrideWarning2": "Are you sure you wish to continue?", + "rateLimitConfirmOverride": "Confirm override" }, "partitionKeyEditor": { "changePartitionKey": "Change {{partitionKeyName}}", diff --git a/src/Utils/arm/generatedClients/cosmos/sqlResources.ts b/src/Utils/arm/generatedClients/cosmos/sqlResources.ts index 33b7dc620..4c1b97392 100644 --- a/src/Utils/arm/generatedClients/cosmos/sqlResources.ts +++ b/src/Utils/arm/generatedClients/cosmos/sqlResources.ts @@ -9,7 +9,7 @@ import { configContext } from "../../../../ConfigContext"; import { armRequest } from "../../request"; import * as Types from "./types"; -const apiVersion = "2025-11-01-preview"; +const apiVersion = "2026-04-01-preview"; /* Lists the SQL databases under an existing Azure Cosmos DB database account. */ export async function listSqlDatabases(