Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Disable api calls #851

Merged
merged 13 commits into from
Dec 16, 2024
36 changes: 35 additions & 1 deletion src/script/acquisition-sdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,14 +58,16 @@ export class AcquisitionStatus {
}

export class AcquisitionManager {
private readonly BASE_URL_PART = "appcenter.ms";
private _appVersion: string;
private _clientUniqueId: string;
private _deploymentKey: string;
private _httpRequester: Http.Requester;
private _ignoreAppVersion: boolean;
private _serverUrl: string;
private _publicPrefixUrl: string = "v0.1/public/codepush/";

private _statusCode: number;
private static _apiCallsDisabled: boolean = false;
constructor(httpRequester: Http.Requester, configuration: Configuration) {
DordeDimitrijev marked this conversation as resolved.
Show resolved Hide resolved
this._httpRequester = httpRequester;

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

private isRecoverable = (statusCode: number): boolean => statusCode >= 500 || statusCode === 408 || statusCode === 429;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You check this when response code is not 200 - what about other 2xx or 3xx responses - are they recoverable or not?


private handleRequestFailure() {
if (this._serverUrl.includes(this.BASE_URL_PART) && !this.isRecoverable(this._statusCode)) {
AcquisitionManager._apiCallsDisabled = true;
}
}

public queryUpdateWithCurrentPackage(currentPackage: Package, callback?: Callback<RemotePackage | NativeUpdateNotification>): void {
if (AcquisitionManager._apiCallsDisabled) {
console.log(`[CodePush] Api calls are disabled, skipping API call`);
callback(/*error=*/ null, /*remotePackage=*/ null);
return;
}

if (!currentPackage || !currentPackage.appVersion) {
throw new CodePushPackageError("Calling common acquisition SDK with incorrect package"); // Unexpected; indicates error in our implementation
}
Expand All @@ -104,6 +120,8 @@ export class AcquisitionManager {

if (response.statusCode !== 200) {
let errorMessage: any;
this._statusCode = response.statusCode;
this.handleRequestFailure();
if (response.statusCode === 0) {
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.`;
} else {
Expand Down Expand Up @@ -147,6 +165,12 @@ export class AcquisitionManager {
}

public reportStatusDeploy(deployedPackage?: Package, status?: string, previousLabelOrAppVersion?: string, previousDeploymentKey?: string, callback?: Callback<void>): void {
if (AcquisitionManager._apiCallsDisabled) {
console.log(`[CodePush] Api calls are disabled, skipping API call`);
callback(/*error*/ null, /*not used*/ null);
return;
DmitriyKirakosyan marked this conversation as resolved.
Show resolved Hide resolved
}

var url: string = this._serverUrl + this._publicPrefixUrl + "report_status/deploy";
var body: DeploymentStatusReport = {
app_version: this._appVersion,
Expand Down Expand Up @@ -197,6 +221,8 @@ export class AcquisitionManager {
}

if (response.statusCode !== 200) {
this._statusCode = response.statusCode;
this.handleRequestFailure();
callback(new CodePushHttpError(response.statusCode + ": " + response.body), /*not used*/ null);
return;
}
Expand All @@ -207,6 +233,12 @@ export class AcquisitionManager {
}

public reportStatusDownload(downloadedPackage: Package, callback?: Callback<void>): void {
if (AcquisitionManager._apiCallsDisabled) {
console.log(`[CodePush] Api calls are disabled, skipping API call`);
callback(/*error*/ null, /*not used*/ null);
return;
}

var url: string = this._serverUrl + this._publicPrefixUrl + "report_status/download";
var body: DownloadReport = {
client_unique_id: this._clientUniqueId,
Expand All @@ -222,6 +254,8 @@ export class AcquisitionManager {
}

if (response.statusCode !== 200) {
this._statusCode = response.statusCode;
this.handleRequestFailure();
callback(new CodePushHttpError(response.statusCode + ": " + response.body), /*not used*/ null);
return;
}
Expand Down
26 changes: 19 additions & 7 deletions src/test/acquisition-rest-mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,31 @@ var reportStatusDeployUrl = serverUrl + publicPrefixUrl + "/report_status/deploy
var reportStatusDownloadUrl = serverUrl + publicPrefixUrl + "/report_status/download";
var updateCheckUrl = serverUrl + publicPrefixUrl + "/update_check?";

export function updateMockUrl() {
reportStatusDeployUrl = serverUrl + publicPrefixUrl + "/report_status/deploy";
reportStatusDownloadUrl = serverUrl + publicPrefixUrl + "/report_status/download";
updateCheckUrl = serverUrl + publicPrefixUrl + "/update_check?";
}

DmitriyKirakosyan marked this conversation as resolved.
Show resolved Hide resolved
export class HttpRequester implements acquisitionSdk.Http.Requester {
private expectedStatusCode: number;

constructor(expectedStatusCode?: number) {
this.expectedStatusCode = expectedStatusCode;
}

public request(verb: acquisitionSdk.Http.Verb, url: string, requestBodyOrCallback: string | acquisitionSdk.Callback<acquisitionSdk.Http.Response>, callback?: acquisitionSdk.Callback<acquisitionSdk.Http.Response>): void {
DmitriyKirakosyan marked this conversation as resolved.
Show resolved Hide resolved
if (!callback && typeof requestBodyOrCallback === "function") {
callback = <acquisitionSdk.Callback<acquisitionSdk.Http.Response>>requestBodyOrCallback;
}

if (verb === acquisitionSdk.Http.Verb.GET && url.indexOf(updateCheckUrl) === 0) {
var params = querystring.parse(url.substring(updateCheckUrl.length));
Server.onUpdateCheck(params, callback);
Server.onUpdateCheck(params, callback, this.expectedStatusCode);
} else if (verb === acquisitionSdk.Http.Verb.POST && url === reportStatusDeployUrl) {
Server.onReportStatus(callback);
Server.onReportStatus(callback, this.expectedStatusCode);
} else if (verb === acquisitionSdk.Http.Verb.POST && url === reportStatusDownloadUrl) {
Server.onReportStatus(callback);
Server.onReportStatus(callback, this.expectedStatusCode);
} else {
throw new Error("Unexpected call");
}
Expand Down Expand Up @@ -73,7 +85,7 @@ class Server {
}
}

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

callback(/*error=*/ null, {
statusCode: 200,
statusCode: expectedStatusCode ? expectedStatusCode : 200,
body: JSON.stringify({ update_info: updateInfo })
});
}
}

public static onReportStatus(callback: acquisitionSdk.Callback<acquisitionSdk.Http.Response>): void {
callback(/*error*/ null, /*response*/ { statusCode: 200 });
public static onReportStatus(callback: acquisitionSdk.Callback<acquisitionSdk.Http.Response>, expectedStatusCode: number): void {
callback(/*error*/ null, /*response*/ { statusCode: expectedStatusCode ? expectedStatusCode : 200 });
}
}
65 changes: 65 additions & 0 deletions src/test/acquisition-sdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import * as acquisitionSdk from "../script/acquisition-sdk";
import * as acquisitionRestMock from "./acquisition-rest-mock";
import * as types from "../script/types";
import { CodePushPackageError } from "../script/code-push-error"
import { updateMockUrl } from "./acquisition-rest-mock";

const mockApi = acquisitionRestMock;
var latestPackage: types.UpdateCheckResponse = clone(mockApi.latestPackage);
Expand Down Expand Up @@ -44,6 +45,8 @@ var nativeUpdateResult: acquisitionSdk.NativeUpdateNotification = {
describe("Acquisition SDK", () => {
beforeEach(() => {
mockApi.latestPackage = clone(latestPackage);
mockApi.serverUrl = "http://myurl.com";
updateMockUrl();
});

it("Package with lower label and different package hash gives update", (done: Mocha.Done) => {
Expand Down Expand Up @@ -226,6 +229,68 @@ describe("Acquisition SDK", () => {
done();
}));
});

it("disables api calls on unsuccessful response", (done: Mocha.Done): void => {
DmitriyKirakosyan marked this conversation as resolved.
Show resolved Hide resolved
var invalidJsonResponse: acquisitionSdk.Http.Response = {
statusCode: 404,
body: "Not found"
};

mockApi.serverUrl = "https://codepush.appcenter.ms";
updateMockUrl();
configuration = { ...configuration, serverUrl: "https://codepush.appcenter.ms" };

var acquisition = new acquisitionSdk.AcquisitionManager(new mockApi.CustomResponseHttpRequester(invalidJsonResponse), configuration);
acquisition.queryUpdateWithCurrentPackage(templateCurrentPackage, (error: Error, returnPackage: acquisitionSdk.RemotePackage | acquisitionSdk.NativeUpdateNotification) => {
assert.strictEqual((acquisitionSdk.AcquisitionManager as any)._apiCallsDisabled, true);
(acquisitionSdk.AcquisitionManager as any)._apiCallsDisabled = false;
});

acquisition.queryUpdateWithCurrentPackage(templateCurrentPackage, (error: Error, returnPackage: acquisitionSdk.RemotePackage | acquisitionSdk.NativeUpdateNotification) => {
assert.strictEqual(returnPackage, null);
acquisition = new acquisitionSdk.AcquisitionManager(new mockApi.HttpRequester(404), configuration);
(acquisitionSdk.AcquisitionManager as any)._apiCallsDisabled = false;
});

acquisition.reportStatusDeploy(templateCurrentPackage, acquisitionSdk.AcquisitionStatus.DeploymentSucceeded, "1.5.0", mockApi.validDeploymentKey, ((error: Error, parameter: void): void => {
assert.strictEqual((acquisitionSdk.AcquisitionManager as any)._apiCallsDisabled, true);
acquisition = new acquisitionSdk.AcquisitionManager(new mockApi.HttpRequester(404), configuration);
(acquisitionSdk.AcquisitionManager as any)._apiCallsDisabled = false;
}));

acquisition.reportStatusDownload(templateCurrentPackage, ((error: Error, parameter: void): void => {
DordeDimitrijev marked this conversation as resolved.
Show resolved Hide resolved
assert.strictEqual((acquisitionSdk.AcquisitionManager as any)._apiCallsDisabled, true);
acquisition = acquisition = new acquisitionSdk.AcquisitionManager(new mockApi.CustomResponseHttpRequester(invalidJsonResponse), configuration);
(acquisitionSdk.AcquisitionManager as any)._apiCallsDisabled = false;
}));

done();
})

it("doesn't disable api calls on successful response", (done: Mocha.Done): void => {
DmitriyKirakosyan marked this conversation as resolved.
Show resolved Hide resolved
var acquisition = new acquisitionSdk.AcquisitionManager(new mockApi.HttpRequester(), configuration);
mockApi.serverUrl = "https://codepush.appcenter.ms";
updateMockUrl();

acquisition.queryUpdateWithCurrentPackage(templateCurrentPackage, (error: Error, returnPackage: acquisitionSdk.RemotePackage | acquisitionSdk.NativeUpdateNotification) => {
DmitriyKirakosyan marked this conversation as resolved.
Show resolved Hide resolved
assert.strictEqual((acquisitionSdk.AcquisitionManager as any)._apiCallsDisabled, false);
});

acquisition.queryUpdateWithCurrentPackage(templateCurrentPackage, (error: Error, returnPackage: acquisitionSdk.RemotePackage | acquisitionSdk.NativeUpdateNotification) => {
assert.notStrictEqual(returnPackage, null);
});

acquisition.reportStatusDeploy(templateCurrentPackage, acquisitionSdk.AcquisitionStatus.DeploymentSucceeded, "1.5.0", mockApi.validDeploymentKey, ((error: Error, parameter: void): void => {
DordeDimitrijev marked this conversation as resolved.
Show resolved Hide resolved
assert.strictEqual((acquisitionSdk.AcquisitionManager as any)._apiCallsDisabled, false);
}));

acquisition.reportStatusDownload(templateCurrentPackage, ((error: Error, parameter: void): void => {
assert.strictEqual((acquisitionSdk.AcquisitionManager as any)._apiCallsDisabled, false);
}));

done();
})

});

DmitriyKirakosyan marked this conversation as resolved.
Show resolved Hide resolved
function clone<T>(initialObject: T): T {
Expand Down