From 136946a4b497fa79a849e7d9112cc64199f3903b Mon Sep 17 00:00:00 2001 From: Igor Krasavin Date: Mon, 20 Apr 2020 18:11:19 +0300 Subject: [PATCH] Specify known errors (#713) --- src/script/acquisition-sdk.ts | 13 ++++--- src/script/management-sdk.ts | 69 ++++++++++++++++++----------------- src/test/acquisition-sdk.ts | 4 ++ src/utils/code-push-error.ts | 34 +++++++++++++++++ 4 files changed, 80 insertions(+), 40 deletions(-) create mode 100644 src/utils/code-push-error.ts diff --git a/src/script/acquisition-sdk.ts b/src/script/acquisition-sdk.ts index 731d099b..db9ac917 100644 --- a/src/script/acquisition-sdk.ts +++ b/src/script/acquisition-sdk.ts @@ -1,4 +1,5 @@ import { UpdateCheckResponse, UpdateCheckRequest, DeploymentStatusReport, DownloadReport } from "rest-definitions"; +import { CodePushHttpError, CodePushDeployStatusError, CodePushPackageError } from "../utils/code-push-error" export module Http { export const enum Verb { @@ -81,7 +82,7 @@ export class AcquisitionManager { public queryUpdateWithCurrentPackage(currentPackage: Package, callback?: Callback): void { if (!currentPackage || !currentPackage.appVersion) { - throw new Error("Calling common acquisition SDK with incorrect package"); // Unexpected; indicates error in our implementation + throw new CodePushPackageError("Calling common acquisition SDK with incorrect package"); // Unexpected; indicates error in our implementation } var updateRequest: UpdateCheckRequest = { @@ -108,7 +109,7 @@ export class AcquisitionManager { } else { errorMessage = `${response.statusCode}: ${response.body}`; } - callback(new Error(errorMessage), /*remotePackage=*/ null); + callback(new CodePushHttpError(errorMessage), /*remotePackage=*/ null); return; } try { @@ -169,9 +170,9 @@ export class AcquisitionManager { default: if (callback) { if (!status) { - callback(new Error("Missing status argument."), /*not used*/ null); + callback(new CodePushDeployStatusError("Missing status argument."), /*not used*/ null); } else { - callback(new Error("Unrecognized status \"" + status + "\"."), /*not used*/ null); + callback(new CodePushDeployStatusError("Unrecognized status \"" + status + "\"."), /*not used*/ null); } } return; @@ -196,7 +197,7 @@ export class AcquisitionManager { } if (response.statusCode !== 200) { - callback(new Error(response.statusCode + ": " + response.body), /*not used*/ null); + callback(new CodePushHttpError(response.statusCode + ": " + response.body), /*not used*/ null); return; } @@ -221,7 +222,7 @@ export class AcquisitionManager { } if (response.statusCode !== 200) { - callback(new Error(response.statusCode + ": " + response.body), /*not used*/ null); + callback(new CodePushHttpError(response.statusCode + ": " + response.body), /*not used*/ null); return; } diff --git a/src/script/management-sdk.ts b/src/script/management-sdk.ts index 4ad4faf5..9e53f6f8 100644 --- a/src/script/management-sdk.ts +++ b/src/script/management-sdk.ts @@ -6,6 +6,7 @@ import slash = require("slash"); import superagent = require("superagent"); import * as recursiveFs from "recursive-fs"; import * as yazl from "yazl"; +import { CodePushUnauthorizedError } from "../utils/code-push-error" import Promise = Q.Promise; @@ -61,7 +62,7 @@ class AccountManager { private _proxy: string; constructor(accessKey: string, customHeaders?: Headers, serverUrl?: string, proxy?: string) { - if (!accessKey) throw new Error("A token must be specified."); + if (!accessKey) throw new CodePushUnauthorizedError("A token must be specified."); this._accessKey = accessKey; this._customHeaders = customHeaders; @@ -75,7 +76,7 @@ class AccountManager { public isAuthenticated(throwIfUnauthorized?: boolean): Promise { return Promise((resolve, reject, notify) => { - var request: superagent.Request = superagent.get(this._serverUrl + urlEncode `/authenticated`); + var request: superagent.Request = superagent.get(this._serverUrl + urlEncode`/authenticated`); if (this._proxy) (request).proxy(this._proxy); this.attachCredentials(request); @@ -88,7 +89,7 @@ class AccountManager { var authenticated: boolean = status === 200; - if (!authenticated && throwIfUnauthorized){ + if (!authenticated && throwIfUnauthorized) { reject(this.getCodePushError(err, res)); return; } @@ -100,7 +101,7 @@ class AccountManager { public addAccessKey(friendlyName: string, ttl?: number): Promise { if (!friendlyName) { - throw new Error("A name must be specified when adding an access key."); + throw new CodePushUnauthorizedError("A name must be specified when adding an access key."); } var accessKeyRequest: AccessKeyRequest = { @@ -109,7 +110,7 @@ class AccountManager { ttl }; - return this.post(urlEncode `/accessKeys/`, JSON.stringify(accessKeyRequest), /*expectResponseBody=*/ true) + return this.post(urlEncode`/accessKeys/`, JSON.stringify(accessKeyRequest), /*expectResponseBody=*/ true) .then((response: JsonResponse) => { return { createdTime: response.body.accessKey.createdTime, @@ -121,7 +122,7 @@ class AccountManager { } public getAccessKey(accessKeyName: string): Promise { - return this.get(urlEncode `/accessKeys/${accessKeyName}`) + return this.get(urlEncode`/accessKeys/${accessKeyName}`) .then((res: JsonResponse) => { return { createdTime: res.body.accessKey.createdTime, @@ -132,7 +133,7 @@ class AccountManager { } public getAccessKeys(): Promise { - return this.get(urlEncode `/accessKeys`) + return this.get(urlEncode`/accessKeys`) .then((res: JsonResponse) => { var accessKeys: AccessKey[] = []; @@ -149,7 +150,7 @@ class AccountManager { } public getSessions(): Promise { - return this.get(urlEncode `/accessKeys`) + return this.get(urlEncode`/accessKeys`) .then((res: JsonResponse) => { // A machine name might be associated with multiple session keys, // but we should only return one per machine name. @@ -178,7 +179,7 @@ class AccountManager { ttl }; - return this.patch(urlEncode `/accessKeys/${oldName}`, JSON.stringify(accessKeyRequest)) + return this.patch(urlEncode`/accessKeys/${oldName}`, JSON.stringify(accessKeyRequest)) .then((res: JsonResponse) => { return { createdTime: res.body.accessKey.createdTime, @@ -189,29 +190,29 @@ class AccountManager { } public removeAccessKey(name: string): Promise { - return this.del(urlEncode `/accessKeys/${name}`) + return this.del(urlEncode`/accessKeys/${name}`) .then(() => null); } public removeSession(machineName: string): Promise { - return this.del(urlEncode `/sessions/${machineName}`) + return this.del(urlEncode`/sessions/${machineName}`) .then(() => null); } // Account public getAccountInfo(): Promise { - return this.get(urlEncode `/account`) + return this.get(urlEncode`/account`) .then((res: JsonResponse) => res.body.account); } // Apps public getApps(): Promise { - return this.get(urlEncode `/apps`) + return this.get(urlEncode`/apps`) .then((res: JsonResponse) => res.body.apps); } public getApp(appName: string): Promise { - return this.get(urlEncode `/apps/${this.appNameParam(appName)}`) + return this.get(urlEncode`/apps/${this.appNameParam(appName)}`) .then((res: JsonResponse) => res.body.app); } @@ -222,80 +223,80 @@ class AccountManager { platform: appPlatform, manuallyProvisionDeployments: manuallyProvisionDeployments }; - return this.post(urlEncode `/apps/`, JSON.stringify(app), /*expectResponseBody=*/ false) + return this.post(urlEncode`/apps/`, JSON.stringify(app), /*expectResponseBody=*/ false) .then(() => app); } public removeApp(appName: string): Promise { - return this.del(urlEncode `/apps/${this.appNameParam(appName)}`) + return this.del(urlEncode`/apps/${this.appNameParam(appName)}`) .then(() => null); } public renameApp(oldAppName: string, newAppName: string): Promise { - return this.patch(urlEncode `/apps/${this.appNameParam(oldAppName)}`, JSON.stringify({ name: newAppName })) + return this.patch(urlEncode`/apps/${this.appNameParam(oldAppName)}`, JSON.stringify({ name: newAppName })) .then(() => null); } public transferApp(appName: string, email: string): Promise { - return this.post(urlEncode `/apps/${this.appNameParam(appName)}/transfer/${email}`, /*requestBody=*/ null, /*expectResponseBody=*/ false) + return this.post(urlEncode`/apps/${this.appNameParam(appName)}/transfer/${email}`, /*requestBody=*/ null, /*expectResponseBody=*/ false) .then(() => null); } // Collaborators public getCollaborators(appName: string): Promise { - return this.get(urlEncode `/apps/${this.appNameParam(appName)}/collaborators`) + return this.get(urlEncode`/apps/${this.appNameParam(appName)}/collaborators`) .then((res: JsonResponse) => res.body.collaborators); } public addCollaborator(appName: string, email: string): Promise { - return this.post(urlEncode `/apps/${this.appNameParam(appName)}/collaborators/${email}`, /*requestBody=*/ null, /*expectResponseBody=*/ false) + return this.post(urlEncode`/apps/${this.appNameParam(appName)}/collaborators/${email}`, /*requestBody=*/ null, /*expectResponseBody=*/ false) .then(() => null); } public removeCollaborator(appName: string, email: string): Promise { - return this.del(urlEncode `/apps/${this.appNameParam(appName)}/collaborators/${email}`) + return this.del(urlEncode`/apps/${this.appNameParam(appName)}/collaborators/${email}`) .then(() => null); } // Deployments public addDeployment(appName: string, deploymentName: string): Promise { var deployment = { name: deploymentName }; - return this.post(urlEncode `/apps/${this.appNameParam(appName)}/deployments/`, JSON.stringify(deployment), /*expectResponseBody=*/ true) + return this.post(urlEncode`/apps/${this.appNameParam(appName)}/deployments/`, JSON.stringify(deployment), /*expectResponseBody=*/ true) .then((res: JsonResponse) => res.body.deployment); } public clearDeploymentHistory(appName: string, deploymentName: string): Promise { - return this.del(urlEncode `/apps/${this.appNameParam(appName)}/deployments/${deploymentName}/history`) + return this.del(urlEncode`/apps/${this.appNameParam(appName)}/deployments/${deploymentName}/history`) .then(() => null); } public getDeployments(appName: string): Promise { - return this.get(urlEncode `/apps/${this.appNameParam(appName)}/deployments/`) + return this.get(urlEncode`/apps/${this.appNameParam(appName)}/deployments/`) .then((res: JsonResponse) => res.body.deployments); } public getDeployment(appName: string, deploymentName: string): Promise { - return this.get(urlEncode `/apps/${this.appNameParam(appName)}/deployments/${deploymentName}`) + return this.get(urlEncode`/apps/${this.appNameParam(appName)}/deployments/${deploymentName}`) .then((res: JsonResponse) => res.body.deployment); } public renameDeployment(appName: string, oldDeploymentName: string, newDeploymentName: string): Promise { - return this.patch(urlEncode `/apps/${this.appNameParam(appName)}/deployments/${oldDeploymentName}`, JSON.stringify({ name: newDeploymentName })) + return this.patch(urlEncode`/apps/${this.appNameParam(appName)}/deployments/${oldDeploymentName}`, JSON.stringify({ name: newDeploymentName })) .then(() => null); } public removeDeployment(appName: string, deploymentName: string): Promise { - return this.del(urlEncode `/apps/${this.appNameParam(appName)}/deployments/${deploymentName}`) + return this.del(urlEncode`/apps/${this.appNameParam(appName)}/deployments/${deploymentName}`) .then(() => null); } public getDeploymentMetrics(appName: string, deploymentName: string): Promise { - return this.get(urlEncode `/apps/${this.appNameParam(appName)}/deployments/${deploymentName}/metrics`) + return this.get(urlEncode`/apps/${this.appNameParam(appName)}/deployments/${deploymentName}/metrics`) .then((res: JsonResponse) => res.body.metrics); } public getDeploymentHistory(appName: string, deploymentName: string): Promise { - return this.get(urlEncode `/apps/${this.appNameParam(appName)}/deployments/${deploymentName}/history`) + return this.get(urlEncode`/apps/${this.appNameParam(appName)}/deployments/${deploymentName}/history`) .then((res: JsonResponse) => res.body.history); } @@ -304,7 +305,7 @@ class AccountManager { return Promise((resolve, reject, notify) => { updateMetadata.appVersion = targetBinaryVersion; - var request: superagent.Request = superagent.post(this._serverUrl + urlEncode `/apps/${this.appNameParam(appName)}/deployments/${deploymentName}/release`); + var request: superagent.Request = superagent.post(this._serverUrl + urlEncode`/apps/${this.appNameParam(appName)}/deployments/${deploymentName}/release`); if (this._proxy) (request).proxy(this._proxy); this.attachCredentials(request); @@ -351,18 +352,18 @@ class AccountManager { public patchRelease(appName: string, deploymentName: string, label: string, updateMetadata: PackageInfo): Promise { updateMetadata.label = label; var requestBody: string = JSON.stringify({ packageInfo: updateMetadata }); - return this.patch(urlEncode `/apps/${this.appNameParam(appName)}/deployments/${deploymentName}/release`, requestBody, /*expectResponseBody=*/ false) + return this.patch(urlEncode`/apps/${this.appNameParam(appName)}/deployments/${deploymentName}/release`, requestBody, /*expectResponseBody=*/ false) .then(() => null); } - public promote(appName: string, sourceDeploymentName: string, destinationDeploymentName: string, updateMetadata: PackageInfo): Promise { + public promote(appName: string, sourceDeploymentName: string, destinationDeploymentName: string, updateMetadata: PackageInfo): Promise { var requestBody: string = JSON.stringify({ packageInfo: updateMetadata }); - return this.post(urlEncode `/apps/${this.appNameParam(appName)}/deployments/${sourceDeploymentName}/promote/${destinationDeploymentName}`, requestBody, /*expectResponseBody=*/ true) + return this.post(urlEncode`/apps/${this.appNameParam(appName)}/deployments/${sourceDeploymentName}/promote/${destinationDeploymentName}`, requestBody, /*expectResponseBody=*/ true) .then((res: JsonResponse) => res.body.package); } public rollback(appName: string, deploymentName: string, targetRelease?: string): Promise { - return this.post(urlEncode `/apps/${this.appNameParam(appName)}/deployments/${deploymentName}/rollback/${targetRelease || ``}`, /*requestBody=*/ null, /*expectResponseBody=*/ false) + return this.post(urlEncode`/apps/${this.appNameParam(appName)}/deployments/${deploymentName}/rollback/${targetRelease || ``}`, /*requestBody=*/ null, /*expectResponseBody=*/ false) .then(() => null); } diff --git a/src/test/acquisition-sdk.ts b/src/test/acquisition-sdk.ts index 61b99230..a5989629 100644 --- a/src/test/acquisition-sdk.ts +++ b/src/test/acquisition-sdk.ts @@ -3,6 +3,7 @@ import * as assert from "assert"; import * as acquisitionSdk from "../script/acquisition-sdk"; import * as acquisitionRestMock from "./acquisition-rest-mock"; import * as rest from "rest-definitions"; +import { CodePushPackageError } from "../utils/code-push-error" const mockApi = acquisitionRestMock; var latestPackage: rest.UpdateCheckResponse = clone(mockApi.latestPackage); @@ -162,6 +163,7 @@ describe("Acquisition SDK", () => { it("If invalid arguments are provided, an error is raised", (done: MochaDone) => { var invalidPackage: acquisitionSdk.Package = clone(templateCurrentPackage); invalidPackage.appVersion = null; + var expectedError = new CodePushPackageError("Calling common acquisition SDK with incorrect package") var acquisition = new acquisitionSdk.AcquisitionManager(new mockApi.HttpRequester(), configuration); try { @@ -170,6 +172,8 @@ describe("Acquisition SDK", () => { done(); }); } catch (error) { + assert.deepEqual(error, expectedError); + assert.equal(error instanceof CodePushPackageError, true) done(); } }); diff --git a/src/utils/code-push-error.ts b/src/utils/code-push-error.ts new file mode 100644 index 00000000..21ca7fd9 --- /dev/null +++ b/src/utils/code-push-error.ts @@ -0,0 +1,34 @@ +export class CodePushError extends Error { + constructor(message: string) { + super(message); + Object.setPrototypeOf(this, CodePushError.prototype); + } +} + +export class CodePushHttpError extends CodePushError { + constructor(message: string) { + super(message); + Object.setPrototypeOf(this, CodePushHttpError.prototype); + } +} + +export class CodePushDeployStatusError extends CodePushError { + constructor(message: string) { + super(message); + Object.setPrototypeOf(this, CodePushDeployStatusError.prototype); + } +} + +export class CodePushPackageError extends CodePushError { + constructor(message: string) { + super(message); + Object.setPrototypeOf(this, CodePushPackageError.prototype); + } +} + +export class CodePushUnauthorizedError extends CodePushError { + constructor(message: string) { + super(message); + Object.setPrototypeOf(this, CodePushUnauthorizedError.prototype); + } +}