@revopush/code-push-cli 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/.eslintrc.json +17 -0
  2. package/.idea/cli.iml +9 -0
  3. package/.idea/inspectionProfiles/Project_Default.xml +6 -0
  4. package/.idea/misc.xml +6 -0
  5. package/.idea/modules.xml +8 -0
  6. package/.idea/prettier.xml +6 -0
  7. package/.idea/vcs.xml +6 -0
  8. package/README.md +774 -0
  9. package/bin/script/acquisition-sdk.js +178 -0
  10. package/bin/script/cli.js +23 -0
  11. package/bin/script/command-executor.js +1290 -0
  12. package/bin/script/command-parser.js +1097 -0
  13. package/bin/script/commands/debug.js +125 -0
  14. package/bin/script/hash-utils.js +203 -0
  15. package/bin/script/index.js +5 -0
  16. package/bin/script/management-sdk.js +419 -0
  17. package/bin/script/react-native-utils.js +249 -0
  18. package/bin/script/sign.js +69 -0
  19. package/bin/script/types/cli.js +40 -0
  20. package/bin/script/types/rest-definitions.js +19 -0
  21. package/bin/script/types.js +4 -0
  22. package/bin/script/utils/file-utils.js +50 -0
  23. package/bin/test/acquisition-rest-mock.js +108 -0
  24. package/bin/test/acquisition-sdk.js +188 -0
  25. package/bin/test/cli.js +1342 -0
  26. package/bin/test/hash-utils.js +149 -0
  27. package/bin/test/management-sdk.js +338 -0
  28. package/package.json +68 -0
  29. package/prettier.config.js +7 -0
  30. package/script/acquisition-sdk.ts +273 -0
  31. package/script/cli.ts +27 -0
  32. package/script/command-executor.ts +1610 -0
  33. package/script/command-parser.ts +1310 -0
  34. package/script/commands/debug.ts +148 -0
  35. package/script/hash-utils.ts +241 -0
  36. package/script/index.ts +5 -0
  37. package/script/management-sdk.ts +575 -0
  38. package/script/react-native-utils.ts +283 -0
  39. package/script/sign.ts +80 -0
  40. package/script/types/cli.ts +232 -0
  41. package/script/types/rest-definitions.ts +152 -0
  42. package/script/types.ts +35 -0
  43. package/script/utils/file-utils.ts +46 -0
  44. package/test/acquisition-rest-mock.ts +125 -0
  45. package/test/acquisition-sdk.ts +272 -0
  46. package/test/cli.ts +1692 -0
  47. package/test/hash-utils.ts +170 -0
  48. package/test/management-sdk.ts +438 -0
  49. package/test/resources/TestApp/android/app/build.gradle +56 -0
  50. package/test/resources/TestApp/iOS/TestApp/Info.plist +49 -0
  51. package/test/resources/TestApp/index.android.js +2 -0
  52. package/test/resources/TestApp/index.ios.js +2 -0
  53. package/test/resources/TestApp/index.windows.js +2 -0
  54. package/test/resources/TestApp/package.json +6 -0
  55. package/test/resources/TestApp/windows/TestApp/Package.appxmanifest +46 -0
  56. package/test/resources/ignoredMetadata.zip +0 -0
  57. package/test/resources/test.zip +0 -0
  58. package/test/superagent-mock-config.js +58 -0
  59. package/tsconfig.json +13 -0
