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

Use async and await for ajax #1410

Merged
merged 4 commits into from
Jan 15, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 37 additions & 42 deletions SETUP/tests/jsTests/ajaxTests.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,77 +4,72 @@
let codeUrl = "https://www.dummy.org";

QUnit.module("Ajax test", function () {
QUnit.test("Return correct data", function (assert) {
QUnit.test("Return correct data", async function (assert) {
function fetchPromise() {
const blob = new Blob([JSON.stringify({ key: "value" })], { type: "application/json" });
const init = { status: 200, headers: { "content-type": "application/json" } };
return Promise.resolve(new Response(blob, init));
}

return ajax("GET", "myUrl", {}, {}, fetchPromise).then(function (data) {
assert.deepEqual(data, { key: "value" });
});
const data = await ajax("GET", "myUrl", {}, {}, fetchPromise);
assert.deepEqual(data, { key: "value" });
});

QUnit.test("Wrong type of data", function (assert) {
QUnit.test("Wrong type of data", async (assert) => {
function fetchPromise() {
const blob = new Blob([JSON.stringify("mydata")], { type: "application/json" });
const init = { status: 200, headers: { "content-type": "text/html" } };
return Promise.resolve(new Response(blob, init));
}

return ajax("GET", "myUrl", {}, {}, fetchPromise).then(
function () {},
function (data) {
assert.deepEqual(data, { error: "Incorrect response type", code: AJAX_ERROR_CODES.INCORRECT_RESPONSE_TYPE });
},
);
try {
await ajax("GET", "myUrl", {}, {}, fetchPromise);
} catch (error) {
assert.strictEqual(error.message, "Incorrect response type");
assert.strictEqual(error.code, AJAX_ERROR_CODES.INCORRECT_RESPONSE_TYPE);
assert.strictEqual(error.name, "AjaxError");
}
});

QUnit.test("Status 404", function (assert) {
QUnit.test("Status 404", async function (assert) {
function fetchPromise() {
const blob = new Blob([JSON.stringify({ error: "not found" })], { type: "application/json" });
const init = { status: 404, headers: { "content-type": "application/json" } };
return Promise.resolve(new Response(blob, init));
}

return ajax("GET", "myUrl", {}, {}, fetchPromise).then(
function () {},
function (data) {
assert.strictEqual(data.error, "not found");
},
);
try {
await ajax("GET", "myUrl", {}, {}, fetchPromise);
} catch (error) {
assert.strictEqual(error.message, "not found");
assert.strictEqual(error.code, 404);
}
});

QUnit.test("Unknown error", function (assert) {
QUnit.test("Unknown error", async function (assert) {
function fetchPromise() {
const blob = new Blob([JSON.stringify("")], { type: "application/json" });
const init = { status: 404, headers: { "content-type": "application/json" } };
return Promise.resolve(new Response(blob, init));
}

return ajax("GET", "myUrl", {}, {}, fetchPromise).then(
function () {},
function (data) {
assert.deepEqual(data, { error: "Unknown error", code: AJAX_ERROR_CODES.UNKNOWN_ERROR });
},
);
try {
await ajax("GET", "myUrl", {}, {}, fetchPromise);
} catch (error) {
assert.strictEqual(error.message, "Unknown error");
assert.strictEqual(error.code, AJAX_ERROR_CODES.UNKNOWN_ERROR);
}
});

QUnit.test("Network error", function (assert) {
QUnit.test("Network error", async function (assert) {
function fetchPromise() {
return Promise.reject();
return Promise.reject("Network error");
}
try {
await ajax("GET", "myUrl", {}, {}, fetchPromise);
} catch (error) {
assert.strictEqual(error.message, "Network error");
assert.strictEqual(error.code, AJAX_ERROR_CODES.NETWORK_ERROR);
}

return ajax("GET", "myUrl", {}, {}, fetchPromise).then(
function () {},
function (data) {
assert.deepEqual(data, { error: "Network error", code: AJAX_ERROR_CODES.NETWORK_ERROR });
},
);
});

QUnit.test("Check sent data", function (assert) {
QUnit.test("Check sent data", async function (assert) {
function fetchPromise(url, options) {
assert.strictEqual(url.href, "https://www.dummy.org/api/index.php?url=myUrl&querykey=queryvalue");
assert.strictEqual(options.method, "POST");
Expand All @@ -87,17 +82,17 @@ QUnit.module("Ajax test", function () {
return Promise.resolve(new Response(blob, init));
}

return ajax("POST", "myUrl", { querykey: "queryvalue" }, { datakey: "datavalue" }, fetchPromise).then();
await ajax("POST", "myUrl", { querykey: "queryvalue" }, { datakey: "datavalue" }, fetchPromise);
});

QUnit.test("Check array parameter", function (assert) {
QUnit.test("Check array parameter", async function (assert) {
function fetchPromise(url) {
const blob = new Blob([JSON.stringify({ key: "value" })], { type: "application/json" });
assert.strictEqual(url.href, "https://www.dummy.org/api/index.php?url=myUrl&key%5B%5D=value1&key%5B%5D=value2");
const init = { status: 200, headers: { "content-type": "application/json" } };
return Promise.resolve(new Response(blob, init));
}

return ajax("GET", "myUrl", { key: ["value1", "value2"] }, {}, fetchPromise).then();
await ajax("GET", "myUrl", { key: ["value1", "value2"] }, {}, fetchPromise);
});
});
54 changes: 27 additions & 27 deletions scripts/api.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
/*global codeUrl */
/* exported ajax */
/* exported ajaxAlert */
/* exported AJAX_ERROR_CODES */

const AJAX_ERROR_CODES = {
Expand All @@ -9,7 +8,15 @@ const AJAX_ERROR_CODES = {
NETWORK_ERROR: 997,
};

function ajax(method, apiUrl, queryParams = {}, data = {}, fetchPromise = fetch) {
class AjaxError extends Error {
constructor(message, code) {
super(message);
this.code = code;
this.name = "AjaxError";
}
}

async function ajax(method, apiUrl, queryParams = {}, data = {}, fetchPromise = fetch) {
let url = new URL(codeUrl + "/api/index.php");
let searchParams = new URLSearchParams();
searchParams.append("url", apiUrl);
Expand All @@ -35,29 +42,22 @@ function ajax(method, apiUrl, queryParams = {}, data = {}, fetchPromise = fetch)
options.headers["Content-Type"] = "application/json";
options.body = JSON.stringify(data);
}
return new Promise(function (resolve, reject) {
fetchPromise(url, options)
.then(function (response) {
const contentType = response.headers.get("content-type");
if (!contentType || !contentType.includes("application/json")) {
reject({ error: "Incorrect response type", code: AJAX_ERROR_CODES.INCORRECT_RESPONSE_TYPE });
} else if (response.ok) {
resolve(response.json());
} else {
response.json().then(function (data) {
if (!data) {
data = { error: "Unknown error", code: AJAX_ERROR_CODES.UNKNOWN_ERROR };
}
reject(data);
});
}
})
.catch(function () {
reject({ error: "Network error", code: AJAX_ERROR_CODES.NETWORK_ERROR });
});
});
}

function ajaxAlert(data) {
alert(data.error);
let response;
try {
response = await fetchPromise(url, options);
} catch (error) {
throw new AjaxError(error, AJAX_ERROR_CODES.NETWORK_ERROR);
}
const contentType = response.headers.get("content-type");
if (!contentType || !contentType.includes("application/json")) {
throw new AjaxError("Incorrect response type", AJAX_ERROR_CODES.INCORRECT_RESPONSE_TYPE);
}
const responseData = await response.json();
Copy link
Collaborator

Choose a reason for hiding this comment

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

we probably shouldn't call .json() until after we confirm the response is ok.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

We need to decode the result even if it is not ok so this avoids doing it twice.

70ray marked this conversation as resolved.
Show resolved Hide resolved
70ray marked this conversation as resolved.
Show resolved Hide resolved
if (!response.ok) {
if (!responseData) {
throw new AjaxError("Unknown error", AJAX_ERROR_CODES.UNKNOWN_ERROR);
}
throw new AjaxError(responseData.error, response.status);
}
return responseData;
}
Loading