diff --git a/app/javascript/src/admin/micro-clusters/macroClusters.js b/app/javascript/src/admin/micro-clusters/macroClusters.js index e080c93ad..53b2dc3ca 100644 --- a/app/javascript/src/admin/micro-clusters/macroClusters.js +++ b/app/javascript/src/admin/micro-clusters/macroClusters.js @@ -68,7 +68,8 @@ export const updateMacroCluster = (id, dispatch) => { const loadMacroClusterPage = async (page) => { const response = await getRequest( - `/admins/macro_clusters.json?per_page=25&page=${page}` + `/admins/macro_clusters.json?per_page=25&page=${page}`, + 3 // timeout after 3s ); return await response.json(); }; diff --git a/app/javascript/src/fetch.js b/app/javascript/src/fetch.js index 8f18d6e14..4d69aa7ba 100644 --- a/app/javascript/src/fetch.js +++ b/app/javascript/src/fetch.js @@ -1,39 +1,52 @@ import "whatwg-fetch"; -export function deleteRequest(path) { - return request(path, "DELETE"); +export function deleteRequest(path, timeout = 30) { + return request(path, "DELETE", undefined, timeout); } -export function getRequest(path) { - return request(path, "GET"); +export function getRequest(path, timeout = 30) { + return request(path, "GET", undefined, timeout); } -export function postRequest(path, body) { - return request(path, "POST", body); +export function postRequest(path, body, timeout = 30) { + return request(path, "POST", body, timeout); } -export function putRequest(path, body) { - return request(path, "PUT", body); +export function putRequest(path, body, timeout = 30) { + return request(path, "PUT", body, timeout); } -function request(path, method, body) { - return req(path, method, body); +function request(path, method, body, timeout) { + return req(path, method, body, timeout); } -async function req(path, method, body, retries = 3) { - const response = await fetch(path, { - credentials: "same-origin", - method: method, - body: JSON.stringify(body), - headers: { - Accept: "application/vnd.api+json", - "Content-Type": "application/vnd.api+json", - "X-CSRF-Token": csrfToken() +async function req(path, method, body, timeout, retries = 3) { + let response; + try { + let extra = {}; + try { + extra = { signal: AbortSignal.timeout(timeout * 1000) }; + } catch (_e) { + // Ignore the error } - }); - if (method === "GET" && response.status >= 500 && retries > 0) { + response = await fetch(path, { + credentials: "same-origin", + method: method, + body: JSON.stringify(body), + headers: { + Accept: "application/vnd.api+json", + "Content-Type": "application/vnd.api+json", + "X-CSRF-Token": csrfToken() + }, + ...extra + }); + } catch (e) { + console.error("Failed to fetch", e); + } + const failure = !response || response.status >= 500; + if (method === "GET" && failure && retries > 0) { console.log("Retrying", path, method, body, retries); - return req(path, method, body, retries - 1); + return req(path, method, body, timeout, retries - 1); } else { return response; } diff --git a/eslint.config.mjs b/eslint.config.mjs index 91dab6b95..6e48275f0 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -43,6 +43,11 @@ export default [ { ignores: ["vendor/"] }, + { + rules: { + "no-unused-vars": ["error", { caughtErrors: "none" }] + } + }, { files: ["**/*.js", "**/*.jsx"],