Skip to content
This repository was archived by the owner on May 20, 2025. It is now read-only.

Commit c7bf0eb

Browse files
Disable api calls (#851)
* Stop sending api calls after unsuccessful request * Update disable api calls logic * Update access modifer * Add test case for disabling api calls * Reformat code * Refactor and reformat code * Improve tests * Update unit tests * Update test cases * Remove static getter * Reformat code * Fix typo and refactor code * Handle all non 2xx status codes
1 parent 682b9f2 commit c7bf0eb

File tree

3 files changed

+122
-11
lines changed

3 files changed

+122
-11
lines changed

src/script/acquisition-sdk.ts

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,14 +58,16 @@ export class AcquisitionStatus {
5858
}
5959

6060
export class AcquisitionManager {
61+
private readonly BASE_URL_PART = "appcenter.ms";
6162
private _appVersion: string;
6263
private _clientUniqueId: string;
6364
private _deploymentKey: string;
6465
private _httpRequester: Http.Requester;
6566
private _ignoreAppVersion: boolean;
6667
private _serverUrl: string;
6768
private _publicPrefixUrl: string = "v0.1/public/codepush/";
68-
69+
private _statusCode: number;
70+
private static _apiCallsDisabled: boolean = false;
6971
constructor(httpRequester: Http.Requester, configuration: Configuration) {
7072
this._httpRequester = httpRequester;
7173

@@ -80,7 +82,21 @@ export class AcquisitionManager {
8082
this._ignoreAppVersion = configuration.ignoreAppVersion;
8183
}
8284

85+
private isRecoverable = (statusCode: number): boolean => statusCode >= 500 || statusCode === 408 || statusCode === 429;
86+
87+
private handleRequestFailure() {
88+
if (this._serverUrl.includes(this.BASE_URL_PART) && !this.isRecoverable(this._statusCode)) {
89+
AcquisitionManager._apiCallsDisabled = true;
90+
}
91+
}
92+
8393
public queryUpdateWithCurrentPackage(currentPackage: Package, callback?: Callback<RemotePackage | NativeUpdateNotification>): void {
94+
if (AcquisitionManager._apiCallsDisabled) {
95+
console.log(`[CodePush] Api calls are disabled, skipping API call`);
96+
callback(/*error=*/ null, /*remotePackage=*/ null);
97+
return;
98+
}
99+
84100
if (!currentPackage || !currentPackage.appVersion) {
85101
throw new CodePushPackageError("Calling common acquisition SDK with incorrect package"); // Unexpected; indicates error in our implementation
86102
}
@@ -102,8 +118,10 @@ export class AcquisitionManager {
102118
return;
103119
}
104120

105-
if (response.statusCode !== 200) {
121+
if (response.statusCode < 200 || response.statusCode >= 300) {
106122
let errorMessage: any;
123+
this._statusCode = response.statusCode;
124+
this.handleRequestFailure();
107125
if (response.statusCode === 0) {
108126
errorMessage = `Couldn't send request to ${requestUrl}, xhr.statusCode = 0 was returned. One of the possible reasons for that might be connection problems. Please, check your internet connection.`;
109127
} else {
@@ -147,6 +165,12 @@ export class AcquisitionManager {
147165
}
148166

149167
public reportStatusDeploy(deployedPackage?: Package, status?: string, previousLabelOrAppVersion?: string, previousDeploymentKey?: string, callback?: Callback<void>): void {
168+
if (AcquisitionManager._apiCallsDisabled) {
169+
console.log(`[CodePush] Api calls are disabled, skipping API call`);
170+
callback(/*error*/ null, /*not used*/ null);
171+
return;
172+
}
173+
150174
var url: string = this._serverUrl + this._publicPrefixUrl + "report_status/deploy";
151175
var body: DeploymentStatusReport = {
152176
app_version: this._appVersion,
@@ -196,7 +220,9 @@ export class AcquisitionManager {
196220
return;
197221
}
198222

199-
if (response.statusCode !== 200) {
223+
if (response.statusCode < 200 || response.statusCode >= 300) {
224+
this._statusCode = response.statusCode;
225+
this.handleRequestFailure();
200226
callback(new CodePushHttpError(response.statusCode + ": " + response.body), /*not used*/ null);
201227
return;
202228
}
@@ -207,6 +233,12 @@ export class AcquisitionManager {
207233
}
208234

209235
public reportStatusDownload(downloadedPackage: Package, callback?: Callback<void>): void {
236+
if (AcquisitionManager._apiCallsDisabled) {
237+
console.log(`[CodePush] Api calls are disabled, skipping API call`);
238+
callback(/*error*/ null, /*not used*/ null);
239+
return;
240+
}
241+
210242
var url: string = this._serverUrl + this._publicPrefixUrl + "report_status/download";
211243
var body: DownloadReport = {
212244
client_unique_id: this._clientUniqueId,
@@ -221,7 +253,9 @@ export class AcquisitionManager {
221253
return;
222254
}
223255

224-
if (response.statusCode !== 200) {
256+
if (response.statusCode < 200 || response.statusCode >= 300) {
257+
this._statusCode = response.statusCode;
258+
this.handleRequestFailure();
225259
callback(new CodePushHttpError(response.statusCode + ": " + response.body), /*not used*/ null);
226260
return;
227261
}

src/test/acquisition-rest-mock.ts

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,19 +22,31 @@ var reportStatusDeployUrl = serverUrl + publicPrefixUrl + "/report_status/deploy
2222
var reportStatusDownloadUrl = serverUrl + publicPrefixUrl + "/report_status/download";
2323
var updateCheckUrl = serverUrl + publicPrefixUrl + "/update_check?";
2424

25+
export function updateMockUrl() {
26+
reportStatusDeployUrl = serverUrl + publicPrefixUrl + "/report_status/deploy";
27+
reportStatusDownloadUrl = serverUrl + publicPrefixUrl + "/report_status/download";
28+
updateCheckUrl = serverUrl + publicPrefixUrl + "/update_check?";
29+
}
30+
2531
export class HttpRequester implements acquisitionSdk.Http.Requester {
32+
private expectedStatusCode: number;
33+
34+
constructor(expectedStatusCode?: number) {
35+
this.expectedStatusCode = expectedStatusCode;
36+
}
37+
2638
public request(verb: acquisitionSdk.Http.Verb, url: string, requestBodyOrCallback: string | acquisitionSdk.Callback<acquisitionSdk.Http.Response>, callback?: acquisitionSdk.Callback<acquisitionSdk.Http.Response>): void {
2739
if (!callback && typeof requestBodyOrCallback === "function") {
2840
callback = <acquisitionSdk.Callback<acquisitionSdk.Http.Response>>requestBodyOrCallback;
2941
}
3042

3143
if (verb === acquisitionSdk.Http.Verb.GET && url.indexOf(updateCheckUrl) === 0) {
3244
var params = querystring.parse(url.substring(updateCheckUrl.length));
33-
Server.onUpdateCheck(params, callback);
45+
Server.onUpdateCheck(params, callback, this.expectedStatusCode);
3446
} else if (verb === acquisitionSdk.Http.Verb.POST && url === reportStatusDeployUrl) {
35-
Server.onReportStatus(callback);
47+
Server.onReportStatus(callback, this.expectedStatusCode);
3648
} else if (verb === acquisitionSdk.Http.Verb.POST && url === reportStatusDownloadUrl) {
37-
Server.onReportStatus(callback);
49+
Server.onReportStatus(callback, this.expectedStatusCode);
3850
} else {
3951
throw new Error("Unexpected call");
4052
}
@@ -73,7 +85,7 @@ class Server {
7385
}
7486
}
7587

76-
public static onUpdateCheck(params: any, callback: acquisitionSdk.Callback<acquisitionSdk.Http.Response>): void {
88+
public static onUpdateCheck(params: any, callback: acquisitionSdk.Callback<acquisitionSdk.Http.Response>, expectedStatusCode?: number): void {
7789
var updateRequest: types.UpdateCheckRequest = {
7890
deployment_key: params.deployment_key,
7991
app_version: params.app_version,
@@ -97,13 +109,13 @@ class Server {
97109
}
98110

99111
callback(/*error=*/ null, {
100-
statusCode: 200,
112+
statusCode: expectedStatusCode ? expectedStatusCode : 200,
101113
body: JSON.stringify({ update_info: updateInfo })
102114
});
103115
}
104116
}
105117

106-
public static onReportStatus(callback: acquisitionSdk.Callback<acquisitionSdk.Http.Response>): void {
107-
callback(/*error*/ null, /*response*/ { statusCode: 200 });
118+
public static onReportStatus(callback: acquisitionSdk.Callback<acquisitionSdk.Http.Response>, expectedStatusCode: number): void {
119+
callback(/*error*/ null, /*response*/ { statusCode: expectedStatusCode ? expectedStatusCode : 200 });
108120
}
109121
}

src/test/acquisition-sdk.ts

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import * as acquisitionSdk from "../script/acquisition-sdk";
44
import * as acquisitionRestMock from "./acquisition-rest-mock";
55
import * as types from "../script/types";
66
import { CodePushPackageError } from "../script/code-push-error"
7+
import { updateMockUrl } from "./acquisition-rest-mock";
78

89
const mockApi = acquisitionRestMock;
910
var latestPackage: types.UpdateCheckResponse = clone(mockApi.latestPackage);
@@ -44,6 +45,8 @@ var nativeUpdateResult: acquisitionSdk.NativeUpdateNotification = {
4445
describe("Acquisition SDK", () => {
4546
beforeEach(() => {
4647
mockApi.latestPackage = clone(latestPackage);
48+
mockApi.serverUrl = "http://myurl.com";
49+
updateMockUrl();
4750
});
4851

4952
it("Package with lower label and different package hash gives update", (done: Mocha.Done) => {
@@ -226,6 +229,68 @@ describe("Acquisition SDK", () => {
226229
done();
227230
}));
228231
});
232+
233+
it("disables api calls on unsuccessful response", (done: Mocha.Done): void => {
234+
var invalidJsonResponse: acquisitionSdk.Http.Response = {
235+
statusCode: 404,
236+
body: "Not found"
237+
};
238+
239+
mockApi.serverUrl = "https://codepush.appcenter.ms";
240+
updateMockUrl();
241+
configuration = { ...configuration, serverUrl: "https://codepush.appcenter.ms" };
242+
243+
var acquisition = new acquisitionSdk.AcquisitionManager(new mockApi.CustomResponseHttpRequester(invalidJsonResponse), configuration);
244+
acquisition.queryUpdateWithCurrentPackage(templateCurrentPackage, (error: Error, returnPackage: acquisitionSdk.RemotePackage | acquisitionSdk.NativeUpdateNotification) => {
245+
assert.strictEqual((acquisitionSdk.AcquisitionManager as any)._apiCallsDisabled, true);
246+
(acquisitionSdk.AcquisitionManager as any)._apiCallsDisabled = false;
247+
});
248+
249+
acquisition.queryUpdateWithCurrentPackage(templateCurrentPackage, (error: Error, returnPackage: acquisitionSdk.RemotePackage | acquisitionSdk.NativeUpdateNotification) => {
250+
assert.strictEqual(returnPackage, null);
251+
acquisition = new acquisitionSdk.AcquisitionManager(new mockApi.HttpRequester(404), configuration);
252+
(acquisitionSdk.AcquisitionManager as any)._apiCallsDisabled = false;
253+
});
254+
255+
acquisition.reportStatusDeploy(templateCurrentPackage, acquisitionSdk.AcquisitionStatus.DeploymentSucceeded, "1.5.0", mockApi.validDeploymentKey, ((error: Error, parameter: void): void => {
256+
assert.strictEqual((acquisitionSdk.AcquisitionManager as any)._apiCallsDisabled, true);
257+
acquisition = new acquisitionSdk.AcquisitionManager(new mockApi.HttpRequester(404), configuration);
258+
(acquisitionSdk.AcquisitionManager as any)._apiCallsDisabled = false;
259+
}));
260+
261+
acquisition.reportStatusDownload(templateCurrentPackage, ((error: Error, parameter: void): void => {
262+
assert.strictEqual((acquisitionSdk.AcquisitionManager as any)._apiCallsDisabled, true);
263+
acquisition = acquisition = new acquisitionSdk.AcquisitionManager(new mockApi.CustomResponseHttpRequester(invalidJsonResponse), configuration);
264+
(acquisitionSdk.AcquisitionManager as any)._apiCallsDisabled = false;
265+
}));
266+
267+
done();
268+
})
269+
270+
it("doesn't disable api calls on successful response", (done: Mocha.Done): void => {
271+
var acquisition = new acquisitionSdk.AcquisitionManager(new mockApi.HttpRequester(), configuration);
272+
mockApi.serverUrl = "https://codepush.appcenter.ms";
273+
updateMockUrl();
274+
275+
acquisition.queryUpdateWithCurrentPackage(templateCurrentPackage, (error: Error, returnPackage: acquisitionSdk.RemotePackage | acquisitionSdk.NativeUpdateNotification) => {
276+
assert.strictEqual((acquisitionSdk.AcquisitionManager as any)._apiCallsDisabled, false);
277+
});
278+
279+
acquisition.queryUpdateWithCurrentPackage(templateCurrentPackage, (error: Error, returnPackage: acquisitionSdk.RemotePackage | acquisitionSdk.NativeUpdateNotification) => {
280+
assert.notStrictEqual(returnPackage, null);
281+
});
282+
283+
acquisition.reportStatusDeploy(templateCurrentPackage, acquisitionSdk.AcquisitionStatus.DeploymentSucceeded, "1.5.0", mockApi.validDeploymentKey, ((error: Error, parameter: void): void => {
284+
assert.strictEqual((acquisitionSdk.AcquisitionManager as any)._apiCallsDisabled, false);
285+
}));
286+
287+
acquisition.reportStatusDownload(templateCurrentPackage, ((error: Error, parameter: void): void => {
288+
assert.strictEqual((acquisitionSdk.AcquisitionManager as any)._apiCallsDisabled, false);
289+
}));
290+
291+
done();
292+
})
293+
229294
});
230295

231296
function clone<T>(initialObject: T): T {

0 commit comments

Comments
 (0)