costco-grocery-list/backend/utils/http.js

117 lines
2.3 KiB
JavaScript

function isPlainObject(value) {
return (
value !== null &&
typeof value === "object" &&
!Array.isArray(value) &&
Object.prototype.toString.call(value) === "[object Object]"
);
}
function errorCodeFromStatus(statusCode) {
switch (statusCode) {
case 400:
return "bad_request";
case 401:
return "unauthorized";
case 403:
return "forbidden";
case 404:
return "not_found";
case 409:
return "conflict";
case 413:
return "payload_too_large";
case 415:
return "unsupported_media_type";
case 422:
return "unprocessable_entity";
case 429:
return "rate_limited";
case 500:
return "internal_error";
default:
return statusCode >= 500 ? "internal_error" : "request_error";
}
}
function normalizeErrorPayload(payload, statusCode) {
if (statusCode < 400) return payload;
if (typeof payload === "string") {
return {
error: {
code: errorCodeFromStatus(statusCode),
message: payload,
},
};
}
if (!isPlainObject(payload)) {
return {
error: {
code: errorCodeFromStatus(statusCode),
message: "Request failed",
},
};
}
if (isPlainObject(payload.error)) {
const code = payload.error.code || errorCodeFromStatus(statusCode);
const message = payload.error.message || "Request failed";
return {
...payload,
error: {
...payload.error,
code,
message,
},
};
}
if (typeof payload.error === "string") {
const { error, ...rest } = payload;
return {
...rest,
error: {
code: errorCodeFromStatus(statusCode),
message: error,
},
};
}
if (typeof payload.message === "string") {
const { message, ...rest } = payload;
return {
...rest,
error: {
code: errorCodeFromStatus(statusCode),
message,
},
};
}
return {
...payload,
error: {
code: errorCodeFromStatus(statusCode),
message: "Request failed",
},
};
}
function sendError(res, statusCode, message, code, extra = {}) {
return res.status(statusCode).json({
...extra,
error: {
code: code || errorCodeFromStatus(statusCode),
message,
},
});
}
module.exports = {
errorCodeFromStatus,
normalizeErrorPayload,
sendError,
};