diff --git a/src/Common/DataAccessUtilityBase.ts b/src/Common/DataAccessUtilityBase.ts index c498bf8ef..30620b7ff 100644 --- a/src/Common/DataAccessUtilityBase.ts +++ b/src/Common/DataAccessUtilityBase.ts @@ -6,8 +6,6 @@ import * as ViewModels from "../Contracts/ViewModels"; import Q from "q"; import { ConflictDefinition, - ContainerDefinition, - ContainerResponse, DatabaseResponse, FeedOptions, ItemDefinition, @@ -203,23 +201,6 @@ export function getPartitionKeyHeader(partitionKeyDefinition: DataModels.Partiti return [partitionKeyValue]; } -export function updateCollection( - databaseId: string, - collectionId: string, - newCollection: DataModels.Collection, - options: any = {} -): Q.Promise { - return Q( - client() - .database(databaseId) - .container(collectionId) - .replace(newCollection as ContainerDefinition, options) - .then(async (response: ContainerResponse) => { - return refreshCachedResources().then(() => response.resource as DataModels.Collection); - }) - ); -} - export function updateDocument( collection: ViewModels.CollectionBase, documentId: DocumentId, diff --git a/src/Common/DocumentClientUtilityBase.ts b/src/Common/DocumentClientUtilityBase.ts index 172e4ae9c..dbc8adec5 100644 --- a/src/Common/DocumentClientUtilityBase.ts +++ b/src/Common/DocumentClientUtilityBase.ts @@ -266,42 +266,6 @@ export function readDocument(collection: ViewModels.CollectionBase, documentId: return deferred.promise; } -export function updateCollection( - databaseId: string, - collection: ViewModels.Collection, - newCollection: DataModels.Collection -): Q.Promise { - var deferred = Q.defer(); - const id = NotificationConsoleUtils.logConsoleMessage( - ConsoleDataType.InProgress, - `Updating container ${collection.id()}` - ); - DataAccessUtilityBase.updateCollection(databaseId, collection.id(), newCollection) - .then( - (replacedCollection: DataModels.Collection) => { - NotificationConsoleUtils.logConsoleMessage( - ConsoleDataType.Info, - `Successfully updated container ${collection.id()}` - ); - deferred.resolve(replacedCollection); - }, - (error: any) => { - NotificationConsoleUtils.logConsoleMessage( - ConsoleDataType.Error, - `Failed to update container ${collection.id()}: ${JSON.stringify(error)}` - ); - Logger.logError(JSON.stringify(error), "UpdateCollection", error.code); - sendNotificationForError(error); - deferred.reject(error); - } - ) - .finally(() => { - NotificationConsoleUtils.clearInProgressMessageWithId(id); - }); - - return deferred.promise; -} - export function updateDocument( collection: ViewModels.CollectionBase, documentId: DocumentId, diff --git a/src/Common/dataAccess/updateCollection.ts b/src/Common/dataAccess/updateCollection.ts new file mode 100644 index 000000000..794c10003 --- /dev/null +++ b/src/Common/dataAccess/updateCollection.ts @@ -0,0 +1,225 @@ +import { AuthType } from "../../AuthType"; +import { Collection } from "../../Contracts/DataModels"; +import { ContainerDefinition } from "@azure/cosmos"; +import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType"; +import { + ExtendedResourceProperties, + SqlContainerCreateUpdateParameters, + SqlContainerResource +} from "../../Utils/arm/generatedClients/2020-04-01/types"; +import { RequestOptions } from "@azure/cosmos/dist-esm"; +import { client } from "../CosmosClient"; +import { createUpdateSqlContainer, getSqlContainer } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources"; +import { + createUpdateCassandraTable, + getCassandraTable +} from "../../Utils/arm/generatedClients/2020-04-01/cassandraResources"; +import { + createUpdateMongoDBCollection, + getMongoDBCollection +} from "../../Utils/arm/generatedClients/2020-04-01/mongoDBResources"; +import { + createUpdateGremlinGraph, + getGremlinGraph +} from "../../Utils/arm/generatedClients/2020-04-01/gremlinResources"; +import { createUpdateTable, getTable } from "../../Utils/arm/generatedClients/2020-04-01/tableResources"; +import { logConsoleError, logConsoleInfo, logConsoleProgress } from "../../Utils/NotificationConsoleUtils"; +import { logError } from "../Logger"; +import { refreshCachedResources } from "../DataAccessUtilityBase"; +import { sendNotificationForError } from "./sendNotificationForError"; +import { userContext } from "../../UserContext"; + +export async function updateCollection( + databaseId: string, + collectionId: string, + newCollection: Collection, + options: RequestOptions = {} +): Promise { + let collection: Collection; + const clearMessage = logConsoleProgress(`Updating container ${collectionId}`); + + try { + if ( + window.authType === AuthType.AAD && + userContext.defaultExperience !== DefaultAccountExperienceType.MongoDB && + userContext.defaultExperience !== DefaultAccountExperienceType.Table + ) { + collection = await updateCollectionWithARM(databaseId, collectionId, newCollection); + } else { + const sdkResponse = await client() + .database(databaseId) + .container(collectionId) + .replace(newCollection as ContainerDefinition, options); + collection = sdkResponse.resource as Collection; + } + } catch (error) { + logConsoleError(`Failed to update container ${collectionId}: ${JSON.stringify(error)}`); + logError(JSON.stringify(error), "UpdateCollection", error.code); + sendNotificationForError(error); + throw error; + } + logConsoleInfo(`Successfully updated container ${collectionId}`); + clearMessage(); + await refreshCachedResources(); + return collection; +} + +async function updateCollectionWithARM( + databaseId: string, + collectionId: string, + newCollection: Collection +): Promise { + const subscriptionId = userContext.subscriptionId; + const resourceGroup = userContext.resourceGroup; + const accountName = userContext.databaseAccount.name; + const defaultExperience = userContext.defaultExperience; + + switch (defaultExperience) { + case DefaultAccountExperienceType.DocumentDB: + return updateSqlContainer(databaseId, collectionId, subscriptionId, resourceGroup, accountName, newCollection); + case DefaultAccountExperienceType.MongoDB: + return updateMongoDBCollection( + databaseId, + collectionId, + subscriptionId, + resourceGroup, + accountName, + newCollection + ); + case DefaultAccountExperienceType.Cassandra: + return updateCassandraTable(databaseId, collectionId, subscriptionId, resourceGroup, accountName, newCollection); + case DefaultAccountExperienceType.Graph: + return updateGremlinGraph(databaseId, collectionId, subscriptionId, resourceGroup, accountName, newCollection); + case DefaultAccountExperienceType.Table: + return updateTable(collectionId, subscriptionId, resourceGroup, accountName, newCollection); + default: + throw new Error(`Unsupported default experience type: ${defaultExperience}`); + } +} + +async function updateSqlContainer( + databaseId: string, + collectionId: string, + subscriptionId: string, + resourceGroup: string, + accountName: string, + newCollection: Collection +): Promise { + const getResponse = await getSqlContainer(subscriptionId, resourceGroup, accountName, databaseId, collectionId); + if (getResponse && getResponse.properties && getResponse.properties.resource) { + getResponse.properties.resource = newCollection as SqlContainerResource & ExtendedResourceProperties; + const updateResponse = await createUpdateSqlContainer( + subscriptionId, + resourceGroup, + accountName, + databaseId, + collectionId, + getResponse as SqlContainerCreateUpdateParameters + ); + return updateResponse && (updateResponse.properties.resource as Collection); + } + + throw new Error(`Sql container to update does not exist. Database id: ${databaseId} Collection id: ${collectionId}`); +} + +async function updateMongoDBCollection( + databaseId: string, + collectionId: string, + subscriptionId: string, + resourceGroup: string, + accountName: string, + newCollection: Collection +): Promise { + const getResponse = await getMongoDBCollection(subscriptionId, resourceGroup, accountName, databaseId, collectionId); + if (getResponse && getResponse.properties && getResponse.properties.resource) { + getResponse.properties.resource = newCollection as SqlContainerResource & ExtendedResourceProperties; + const updateResponse = await createUpdateMongoDBCollection( + subscriptionId, + resourceGroup, + accountName, + databaseId, + collectionId, + getResponse as SqlContainerCreateUpdateParameters + ); + return updateResponse && (updateResponse.properties.resource as Collection); + } + + throw new Error( + `MongoDB collection to update does not exist. Database id: ${databaseId} Collection id: ${collectionId}` + ); +} + +async function updateCassandraTable( + databaseId: string, + collectionId: string, + subscriptionId: string, + resourceGroup: string, + accountName: string, + newCollection: Collection +): Promise { + const getResponse = await getCassandraTable(subscriptionId, resourceGroup, accountName, databaseId, collectionId); + if (getResponse && getResponse.properties && getResponse.properties.resource) { + getResponse.properties.resource = newCollection as SqlContainerResource & ExtendedResourceProperties; + const updateResponse = await createUpdateCassandraTable( + subscriptionId, + resourceGroup, + accountName, + databaseId, + collectionId, + getResponse as SqlContainerCreateUpdateParameters + ); + return updateResponse && (updateResponse.properties.resource as Collection); + } + + throw new Error( + `Cassandra table to update does not exist. Database id: ${databaseId} Collection id: ${collectionId}` + ); +} + +async function updateGremlinGraph( + databaseId: string, + collectionId: string, + subscriptionId: string, + resourceGroup: string, + accountName: string, + newCollection: Collection +): Promise { + const getResponse = await getGremlinGraph(subscriptionId, resourceGroup, accountName, databaseId, collectionId); + if (getResponse && getResponse.properties && getResponse.properties.resource) { + getResponse.properties.resource = newCollection as SqlContainerResource & ExtendedResourceProperties; + const updateResponse = await createUpdateGremlinGraph( + subscriptionId, + resourceGroup, + accountName, + databaseId, + collectionId, + getResponse as SqlContainerCreateUpdateParameters + ); + return updateResponse && (updateResponse.properties.resource as Collection); + } + + throw new Error(`Gremlin graph to update does not exist. Database id: ${databaseId} Collection id: ${collectionId}`); +} + +async function updateTable( + collectionId: string, + subscriptionId: string, + resourceGroup: string, + accountName: string, + newCollection: Collection +): Promise { + const getResponse = await getTable(subscriptionId, resourceGroup, accountName, collectionId); + if (getResponse && getResponse.properties && getResponse.properties.resource) { + getResponse.properties.resource = newCollection as SqlContainerResource & ExtendedResourceProperties; + const updateResponse = await createUpdateTable( + subscriptionId, + resourceGroup, + accountName, + collectionId, + getResponse as SqlContainerCreateUpdateParameters + ); + return updateResponse && (updateResponse.properties.resource as Collection); + } + + throw new Error(`Table to update does not exist. Table id: ${collectionId}`); +} diff --git a/src/Contracts/DataModels.ts b/src/Contracts/DataModels.ts index fa8a77a69..4abe8a0f4 100644 --- a/src/Contracts/DataModels.ts +++ b/src/Contracts/DataModels.ts @@ -153,7 +153,14 @@ export interface KeyResource { Token: string; } -export interface IndexingPolicy {} +export interface IndexingPolicy { + automatic: boolean; + indexingMode: string; + includedPaths: any; + excludedPaths: any; + compositeIndexes?: any; + spatialIndexes?: any; +} export interface PartitionKey { paths: string[]; diff --git a/src/Explorer/Tabs/SettingsTab.test.ts b/src/Explorer/Tabs/SettingsTab.test.ts index 67242446c..3e3e13b00 100644 --- a/src/Explorer/Tabs/SettingsTab.test.ts +++ b/src/Explorer/Tabs/SettingsTab.test.ts @@ -7,6 +7,7 @@ import Database from "../Tree/Database"; import Explorer from "../Explorer"; import SettingsTab from "../Tabs/SettingsTab"; import { CommandButtonComponentProps } from "../Controls/CommandButton/CommandButtonComponent"; +import { IndexingPolicies } from "../../Shared/Constants"; describe("Settings tab", () => { const baseCollection: DataModels.Collection = { @@ -16,7 +17,7 @@ describe("Settings tab", () => { mode: DataModels.ConflictResolutionMode.LastWriterWins, conflictResolutionPath: "/_ts" }, - indexingPolicy: {}, + indexingPolicy: IndexingPolicies.SharedDatabaseDefault, _rid: "", _self: "", _etag: "", @@ -51,7 +52,7 @@ describe("Settings tab", () => { defaultTtl: 200, partitionKey: null, conflictResolutionPolicy: null, - indexingPolicy: {}, + indexingPolicy: IndexingPolicies.SharedDatabaseDefault, _rid: "", _self: "", _etag: "", @@ -345,7 +346,6 @@ describe("Settings tab", () => { const offer: DataModels.Offer = null; const defaultTtl = 200; - const indexingPolicy = {}; const database = new Database(explorer, baseDatabase, null); const conflictResolutionPolicy = { mode: DataModels.ConflictResolutionMode.LastWriterWins, @@ -367,7 +367,7 @@ describe("Settings tab", () => { } : null, conflictResolutionPolicy: conflictResolutionPolicy, - indexingPolicy: indexingPolicy, + indexingPolicy: IndexingPolicies.SharedDatabaseDefault, _rid: "", _self: "", _etag: "", diff --git a/src/Explorer/Tabs/SettingsTab.ts b/src/Explorer/Tabs/SettingsTab.ts index f3f24fd0a..844a26222 100644 --- a/src/Explorer/Tabs/SettingsTab.ts +++ b/src/Explorer/Tabs/SettingsTab.ts @@ -17,7 +17,8 @@ import { Action } from "../../Shared/Telemetry/TelemetryConstants"; import { PlatformType } from "../../PlatformType"; import { RequestOptions } from "@azure/cosmos/dist-esm"; import Explorer from "../Explorer"; -import { updateOffer, updateCollection } from "../../Common/DocumentClientUtilityBase"; +import { updateOffer } from "../../Common/DocumentClientUtilityBase"; +import { updateCollection } from "../../Common/dataAccess/updateCollection"; import { CommandButtonComponentProps } from "../Controls/CommandButton/CommandButtonComponent"; import { userContext } from "../../UserContext"; import { updateOfferThroughputBeyondLimit } from "../../Common/dataAccess/updateOfferThroughputBeyondLimit"; @@ -1009,8 +1010,7 @@ export default class SettingsTab extends TabsBase implements ViewModels.WaitsFor ); } - public onSaveClick = (): Q.Promise => { - let promises: Q.Promise[] = []; + public onSaveClick = async (): Promise => { this.isExecutionError(false); this.isExecuting(true); @@ -1023,50 +1023,60 @@ export default class SettingsTab extends TabsBase implements ViewModels.WaitsFor const newCollectionAttributes: any = {}; - if (this.shouldUpdateCollection()) { - let defaultTtl: number; - switch (this.timeToLive()) { - case "on": - defaultTtl = Number(this.timeToLiveSeconds()); - break; - case "on-nodefault": - defaultTtl = -1; - break; - case "off": - default: - defaultTtl = undefined; - break; - } + try { + if (this.shouldUpdateCollection()) { + let defaultTtl: number; + switch (this.timeToLive()) { + case "on": + defaultTtl = Number(this.timeToLiveSeconds()); + break; + case "on-nodefault": + defaultTtl = -1; + break; + case "off": + default: + defaultTtl = undefined; + break; + } - newCollectionAttributes.defaultTtl = defaultTtl; + newCollectionAttributes.defaultTtl = defaultTtl; - newCollectionAttributes.indexingPolicy = this.indexingPolicyContent(); + newCollectionAttributes.indexingPolicy = this.indexingPolicyContent(); - newCollectionAttributes.changeFeedPolicy = - this.changeFeedPolicyVisible() && this.changeFeedPolicyToggled() === ChangeFeedPolicyToggledState.On - ? ({ - retentionDuration: Constants.BackendDefaults.maxChangeFeedRetentionDuration - } as DataModels.ChangeFeedPolicy) + newCollectionAttributes.changeFeedPolicy = + this.changeFeedPolicyVisible() && this.changeFeedPolicyToggled() === ChangeFeedPolicyToggledState.On + ? ({ + retentionDuration: Constants.BackendDefaults.maxChangeFeedRetentionDuration + } as DataModels.ChangeFeedPolicy) + : undefined; + + newCollectionAttributes.analyticalStorageTtl = this.isAnalyticalStorageEnabled + ? this.analyticalStorageTtlSelection() === "on" + ? Number(this.analyticalStorageTtlSeconds()) + : Constants.AnalyticalStorageTtl.Infinite : undefined; - newCollectionAttributes.analyticalStorageTtl = this.isAnalyticalStorageEnabled - ? this.analyticalStorageTtlSelection() === "on" - ? Number(this.analyticalStorageTtlSeconds()) - : Constants.AnalyticalStorageTtl.Infinite - : undefined; + newCollectionAttributes.geospatialConfig = { + type: this.geospatialConfigType() + }; - newCollectionAttributes.geospatialConfig = { - type: this.geospatialConfigType() - }; + const conflictResolutionChanges: DataModels.ConflictResolutionPolicy = this.getUpdatedConflictResolutionPolicy(); + if (!!conflictResolutionChanges) { + newCollectionAttributes.conflictResolutionPolicy = conflictResolutionChanges; + } - const conflictResolutionChanges: DataModels.ConflictResolutionPolicy = this.getUpdatedConflictResolutionPolicy(); - if (!!conflictResolutionChanges) { - newCollectionAttributes.conflictResolutionPolicy = conflictResolutionChanges; - } + const newCollection: DataModels.Collection = _.extend( + {}, + this.collection.rawDataModel, + newCollectionAttributes + ); + const updatedCollection: DataModels.Collection = await updateCollection( + this.collection.databaseId, + this.collection.id(), + newCollection + ); - const newCollection: DataModels.Collection = _.extend({}, this.collection.rawDataModel, newCollectionAttributes); - const updateCollectionPromise = updateCollection(this.collection.databaseId, this.collection, newCollection).then( - (updatedCollection: DataModels.Collection) => { + if (updatedCollection) { this.collection.rawDataModel = updatedCollection; this.collection.defaultTtl(updatedCollection.defaultTtl); this.collection.analyticalStorageTtl(updatedCollection.analyticalStorageTtl); @@ -1076,164 +1086,133 @@ export default class SettingsTab extends TabsBase implements ViewModels.WaitsFor this.collection.changeFeedPolicy(updatedCollection.changeFeedPolicy); this.collection.geospatialConfig(updatedCollection.geospatialConfig); } - ); - - promises.push(updateCollectionPromise); - } - - if ( - this.throughput.editableIsDirty() || - this.rupm.editableIsDirty() || - this._isAutoPilotDirty() || - this._hasProvisioningTypeChanged() - ) { - const newThroughput = this.throughput(); - const isRUPerMinuteThroughputEnabled: boolean = this.rupm() === Constants.RUPMStates.on; - let newOffer: DataModels.Offer = _.extend({}, this.collection.offer()); - const originalThroughputValue: number = this.throughput.getEditableOriginalValue(); - - if (newOffer.content) { - newOffer.content.offerThroughput = newThroughput; - newOffer.content.offerIsRUPerMinuteThroughputEnabled = isRUPerMinuteThroughputEnabled; - } else { - newOffer = _.extend({}, newOffer, { - content: { - offerThroughput: newThroughput, - offerIsRUPerMinuteThroughputEnabled: isRUPerMinuteThroughputEnabled - } - }); - } - - const headerOptions: RequestOptions = { initialHeaders: {} }; - - if (this.isAutoPilotSelected()) { - if (!this.hasAutoPilotV2FeatureFlag()) { - newOffer.content.offerAutopilotSettings = { - maxThroughput: this.autoPilotThroughput() - }; - } else { - newOffer.content.offerAutopilotSettings = { - tier: this.selectedAutoPilotTier() - }; - } - - // user has changed from provisioned --> autoscale - if (!this.hasAutoPilotV2FeatureFlag() && this._hasProvisioningTypeChanged()) { - headerOptions.initialHeaders[Constants.HttpHeaders.migrateOfferToAutopilot] = "true"; - delete newOffer.content.offerAutopilotSettings; - } else { - delete newOffer.content.offerThroughput; - } - } else { - this.isAutoPilotSelected(false); - this.userCanChangeProvisioningTypes(false || !this.hasAutoPilotV2FeatureFlag()); - - // user has changed from autoscale --> provisioned - if (!this.hasAutoPilotV2FeatureFlag() && this._hasProvisioningTypeChanged()) { - headerOptions.initialHeaders[Constants.HttpHeaders.migrateOfferToManualThroughput] = "true"; - } else { - delete newOffer.content.offerAutopilotSettings; - } } if ( - this.maxRUs() <= SharedConstants.CollectionCreation.DefaultCollectionRUs1Million && - newThroughput > SharedConstants.CollectionCreation.DefaultCollectionRUs1Million && - this.container != null + this.throughput.editableIsDirty() || + this.rupm.editableIsDirty() || + this._isAutoPilotDirty() || + this._hasProvisioningTypeChanged() ) { - const requestPayload = { - subscriptionId: userContext.subscriptionId, - databaseAccountName: userContext.databaseAccount.name, - resourceGroup: userContext.resourceGroup, - databaseName: this.collection.databaseId, - collectionName: this.collection.id(), - throughput: newThroughput, - offerIsRUPerMinuteThroughputEnabled: isRUPerMinuteThroughputEnabled - }; - const updateOfferBeyondLimitPromise = updateOfferThroughputBeyondLimit(requestPayload).then( - () => { - this.collection.offer().content.offerThroughput = originalThroughputValue; - this.throughput(originalThroughputValue); - this.notificationStatusInfo( - throughputApplyDelayedMessage( - this.isAutoPilotSelected(), - originalThroughputValue, - this._getThroughputUnit(), - this.collection.databaseId, - this.collection.id(), - newThroughput - ) - ); - this.throughput.valueHasMutated(); // force component re-render - }, - (error: any) => { - TelemetryProcessor.traceFailure( - Action.UpdateSettings, - { - databaseAccountName: this.container.databaseAccount().name, - databaseName: this.collection && this.collection.databaseId, - collectionName: this.collection && this.collection.id(), - defaultExperience: this.container.defaultExperience(), - dataExplorerArea: Constants.Areas.Tab, - tabTitle: this.tabTitle(), - error: error - }, - startKey - ); - } - ); - promises.push(Q(updateOfferBeyondLimitPromise)); - } else { - const updateOfferPromise = updateOffer(this.collection.offer(), newOffer, headerOptions).then( - (updatedOffer: DataModels.Offer) => { - this.collection.offer(updatedOffer); - this.collection.offer.valueHasMutated(); - } - ); + const newThroughput = this.throughput(); + const isRUPerMinuteThroughputEnabled: boolean = this.rupm() === Constants.RUPMStates.on; + let newOffer: DataModels.Offer = _.extend({}, this.collection.offer()); + const originalThroughputValue: number = this.throughput.getEditableOriginalValue(); - promises.push(updateOfferPromise); - } - } - - if (promises.length === 0) { - this.isExecuting(false); - } - - return Q.all(promises) - .then( - () => { - this.container.isRefreshingExplorer(false); - this._setBaseline(); - this.collection.readSettings(); - this._wasAutopilotOriginallySet(this.isAutoPilotSelected()); - TelemetryProcessor.traceSuccess( - Action.UpdateSettings, - { - databaseAccountName: this.container.databaseAccount().name, - defaultExperience: this.container.defaultExperience(), - dataExplorerArea: Constants.Areas.Tab, - tabTitle: this.tabTitle() - }, - startKey - ); - }, - (reason: any) => { - this.container.isRefreshingExplorer(false); - this.isExecutionError(true); - console.error(reason); - TelemetryProcessor.traceFailure( - Action.UpdateSettings, - { - databaseAccountName: this.container.databaseAccount().name, - defaultExperience: this.container.defaultExperience(), - dataExplorerArea: Constants.Areas.Tab, - tabTitle: this.tabTitle() - }, - startKey - ); + if (newOffer.content) { + newOffer.content.offerThroughput = newThroughput; + newOffer.content.offerIsRUPerMinuteThroughputEnabled = isRUPerMinuteThroughputEnabled; + } else { + newOffer = _.extend({}, newOffer, { + content: { + offerThroughput: newThroughput, + offerIsRUPerMinuteThroughputEnabled: isRUPerMinuteThroughputEnabled + } + }); } - ) - .finally(() => this.isExecuting(false)); + + const headerOptions: RequestOptions = { initialHeaders: {} }; + + if (this.isAutoPilotSelected()) { + if (!this.hasAutoPilotV2FeatureFlag()) { + newOffer.content.offerAutopilotSettings = { + maxThroughput: this.autoPilotThroughput() + }; + } else { + newOffer.content.offerAutopilotSettings = { + tier: this.selectedAutoPilotTier() + }; + } + + // user has changed from provisioned --> autoscale + if (!this.hasAutoPilotV2FeatureFlag() && this._hasProvisioningTypeChanged()) { + headerOptions.initialHeaders[Constants.HttpHeaders.migrateOfferToAutopilot] = "true"; + delete newOffer.content.offerAutopilotSettings; + } else { + delete newOffer.content.offerThroughput; + } + } else { + this.isAutoPilotSelected(false); + this.userCanChangeProvisioningTypes(false || !this.hasAutoPilotV2FeatureFlag()); + + // user has changed from autoscale --> provisioned + if (!this.hasAutoPilotV2FeatureFlag() && this._hasProvisioningTypeChanged()) { + headerOptions.initialHeaders[Constants.HttpHeaders.migrateOfferToManualThroughput] = "true"; + } else { + delete newOffer.content.offerAutopilotSettings; + } + } + + if ( + this.maxRUs() <= SharedConstants.CollectionCreation.DefaultCollectionRUs1Million && + newThroughput > SharedConstants.CollectionCreation.DefaultCollectionRUs1Million && + this.container != null + ) { + const requestPayload = { + subscriptionId: userContext.subscriptionId, + databaseAccountName: userContext.databaseAccount.name, + resourceGroup: userContext.resourceGroup, + databaseName: this.collection.databaseId, + collectionName: this.collection.id(), + throughput: newThroughput, + offerIsRUPerMinuteThroughputEnabled: isRUPerMinuteThroughputEnabled + }; + + await updateOfferThroughputBeyondLimit(requestPayload); + this.collection.offer().content.offerThroughput = originalThroughputValue; + this.throughput(originalThroughputValue); + this.notificationStatusInfo( + throughputApplyDelayedMessage( + this.isAutoPilotSelected(), + originalThroughputValue, + this._getThroughputUnit(), + this.collection.databaseId, + this.collection.id(), + newThroughput + ) + ); + this.throughput.valueHasMutated(); // force component re-render + } else { + const updatedOffer: DataModels.Offer = await updateOffer(this.collection.offer(), newOffer, headerOptions); + this.collection.offer(updatedOffer); + this.collection.offer.valueHasMutated(); + } + } + + this.container.isRefreshingExplorer(false); + this._setBaseline(); + this.collection.readSettings(); + this._wasAutopilotOriginallySet(this.isAutoPilotSelected()); + TelemetryProcessor.traceSuccess( + Action.UpdateSettings, + { + databaseAccountName: this.container.databaseAccount().name, + defaultExperience: this.container.defaultExperience(), + dataExplorerArea: Constants.Areas.Tab, + tabTitle: this.tabTitle() + }, + startKey + ); + } catch (error) { + this.container.isRefreshingExplorer(false); + this.isExecutionError(true); + console.error(error); + TelemetryProcessor.traceFailure( + Action.UpdateSettings, + { + databaseAccountName: this.container.databaseAccount().name, + databaseName: this.collection && this.collection.databaseId, + collectionName: this.collection && this.collection.id(), + defaultExperience: this.container.defaultExperience(), + dataExplorerArea: Constants.Areas.Tab, + tabTitle: this.tabTitle(), + error: error + }, + startKey + ); + } + + this.isExecuting(false); }; public onRevertClick = (): Q.Promise => {