General source code refactoring
This commit is contained in:
169
src/services/http/background-json.js
Normal file
169
src/services/http/background-json.js
Normal file
@@ -0,0 +1,169 @@
|
||||
function createHttpBackgroundJson({
|
||||
DEBUG_BODY_LIMIT,
|
||||
environment
|
||||
}) {
|
||||
const { fetch } = environment;
|
||||
async function fetchJson(url, options, requestName, debugRequests = null) {
|
||||
const startedAt = new Date();
|
||||
const debugEntry = createBackgroundDebugEntry(requestName, url, options, startedAt);
|
||||
let response;
|
||||
|
||||
try {
|
||||
response = await fetch(url, options);
|
||||
} catch (error) {
|
||||
completeBackgroundDebugEntry(debugEntry, null, "", error.message || "Network request failed.");
|
||||
appendBackgroundDebugEntry(debugRequests, debugEntry);
|
||||
throw error;
|
||||
}
|
||||
|
||||
const responsePayload = await readResponsePayload(response);
|
||||
const responseBody = createDebugBody(responsePayload.text);
|
||||
completeBackgroundDebugEntry(debugEntry, response, responseBody, "");
|
||||
debugEntry.metadata.responseContentType = response.headers.get("content-type") || "(not provided)";
|
||||
debugEntry.metadata.responseLength = String(responsePayload.text.length);
|
||||
appendBackgroundDebugEntry(debugRequests, debugEntry);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`${requestName} failed with status ${response.status}.`);
|
||||
}
|
||||
|
||||
if (responsePayload.error) {
|
||||
throw new Error(`${requestName} response body could not be read.`);
|
||||
}
|
||||
|
||||
try {
|
||||
return parseJsonResponse(responsePayload.text, requestName);
|
||||
} catch (error) {
|
||||
error.httpStatus = response.status;
|
||||
debugEntry.status = "failed";
|
||||
debugEntry.error = error.message;
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
function createBackgroundDebugEntry(requestName, url, options, startedAt) {
|
||||
return {
|
||||
name: requestName,
|
||||
method: options.method || "GET",
|
||||
url,
|
||||
status: "pending",
|
||||
startedAt: startedAt.toISOString(),
|
||||
completedAt: null,
|
||||
durationMs: null,
|
||||
httpStatus: null,
|
||||
statusText: "",
|
||||
requestHeaders: normalizeDebugHeaders(options.headers),
|
||||
requestBody: formatDebugRequestBody(options.body),
|
||||
metadata: {
|
||||
executionContext: "background"
|
||||
},
|
||||
responseHeaders: {},
|
||||
responseBody: "",
|
||||
responseBodyTruncated: false,
|
||||
error: ""
|
||||
};
|
||||
}
|
||||
|
||||
function completeBackgroundDebugEntry(entry, response, responseBody, errorMessage) {
|
||||
const completedAt = new Date();
|
||||
entry.completedAt = completedAt.toISOString();
|
||||
entry.durationMs = completedAt.getTime() - new Date(entry.startedAt).getTime();
|
||||
entry.error = errorMessage;
|
||||
|
||||
if (!response) {
|
||||
entry.status = "failed";
|
||||
return;
|
||||
}
|
||||
|
||||
entry.status = response.ok ? "success" : "failed";
|
||||
entry.httpStatus = response.status;
|
||||
entry.statusText = response.statusText || "";
|
||||
entry.responseHeaders = headersToObject(response.headers);
|
||||
entry.responseBody = responseBody.value;
|
||||
entry.responseBodyTruncated = responseBody.truncated;
|
||||
}
|
||||
|
||||
function appendBackgroundDebugEntry(debugRequests, entry) {
|
||||
if (Array.isArray(debugRequests)) {
|
||||
debugRequests.push(entry);
|
||||
}
|
||||
}
|
||||
|
||||
async function readResponsePayload(response) {
|
||||
try {
|
||||
return {
|
||||
text: await response.text(),
|
||||
error: ""
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
text: `Unable to read response body: ${error.message || "unknown error"}`,
|
||||
error: error.message || "unknown error"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function parseJsonResponse(text, requestName) {
|
||||
const normalizedText = String(text)
|
||||
.replace(/^\uFEFF/, "")
|
||||
.trimStart()
|
||||
.replace(/^\)\]\}',?\s*/, "")
|
||||
.replace(/^while\s*\(1\);\s*/, "");
|
||||
|
||||
try {
|
||||
return JSON.parse(normalizedText);
|
||||
} catch (error) {
|
||||
const invalidJsonError = new Error(`${requestName} returned an invalid JSON response.`);
|
||||
invalidJsonError.code = "INVALID_JSON_RESPONSE";
|
||||
throw invalidJsonError;
|
||||
}
|
||||
}
|
||||
|
||||
function createDebugBody(text) {
|
||||
if (text.length > DEBUG_BODY_LIMIT) {
|
||||
return {
|
||||
value: `${text.slice(0, DEBUG_BODY_LIMIT)}\n... truncated ${text.length - DEBUG_BODY_LIMIT} characters`,
|
||||
truncated: true
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
value: text || "(empty response body)",
|
||||
truncated: false
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeDebugHeaders(headers) {
|
||||
const normalized = {};
|
||||
|
||||
Object.entries(headers || {}).forEach(([key, value]) => {
|
||||
normalized[key] = String(value);
|
||||
});
|
||||
return normalized;
|
||||
}
|
||||
|
||||
function headersToObject(headers) {
|
||||
const values = {};
|
||||
|
||||
if (headers && typeof headers.forEach === "function") {
|
||||
headers.forEach((value, key) => {
|
||||
values[key] = value;
|
||||
});
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
function formatDebugRequestBody(body) {
|
||||
if (!body) {
|
||||
return "";
|
||||
}
|
||||
|
||||
try {
|
||||
return JSON.stringify(JSON.parse(body), null, 2);
|
||||
} catch (error) {
|
||||
return String(body);
|
||||
}
|
||||
}
|
||||
|
||||
return { fetchJson, createBackgroundDebugEntry, completeBackgroundDebugEntry, appendBackgroundDebugEntry, readResponsePayload, parseJsonResponse, createDebugBody, normalizeDebugHeaders, headersToObject, formatDebugRequestBody };
|
||||
}
|
||||
Reference in New Issue
Block a user