From 455a6ac81ba95d19c011200d464671b6b9eccd0f Mon Sep 17 00:00:00 2001 From: Steve Faulkner Date: Thu, 6 Aug 2020 18:15:40 -0500 Subject: [PATCH] Fix update offer beyond throughput limit error (#135) --- src/Common/DataAccessUtilityBase.ts | 24 --------- src/Common/DocumentClientUtilityBase.ts | 34 ------------ .../updateOfferThroughputBeyondLimit.test.ts | 26 ++++++++++ .../updateOfferThroughputBeyondLimit.ts | 52 +++++++++++++++++++ src/Contracts/DataModels.ts | 11 ---- src/Explorer/Tabs/DatabaseSettingsTab.ts | 10 ++-- src/Explorer/Tabs/SettingsTab.ts | 13 ++--- 7 files changed, 88 insertions(+), 82 deletions(-) create mode 100644 src/Common/dataAccess/updateOfferThroughputBeyondLimit.test.ts create mode 100644 src/Common/dataAccess/updateOfferThroughputBeyondLimit.ts diff --git a/src/Common/DataAccessUtilityBase.ts b/src/Common/DataAccessUtilityBase.ts index 44dfd69ee..4ed13f883 100644 --- a/src/Common/DataAccessUtilityBase.ts +++ b/src/Common/DataAccessUtilityBase.ts @@ -26,7 +26,6 @@ import { OfferUtils } from "../Utils/OfferUtils"; import { RequestOptions } from "@azure/cosmos/dist-esm"; import StoredProcedure from "../Explorer/Tree/StoredProcedure"; import { Platform, configContext } from "../ConfigContext"; -import { getAuthorizationHeader } from "../Utils/AuthorizationUtils"; import DocumentId from "../Explorer/Tree/DocumentId"; import ConflictId from "../Explorer/Tree/ConflictId"; @@ -640,29 +639,6 @@ export function queryConflicts( return Q(documentsIterator); } -export async function updateOfferThroughputBeyondLimit( - request: DataModels.UpdateOfferThroughputRequest -): Promise { - if (configContext.platform !== Platform.Portal) { - throw new Error("Updating throughput beyond specified limit is not supported on this platform"); - } - - const explorer = window.dataExplorer; - const url = `${explorer.extensionEndpoint()}/api/offerthroughputrequest/updatebeyondspecifiedlimit`; - const authorizationHeader = getAuthorizationHeader(); - - const response = await fetch(url, { - method: "POST", - body: JSON.stringify(request), - headers: { [authorizationHeader.header]: authorizationHeader.token } - }); - - if (response.ok) { - return undefined; - } - throw new Error(await response.text()); -} - function _createDatabase(request: DataModels.CreateDatabaseRequest, options: any = {}): Q.Promise { const { databaseId, databaseLevelThroughput, offerThroughput, autoPilot, hasAutoPilotV2FeatureFlag } = request; const createBody: DatabaseRequest = { id: databaseId }; diff --git a/src/Common/DocumentClientUtilityBase.ts b/src/Common/DocumentClientUtilityBase.ts index 57742dc67..05a6ff017 100644 --- a/src/Common/DocumentClientUtilityBase.ts +++ b/src/Common/DocumentClientUtilityBase.ts @@ -383,40 +383,6 @@ export function updateOffer( return deferred.promise; } -export function updateOfferThroughputBeyondLimit( - requestPayload: DataModels.UpdateOfferThroughputRequest -): Q.Promise { - const deferred: Q.Deferred = Q.defer(); - const resourceDescriptionInfo: string = requestPayload.collectionName - ? `database ${requestPayload.databaseName} and container ${requestPayload.collectionName}` - : `database ${requestPayload.databaseName}`; - const id = NotificationConsoleUtils.logConsoleMessage( - ConsoleDataType.InProgress, - `Requesting increase in throughput to ${requestPayload.throughput} for ${resourceDescriptionInfo}` - ); - DataAccessUtilityBase.updateOfferThroughputBeyondLimit(requestPayload) - .then( - () => { - NotificationConsoleUtils.logConsoleMessage( - ConsoleDataType.Info, - `Successfully requested an increase in throughput to ${requestPayload.throughput} for ${resourceDescriptionInfo}` - ); - deferred.resolve(); - }, - (error: any) => { - NotificationConsoleUtils.logConsoleMessage( - ConsoleDataType.Error, - `Failed to request an increase in throughput for ${requestPayload.throughput}: ${JSON.stringify(error)}` - ); - sendNotificationForError(error); - deferred.reject(error); - } - ) - .finally(() => NotificationConsoleUtils.clearInProgressMessageWithId(id)); - - return deferred.promise.timeout(Constants.ClientDefaults.requestTimeoutMs); -} - export function updateStoredProcedure( collection: ViewModels.Collection, storedProcedure: DataModels.StoredProcedure, diff --git a/src/Common/dataAccess/updateOfferThroughputBeyondLimit.test.ts b/src/Common/dataAccess/updateOfferThroughputBeyondLimit.test.ts new file mode 100644 index 000000000..93d6add04 --- /dev/null +++ b/src/Common/dataAccess/updateOfferThroughputBeyondLimit.test.ts @@ -0,0 +1,26 @@ +import { updateOfferThroughputBeyondLimit } from "./updateOfferThroughputBeyondLimit"; + +describe("updateOfferThroughputBeyondLimit", () => { + it("should call fetch", async () => { + window.fetch = jest.fn(() => { + return { + ok: true + }; + }); + window.dataExplorer = { + logConsoleData: jest.fn(), + deleteInProgressConsoleDataWithId: jest.fn(), + extensionEndpoint: jest.fn() + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } as any; + await updateOfferThroughputBeyondLimit({ + subscriptionId: "foo", + resourceGroup: "foo", + databaseAccountName: "foo", + databaseName: "foo", + throughput: 1000000000, + offerIsRUPerMinuteThroughputEnabled: false + }); + expect(window.fetch).toHaveBeenCalled(); + }); +}); diff --git a/src/Common/dataAccess/updateOfferThroughputBeyondLimit.ts b/src/Common/dataAccess/updateOfferThroughputBeyondLimit.ts new file mode 100644 index 000000000..43fcc849f --- /dev/null +++ b/src/Common/dataAccess/updateOfferThroughputBeyondLimit.ts @@ -0,0 +1,52 @@ +import { Platform, configContext } from "../../ConfigContext"; +import { getAuthorizationHeader } from "../../Utils/AuthorizationUtils"; +import { AutoPilotOfferSettings } from "../../Contracts/DataModels"; +import { logConsoleProgress, logConsoleInfo, logConsoleError } from "../../Utils/NotificationConsoleUtils"; +import { HttpHeaders } from "../Constants"; + +interface UpdateOfferThroughputRequest { + subscriptionId: string; + resourceGroup: string; + databaseAccountName: string; + databaseName: string; + collectionName?: string; + throughput: number; + offerIsRUPerMinuteThroughputEnabled: boolean; + offerAutopilotSettings?: AutoPilotOfferSettings; +} + +export async function updateOfferThroughputBeyondLimit(request: UpdateOfferThroughputRequest): Promise { + if (configContext.platform !== Platform.Portal) { + throw new Error("Updating throughput beyond specified limit is not supported on this platform"); + } + + const resourceDescriptionInfo = request.collectionName + ? `database ${request.databaseName} and container ${request.collectionName}` + : `database ${request.databaseName}`; + + const clearMessage = logConsoleProgress( + `Requesting increase in throughput to ${request.throughput} for ${resourceDescriptionInfo}` + ); + + const explorer = window.dataExplorer; + const url = `${explorer.extensionEndpoint()}/api/offerthroughputrequest/updatebeyondspecifiedlimit`; + const authorizationHeader = getAuthorizationHeader(); + + const response = await fetch(url, { + method: "POST", + body: JSON.stringify(request), + headers: { [authorizationHeader.header]: authorizationHeader.token, [HttpHeaders.contentType]: "application/json" } + }); + + if (response.ok) { + logConsoleInfo( + `Successfully requested an increase in throughput to ${request.throughput} for ${resourceDescriptionInfo}` + ); + clearMessage(); + return undefined; + } + const error = await response.json(); + logConsoleError(`Failed to request an increase in throughput for ${request.throughput}: ${error.message}`); + clearMessage(); + throw new Error(error.message); +} diff --git a/src/Contracts/DataModels.ts b/src/Contracts/DataModels.ts index 85b767204..fa8a77a69 100644 --- a/src/Contracts/DataModels.ts +++ b/src/Contracts/DataModels.ts @@ -312,17 +312,6 @@ export interface Query { query: string; } -export interface UpdateOfferThroughputRequest { - subscriptionId: string; - resourceGroup: string; - databaseAccountName: string; - databaseName: string; - collectionName: string; - throughput: number; - offerIsRUPerMinuteThroughputEnabled: boolean; - offerAutopilotSettings?: AutoPilotOfferSettings; -} - export interface AutoPilotOfferSettings { tier?: AutopilotTier; maximumTierThroughput?: number; diff --git a/src/Explorer/Tabs/DatabaseSettingsTab.ts b/src/Explorer/Tabs/DatabaseSettingsTab.ts index 053857bb8..75915b25b 100644 --- a/src/Explorer/Tabs/DatabaseSettingsTab.ts +++ b/src/Explorer/Tabs/DatabaseSettingsTab.ts @@ -16,9 +16,10 @@ import { Action } from "../../Shared/Telemetry/TelemetryConstants"; import { PlatformType } from "../../PlatformType"; import { RequestOptions } from "@azure/cosmos/dist-esm"; import Explorer from "../Explorer"; -import { updateOfferThroughputBeyondLimit, updateOffer } from "../../Common/DocumentClientUtilityBase"; +import { updateOffer } from "../../Common/DocumentClientUtilityBase"; import { CommandButtonComponentProps } from "../Controls/CommandButton/CommandButtonComponent"; import { userContext } from "../../UserContext"; +import { updateOfferThroughputBeyondLimit } from "../../Common/dataAccess/updateOfferThroughputBeyondLimit"; const updateThroughputBeyondLimitWarningMessage: string = ` You are about to request an increase in throughput beyond the pre-allocated capacity. @@ -519,16 +520,15 @@ export default class DatabaseSettingsTab extends TabsBase implements ViewModels. this.maxRUs() <= SharedConstants.CollectionCreation.DefaultCollectionRUs1Million && this.throughput() > SharedConstants.CollectionCreation.DefaultCollectionRUs1Million ) { - const requestPayload: DataModels.UpdateOfferThroughputRequest = { + const requestPayload = { subscriptionId: userContext.subscriptionId, databaseAccountName: userContext.databaseAccount.name, resourceGroup: userContext.resourceGroup, databaseName: this.database.id(), - collectionName: undefined, throughput: newThroughput, offerIsRUPerMinuteThroughputEnabled: false }; - const updateOfferBeyondLimitPromise: Q.Promise = updateOfferThroughputBeyondLimit(requestPayload).then( + const updateOfferBeyondLimitPromise = updateOfferThroughputBeyondLimit(requestPayload).then( () => { this.database.offer().content.offerThroughput = originalThroughputValue; this.throughput(originalThroughputValue); @@ -552,7 +552,7 @@ export default class DatabaseSettingsTab extends TabsBase implements ViewModels. ); } ); - promises.push(updateOfferBeyondLimitPromise); + promises.push(Q(updateOfferBeyondLimitPromise)); } else { const newOffer: DataModels.Offer = { content: { diff --git a/src/Explorer/Tabs/SettingsTab.ts b/src/Explorer/Tabs/SettingsTab.ts index b7fd486a8..3a6d2de10 100644 --- a/src/Explorer/Tabs/SettingsTab.ts +++ b/src/Explorer/Tabs/SettingsTab.ts @@ -17,13 +17,10 @@ import { Action } from "../../Shared/Telemetry/TelemetryConstants"; import { PlatformType } from "../../PlatformType"; import { RequestOptions } from "@azure/cosmos/dist-esm"; import Explorer from "../Explorer"; -import { - updateOfferThroughputBeyondLimit, - updateOffer, - updateCollection -} from "../../Common/DocumentClientUtilityBase"; +import { updateOffer, updateCollection } from "../../Common/DocumentClientUtilityBase"; import { CommandButtonComponentProps } from "../Controls/CommandButton/CommandButtonComponent"; import { userContext } from "../../UserContext"; +import { updateOfferThroughputBeyondLimit } from "../../Common/dataAccess/updateOfferThroughputBeyondLimit"; const ttlWarning: string = ` 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. @@ -1144,7 +1141,7 @@ export default class SettingsTab extends TabsBase implements ViewModels.WaitsFor newThroughput > SharedConstants.CollectionCreation.DefaultCollectionRUs1Million && this.container != null ) { - const requestPayload: DataModels.UpdateOfferThroughputRequest = { + const requestPayload = { subscriptionId: userContext.subscriptionId, databaseAccountName: userContext.databaseAccount.name, resourceGroup: userContext.resourceGroup, @@ -1153,7 +1150,7 @@ export default class SettingsTab extends TabsBase implements ViewModels.WaitsFor throughput: newThroughput, offerIsRUPerMinuteThroughputEnabled: isRUPerMinuteThroughputEnabled }; - const updateOfferBeyondLimitPromise: Q.Promise = updateOfferThroughputBeyondLimit(requestPayload).then( + const updateOfferBeyondLimitPromise = updateOfferThroughputBeyondLimit(requestPayload).then( () => { this.collection.offer().content.offerThroughput = originalThroughputValue; this.throughput(originalThroughputValue); @@ -1185,7 +1182,7 @@ export default class SettingsTab extends TabsBase implements ViewModels.WaitsFor ); } ); - promises.push(updateOfferBeyondLimitPromise); + promises.push(Q(updateOfferBeyondLimitPromise)); } else { const updateOfferPromise = updateOffer(this.collection.offer(), newOffer, headerOptions).then( (updatedOffer: DataModels.Offer) => {