diff --git a/src/Explorer/Panes/DeleteDatabaseConfirmationPane.ts b/src/Explorer/Panes/DeleteDatabaseConfirmationPane.ts index cfef459e4..ff1ae42c9 100644 --- a/src/Explorer/Panes/DeleteDatabaseConfirmationPane.ts +++ b/src/Explorer/Panes/DeleteDatabaseConfirmationPane.ts @@ -13,6 +13,7 @@ import DeleteFeedback from "../../Common/DeleteFeedback"; import * as NotificationConsoleUtils from "../../Utils/NotificationConsoleUtils"; import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor"; import { deleteDatabase } from "../../Common/dataAccess/deleteDatabase"; +import { ARMError } from "../../Utils/arm/request"; export default class DeleteDatabaseConfirmationPane extends ContextualPaneBase { public databaseIdConfirmationText: ko.Observable; @@ -105,11 +106,12 @@ export default class DeleteDatabaseConfirmationPane extends ContextualPaneBase { this.databaseDeleteFeedback(""); } }, - (reason: any) => { + (reason: unknown) => { this.isExecuting(false); - const message = ErrorParserUtility.parse(reason); - this.formErrors(message[0].message); - this.formErrorsDetails(message[0].message); + + const message = reason instanceof ARMError ? reason.message : ErrorParserUtility.parse(reason)[0].message; + this.formErrors(message); + this.formErrorsDetails(message); TelemetryProcessor.traceFailure( Action.DeleteDatabase, { diff --git a/src/Utils/arm/request.ts b/src/Utils/arm/request.ts index 857841463..0394cfe74 100644 --- a/src/Utils/arm/request.ts +++ b/src/Utils/arm/request.ts @@ -6,11 +6,30 @@ Instead, generate ARM clients that consume this function with stricter typing. */ import promiseRetry, { AbortError } from "p-retry"; -import { ErrorResponse } from "./generatedClients/2020-04-01/types"; import { userContext } from "../../UserContext"; -interface ARMError extends Error { +interface ErrorResponse { code: string; + message: string; +} + +// ARM sometimes returns an error wrapped in a top level error object +// Example: 409 Conflict error when trying to delete a locked resource +interface WrappedErrorResponse { + error: ErrorResponse; +} + +type ParsedErrorResponse = ErrorResponse | WrappedErrorResponse; + +export class ARMError extends Error { + constructor(message: string) { + super(message); + // Set the prototype explicitly. + // https://github.com/Microsoft/TypeScript/wiki/FAQ#why-doesnt-extending-built-ins-like-error-array-and-map-work + Object.setPrototypeOf(this, ARMError.prototype); + } + + public code: string | number; } interface Options { @@ -33,9 +52,20 @@ export async function armRequest({ host, path, apiVersion, method, body: requ body: requestBody ? JSON.stringify(requestBody) : undefined }); if (!response.ok) { - const errorResponse = (await response.json()) as ErrorResponse; - const error = new Error(errorResponse.message) as ARMError; - error.code = errorResponse.code; + let error: ARMError; + try { + const errorResponse = (await response.json()) as ParsedErrorResponse; + if ("error" in errorResponse) { + error = new ARMError(errorResponse.error.message); + error.code = errorResponse.error.code; + } else { + error = new ARMError(errorResponse.message); + error.code = errorResponse.code; + } + } catch (error) { + throw new Error(await response.text()); + } + throw error; }