@@ -0,0 +1,575 @@
1
+ // Copyright (c) Microsoft Corporation.
2
+ // Licensed under the MIT License.
3
+
4
+ import * as fs from "fs";
5
+ import * as os from "os";
6
+ import * as path from "path";
7
+ import Q = require("q");
8
+ import superagent = require("superagent");
9
+ import * as recursiveFs from "recursive-fs";
10
+ import * as yazl from "yazl";
11
+ import slash = require("slash");
12
+
13
+ import Promise = Q.Promise;
14
+
15
+ import {
16
+ AccessKey,
17
+ AccessKeyRequest,
18
+ Account,
19
+ App,
20
+ CodePushError,
21
+ CollaboratorMap,
22
+ Deployment,
23
+ DeploymentMetrics,
24
+ Headers,
25
+ Package,
26
+ PackageInfo,
27
+ ServerAccessKey,
28
+ Session,
29
+ } from "./types";
30
+
31
+ const packageJson = require("../../package.json");
32
+
33
+ interface JsonResponse {
34
+ headers: Headers;
35
+ body?: any;
36
+ }
37
+
38
+ interface PackageFile {
39
+ isTemporary: boolean;
40
+ path: string;
41
+ }
42
+
43
+ // A template string tag function that URL encodes the substituted values
44
+ function urlEncode(strings: string[], ...values: string[]): string {
45
+ let result = "";
46
+ for (let i = 0; i < strings.length; i++) {
47
+ result += strings[i];
48
+ if (i < values.length) {
49
+ result += encodeURIComponent(values[i]);
50
+ }
51
+ }
52
+
53
+ return result;
54
+ }
55
+
56
+ class AccountManager {
57
+ public static AppPermission = {
58
+ OWNER: "Owner",
59
+ COLLABORATOR: "Collaborator",
60
+ };
61
+ public static SERVER_URL = "http://localhost:3000";
62
+
63
+ private static API_VERSION: number = 2;
64
+
65
+ public static ERROR_GATEWAY_TIMEOUT = 504; // Used if there is a network error
66
+ public static ERROR_INTERNAL_SERVER = 500;
67
+ public static ERROR_NOT_FOUND = 404;
68
+ public static ERROR_CONFLICT = 409; // Used if the resource already exists
69
+ public static ERROR_UNAUTHORIZED = 401;
70
+
71
+ private _accessKey: string;
72
+ private _serverUrl: string;
73
+ private _customHeaders: Headers;
74
+
75
+ constructor(accessKey: string, customHeaders?: Headers, serverUrl?: string) {
76
+ if (!accessKey) throw new Error("An access key must be specified.");
77
+
78
+ this._accessKey = accessKey;
79
+ this._customHeaders = customHeaders;
80
+ this._serverUrl = serverUrl || AccountManager.SERVER_URL;
81
+ }
82
+
83
+ public get accessKey(): string {
84
+ return this._accessKey;
85
+ }
86
+
87
+ public isAuthenticated(throwIfUnauthorized?: boolean): Promise<boolean> {
88
+ return Promise<any>((resolve, reject, notify) => {
89
+ const request: superagent.Request<any> = superagent.get(`${this._serverUrl}${urlEncode(["/authenticated"])}`);
90
+ this.attachCredentials(request);
91
+
92
+ request.end((err: any, res: superagent.Response) => {
93
+ const status: number = this.getErrorStatus(err, res);
94
+ if (err && status !== AccountManager.ERROR_UNAUTHORIZED) {
95
+ reject(this.getCodePushError(err, res));
96
+ return;
97
+ }
98
+
99
+ const authenticated: boolean = status === 200;
100
+
101
+ if (!authenticated && throwIfUnauthorized) {
102
+ reject(this.getCodePushError(err, res));
103
+ return;
104
+ }
105
+
106
+ resolve(authenticated);
107
+ });
108
+ });
109
+ }
110
+
111
+ public addAccessKey(friendlyName: string, ttl?: number): Promise<AccessKey> {
112
+ if (!friendlyName) {
113
+ throw new Error("A name must be specified when adding an access key.");
114
+ }
115
+
116
+ const accessKeyRequest: AccessKeyRequest = {
117
+ createdBy: os.hostname(),
118
+ friendlyName,
119
+ ttl,
120
+ };
121
+
122
+ return this.post(urlEncode(["/accessKeys/"]), JSON.stringify(accessKeyRequest), /*expectResponseBody=*/ true).then(
123
+ (response: JsonResponse) => {
124
+ return {
125
+ createdTime: response.body.accessKey.createdTime,
126
+ expires: response.body.accessKey.expires,
127
+ key: response.body.accessKey.name,
128
+ name: response.body.accessKey.friendlyName,
129
+ };
130
+ }
131
+ );
132
+ }
133
+
134
+ public getAccessKey(accessKeyName: string): Promise<AccessKey> {
135
+ return this.get(urlEncode([`/accessKeys/${accessKeyName}`])).then((res: JsonResponse) => {
136
+ return {
137
+ createdTime: res.body.accessKey.createdTime,
138
+ expires: res.body.accessKey.expires,
139
+ name: res.body.accessKey.friendlyName,
140
+ };
141
+ });
142
+ }
143
+
144
+ public getAccessKeys(): Promise<AccessKey[]> {
145
+ return this.get(urlEncode(["/accessKeys"])).then((res: JsonResponse) => {
146
+ const accessKeys: AccessKey[] = [];
147
+
148
+ res.body.accessKeys.forEach((serverAccessKey: ServerAccessKey) => {
149
+ !serverAccessKey.isSession &&
150
+ accessKeys.push({
151
+ createdTime: serverAccessKey.createdTime,
152
+ expires: serverAccessKey.expires,
153
+ name: serverAccessKey.friendlyName,
154
+ });
155
+ });
156
+
157
+ return accessKeys;
158
+ });
159
+ }
160
+
161
+ public getSessions(): Promise<Session[]> {
162
+ return this.get(urlEncode(["/accessKeys"])).then((res: JsonResponse) => {
163
+ // A machine name might be associated with multiple session keys,
164
+ // but we should only return one per machine name.
165
+ const sessionMap: { [machineName: string]: Session } = {};
166
+ const now: number = new Date().getTime();
167
+ res.body.accessKeys.forEach((serverAccessKey: ServerAccessKey) => {
168
+ if (serverAccessKey.isSession && serverAccessKey.expires > now) {
169
+ sessionMap[serverAccessKey.createdBy] = {
170
+ loggedInTime: serverAccessKey.createdTime,
171
+ machineName: serverAccessKey.createdBy,
172
+ };
173
+ }
174
+ });
175
+
176
+ const sessions: Session[] = Object.keys(sessionMap).map((machineName: string) => sessionMap[machineName]);
177
+
178
+ return sessions;
179
+ });
180
+ }
181
+
182
+ public patchAccessKey(oldName: string, newName?: string, ttl?: number): Promise<AccessKey> {
183
+ const accessKeyRequest: AccessKeyRequest = {
184
+ friendlyName: newName,
185
+ ttl,
186
+ };
187
+
188
+ return this.patch(urlEncode([`/accessKeys/${oldName}`]), JSON.stringify(accessKeyRequest)).then((res: JsonResponse) => {
189
+ return {
190
+ createdTime: res.body.accessKey.createdTime,
191
+ expires: res.body.accessKey.expires,
192
+ name: res.body.accessKey.friendlyName,
193
+ };
194
+ });
195
+ }
196
+
197
+ public removeAccessKey(name: string): Promise<void> {
198
+ return this.del(urlEncode([`/accessKeys/${name}`])).then(() => null);
199
+ }
200
+
201
+ public removeSession(machineName: string): Promise<void> {
202
+ return this.del(urlEncode([`/sessions/${machineName}`])).then(() => null);
203
+ }
204
+
205
+ // Account
206
+ public getAccountInfo(): Promise<Account> {
207
+ return this.get(urlEncode(["/account"])).then((res: JsonResponse) => res.body.account);
208
+ }
209
+
210
+ // Apps
211
+ public getApps(): Promise<App[]> {
212
+ return this.get(urlEncode(["/apps"])).then((res: JsonResponse) => res.body.apps);
213
+ }
214
+
215
+ public getApp(appName: string): Promise<App> {
216
+ return this.get(urlEncode([`/apps/${appName}`])).then((res: JsonResponse) => res.body.app);
217
+ }
218
+
219
+ public addApp(appName: string): Promise<App> {
220
+ const app: App = { name: appName };
221
+ return this.post(urlEncode(["/apps/"]), JSON.stringify(app), /*expectResponseBody=*/ false).then(() => app);
222
+ }
223
+
224
+ public removeApp(appName: string): Promise<void> {
225
+ return this.del(urlEncode([`/apps/${appName}`])).then(() => null);
226
+ }
227
+
228
+ public renameApp(oldAppName: string, newAppName: string): Promise<void> {
229
+ return this.patch(urlEncode([`/apps/${oldAppName}`]), JSON.stringify({ name: newAppName })).then(() => null);
230
+ }
231
+
232
+ public transferApp(appName: string, email: string): Promise<void> {
233
+ return this.post(urlEncode([`/apps/${appName}/transfer/${email}`]), /*requestBody=*/ null, /*expectResponseBody=*/ false).then(
234
+ () => null
235
+ );
236
+ }
237
+
238
+ // Collaborators
239
+ public getCollaborators(appName: string): Promise<CollaboratorMap> {
240
+ return this.get(urlEncode([`/apps/${appName}/collaborators`])).then((res: JsonResponse) => res.body.collaborators);
241
+ }
242
+
243
+ public addCollaborator(appName: string, email: string): Promise<void> {
244
+ return this.post(
245
+ urlEncode([`/apps/${appName}/collaborators/${email}`]),
246
+ /*requestBody=*/ null,
247
+ /*expectResponseBody=*/ false
248
+ ).then(() => null);
249
+ }
250
+
251
+ public removeCollaborator(appName: string, email: string): Promise<void> {
252
+ return this.del(urlEncode([`/apps/${appName}/collaborators/${email}`])).then(() => null);
253
+ }
254
+
255
+ // Deployments
256
+ public addDeployment(appName: string, deploymentName: string): Promise<Deployment> {
257
+ const deployment = <Deployment>{ name: deploymentName };
258
+ return this.post(urlEncode([`/apps/${appName}/deployments/`]), JSON.stringify(deployment), /*expectResponseBody=*/ true).then(
259
+ (res: JsonResponse) => res.body.deployment
260
+ );
261
+ }
262
+
263
+ public clearDeploymentHistory(appName: string, deploymentName: string): Promise<void> {
264
+ return this.del(urlEncode([`/apps/${appName}/deployments/${deploymentName}/history`])).then(() => null);
265
+ }
266
+
267
+ public getDeployments(appName: string): Promise<Deployment[]> {
268
+ return this.get(urlEncode([`/apps/${appName}/deployments/`])).then((res: JsonResponse) => res.body.deployments);
269
+ }
270
+
271
+ public getDeployment(appName: string, deploymentName: string): Promise<Deployment> {
272
+ return this.get(urlEncode([`/apps/${appName}/deployments/${deploymentName}`])).then((res: JsonResponse) => res.body.deployment);
273
+ }
274
+
275
+ public renameDeployment(appName: string, oldDeploymentName: string, newDeploymentName: string): Promise<void> {
276
+ return this.patch(
277
+ urlEncode([`/apps/${appName}/deployments/${oldDeploymentName}`]),
278
+ JSON.stringify({ name: newDeploymentName })
279
+ ).then(() => null);
280
+ }
281
+
282
+ public removeDeployment(appName: string, deploymentName: string): Promise<void> {
283
+ return this.del(urlEncode([`/apps/${appName}/deployments/${deploymentName}`])).then(() => null);
284
+ }
285
+
286
+ public getDeploymentMetrics(appName: string, deploymentName: string): Promise<DeploymentMetrics> {
287
+ return this.get(urlEncode([`/apps/${appName}/deployments/${deploymentName}/metrics`])).then(
288
+ (res: JsonResponse) => res.body.metrics
289
+ );
290
+ }
291
+
292
+ public getDeploymentHistory(appName: string, deploymentName: string): Promise<Package[]> {
293
+ return this.get(urlEncode([`/apps/${appName}/deployments/${deploymentName}/history`])).then(
294
+ (res: JsonResponse) => res.body.history
295
+ );
296
+ }
297
+
298
+ public release(
299
+ appName: string,
300
+ deploymentName: string,
301
+ filePath: string,
302
+ targetBinaryVersion: string,
303
+ updateMetadata: PackageInfo,
304
+ uploadProgressCallback?: (progress: number) => void
305
+ ): Promise<void> {
306
+ return Promise<void>((resolve, reject, notify) => {
307
+ updateMetadata.appVersion = targetBinaryVersion;
308
+ const request: superagent.Request<any> = superagent.post(
309
+ this._serverUrl + urlEncode([`/apps/${appName}/deployments/${deploymentName}/release`])
310
+ );
311
+
312
+ this.attachCredentials(request);
313
+
314
+ const getPackageFilePromise = Q.Promise((resolve, reject) => {
315
+ this.packageFileFromPath(filePath)
316
+ .then((result) => {
317
+ resolve(result);
318
+ })
319
+ .catch((error) => {
320
+ reject(error);
321
+ });
322
+ });
323
+
324
+ getPackageFilePromise.then((packageFile: PackageFile) => {
325
+ const file: any = fs.createReadStream(packageFile.path);
326
+ request
327
+ .attach("package", file)
328
+ .field("packageInfo", JSON.stringify(updateMetadata))
329
+ .on("progress", (event: any) => {
330
+ if (uploadProgressCallback && event && event.total > 0) {
331
+ const currentProgress: number = (event.loaded / event.total) * 100;
332
+ uploadProgressCallback(currentProgress);
333
+ }
334
+ })
335
+ .end((err: any, res: superagent.Response) => {
336
+ if (packageFile.isTemporary) {
337
+ fs.unlinkSync(packageFile.path);
338
+ }
339
+
340
+ if (err) {
341
+ reject(this.getCodePushError(err, res));
342
+ return;
343
+ }
344
+
345
+ if (res.ok) {
346
+ resolve(<void>null);
347
+ } else {
348
+ let body;
349
+ try {
350
+ body = JSON.parse(res.text);
351
+ } catch (err) {}
352
+
353
+ if (body) {
354
+ reject(<CodePushError>{
355
+ message: body.message,
356
+ statusCode: res && res.status,
357
+ });
358
+ } else {
359
+ reject(<CodePushError>{
360
+ message: res.text,
361
+ statusCode: res && res.status,
362
+ });
363
+ }
364
+ }
365
+ });
366
+ });
367
+ });
368
+ }
369
+
370
+ public patchRelease(appName: string, deploymentName: string, label: string, updateMetadata: PackageInfo): Promise<void> {
371
+ updateMetadata.label = label;
372
+ const requestBody: string = JSON.stringify({ packageInfo: updateMetadata });
373
+ return this.patch(
374
+ urlEncode([`/apps/${appName}/deployments/${deploymentName}/release`]),
375
+ requestBody,
376
+ /*expectResponseBody=*/ false
377
+ ).then(() => null);
378
+ }
379
+
380
+ public promote(
381
+ appName: string,
382
+ sourceDeploymentName: string,
383
+ destinationDeploymentName: string,
384
+ updateMetadata: PackageInfo
385
+ ): Promise<void> {
386
+ const requestBody: string = JSON.stringify({ packageInfo: updateMetadata });
387
+ return this.post(
388
+ urlEncode([`/apps/${appName}/deployments/${sourceDeploymentName}/promote/${destinationDeploymentName}`]),
389
+ requestBody,
390
+ /*expectResponseBody=*/ false
391
+ ).then(() => null);
392
+ }
393
+
394
+ public rollback(appName: string, deploymentName: string, targetRelease?: string): Promise<void> {
395
+ return this.post(
396
+ urlEncode([`/apps/${appName}/deployments/${deploymentName}/rollback/${targetRelease || ``}`]),
397
+ /*requestBody=*/ null,
398
+ /*expectResponseBody=*/ false
399
+ ).then(() => null);
400
+ }
401
+
402
+ private packageFileFromPath(filePath: string) {
403
+ let getPackageFilePromise: Promise<PackageFile>;
404
+ if (fs.lstatSync(filePath).isDirectory()) {
405
+ getPackageFilePromise = Promise<PackageFile>((resolve: (file: PackageFile) => void, reject: (reason: Error) => void): void => {
406
+ const directoryPath: string = filePath;
407
+
408
+ recursiveFs.readdirr(directoryPath, (error?: any, directories?: string[], files?: string[]) => {
409
+ if (error) {
410
+ reject(error);
411
+ return;
412
+ }
413
+
414
+ const baseDirectoryPath = path.dirname(directoryPath);
415
+ const fileName: string = this.generateRandomFilename(15) + ".zip";
416
+ const zipFile = new yazl.ZipFile();
417
+ const writeStream: fs.WriteStream = fs.createWriteStream(fileName);
418
+
419
+ zipFile.outputStream
420
+ .pipe(writeStream)
421
+ .on("error", (error: Error): void => {
422
+ reject(error);
423
+ })
424
+ .on("close", (): void => {
425
+ filePath = path.join(process.cwd(), fileName);
426
+
427
+ resolve({ isTemporary: true, path: filePath });
428
+ });
429
+
430
+ for (let i = 0; i < files.length; ++i) {
431
+ const file: string = files[i];
432
+ // yazl does not like backslash (\) in the metadata path.
433
+ const relativePath: string = slash(path.relative(baseDirectoryPath, file));
434
+
435
+ zipFile.addFile(file, relativePath);
436
+ }
437
+
438
+ zipFile.end();
439
+ });
440
+ });
441
+ } else {
442
+ getPackageFilePromise = Q({ isTemporary: false, path: filePath });
443
+ }
444
+ return getPackageFilePromise;
445
+ }
446
+
447
+ private generateRandomFilename(length: number): string {
448
+ let filename: string = "";
449
+ const validChar: string = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
450
+
451
+ for (let i = 0; i < length; i++) {
452
+ filename += validChar.charAt(Math.floor(Math.random() * validChar.length));
453
+ }
454
+
455
+ return filename;
456
+ }
457
+
458
+ private get(endpoint: string, expectResponseBody: boolean = true): Promise<JsonResponse> {
459
+ return this.makeApiRequest("get", endpoint, /*requestBody=*/ null, expectResponseBody, /*contentType=*/ null);
460
+ }
461
+
462
+ private post(
463
+ endpoint: string,
464
+ requestBody: string,
465
+ expectResponseBody: boolean,
466
+ contentType: string = "application/json;charset=UTF-8"
467
+ ): Promise<JsonResponse> {
468
+ return this.makeApiRequest("post", endpoint, requestBody, expectResponseBody, contentType);
469
+ }
470
+
471
+ private patch(
472
+ endpoint: string,
473
+ requestBody: string,
474
+ expectResponseBody: boolean = false,
475
+ contentType: string = "application/json;charset=UTF-8"
476
+ ): Promise<JsonResponse> {
477
+ return this.makeApiRequest("patch", endpoint, requestBody, expectResponseBody, contentType);
478
+ }
479
+
480
+ private del(endpoint: string, expectResponseBody: boolean = false): Promise<JsonResponse> {
481
+ return this.makeApiRequest("del", endpoint, /*requestBody=*/ null, expectResponseBody, /*contentType=*/ null);
482
+ }
483
+
484
+ private makeApiRequest(
485
+ method: string,
486
+ endpoint: string,
487
+ requestBody: string,
488
+ expectResponseBody: boolean,
489
+ contentType: string
490
+ ): Promise<JsonResponse> {
491
+ return Promise<JsonResponse>((resolve, reject, notify) => {
492
+ let request: superagent.Request<any> = (<any>superagent)[method](this._serverUrl + endpoint);
493
+
494
+ this.attachCredentials(request);
495
+
496
+ if (requestBody) {
497
+ if (contentType) {
498
+ request = request.set("Content-Type", contentType);
499
+ }
500
+
501
+ request = request.send(requestBody);
502
+ }
503
+
504
+ request.end((err: any, res: superagent.Response) => {
505
+ if (err) {
506
+ reject(this.getCodePushError(err, res));
507
+ return;
508
+ }
509
+ let body;
510
+ try {
511
+ body = JSON.parse(res.text);
512
+ } catch (err) {}
513
+
514
+ if (res.ok) {
515
+ if (expectResponseBody && !body) {
516
+ reject(<CodePushError>{
517
+ message: `Could not parse response: ${res.text}`,
518
+ statusCode: AccountManager.ERROR_INTERNAL_SERVER,
519
+ });
520
+ } else {
521
+ resolve(<JsonResponse>{
522
+ headers: res.header,
523
+ body: body,
524
+ });
525
+ }
526
+ } else {
527
+ if (body) {
528
+ reject(<CodePushError>{
529
+ message: body.message,
530
+ statusCode: this.getErrorStatus(err, res),
531
+ });
532
+ } else {
533
+ reject(<CodePushError>{
534
+ message: res.text,
535
+ statusCode: this.getErrorStatus(err, res),
536
+ });
537
+ }
538
+ }
539
+ });
540
+ });
541
+ }
542
+
543
+ private getCodePushError(error: any, response: superagent.Response): CodePushError {
544
+ if (error.syscall === "getaddrinfo") {
545
+ error.message = `Unable to connect to the CodePush server. Are you offline, or behind a firewall or proxy?\n(${error.message})`;
546
+ }
547
+
548
+ return {
549
+ message: this.getErrorMessage(error, response),
550
+ statusCode: this.getErrorStatus(error, response),
551
+ };
552
+ }
553
+
554
+ private getErrorStatus(error: any, response: superagent.Response): number {
555
+ return (error && error.status) || (response && response.status) || AccountManager.ERROR_GATEWAY_TIMEOUT;
556
+ }
557
+
558
+ private getErrorMessage(error: Error, response: superagent.Response): string {
559
+ return response && response.text ? response.text : error.message;
560
+ }
561
+
562
+ private attachCredentials(request: superagent.Request<any>): void {
563
+ if (this._customHeaders) {
564
+ for (const headerName in this._customHeaders) {
565
+ request.set(headerName, this._customHeaders[headerName]);
566
+ }
567
+ }
568
+
569
+ request.set("Accept", `application/vnd.code-push.v${AccountManager.API_VERSION}+json`);
570
+ request.set("Authorization", `Bearer ${this._accessKey}`);
571
+ request.set("X-CodePush-SDK-Version", packageJson.version);
572
+ }
573
+ }
574
+
575
+ export = AccountManager;