diff options
Diffstat (limited to 'src/app/db/service/base/response.js')
| -rw-r--r-- | src/app/db/service/base/response.js | 118 |
1 files changed, 118 insertions, 0 deletions
diff --git a/src/app/db/service/base/response.js b/src/app/db/service/base/response.js new file mode 100644 index 0000000..a4bac60 --- /dev/null +++ b/src/app/db/service/base/response.js @@ -0,0 +1,118 @@ +/** + * Service API responses + * @module app/db/service/base/response + */ + +import { zipCSVs, stringifyCSV, sanitizeCSV } from "app/utils/file_utils"; +import debugModule from "debug"; + +/** + * Debug logger + */ +const debug = debugModule("shoebox:service"); + +/** + * Middleware to return the response as JSON or CSV + */ +export async function sendResponse(request, response) { + if (response.locals.csv) { + if (response.locals.csv.files) { + await sendCSVZipResponse(response); + } else { + await sendCSVResponse(response); + } + } else { + await sendJSONResponse(response); + } +} + +/** + * Respond with a ZIP file containing multiple CSVs + * @param {Response} response the response object + */ +async function sendCSVZipResponse(response) { + const zipData = await zipCSVs(response.locals.csv.files); + response.set("Content-Type", "application/zip"); + response.set( + "Content-Disposition", + `attachment; filename=${response.locals.csv.filename}` + ); + response.set("Content-Length", zipData.length); + response.set("Access-Control-Expose-Headers", "Content-Disposition"); + response.write(zipData, "binary"); + response.end(null, "binary"); +} + +/** + * Respond with a single CSV + * @param {Response} response the response object + */ +async function sendCSVResponse(response) { + const csvData = await stringifyCSV(sanitizeCSV(response.locals.csv.data)); + response.set("Content-Type", "text/csv; charset=utf-8"); + response.set( + "Content-Disposition", + `attachment; filename=${response.locals.csv.filename}` + ); + response.set("Content-Length", csvData.length); + response.set("Access-Control-Expose-Headers", "Content-Disposition"); + response.send(csvData); +} + +/** + * Respond with JSON + * @param {Response} response the response object + */ +async function sendJSONResponse(response) { + response.json(response.locals); +} + +/** + * Error-handling middleware + * @param {Error} error an error object + * @param {express.request} request the request object + * @param {express.response} response the response object + * @param {function} next function to proceed to the next middleware + */ +export function handleError(error, request, response, next) { + const { errors } = request.service.options; + debug("Error", error.name, error.message); + let message; + + if (errors) { + Object.keys(errors).some((key) => { + if (error.message.match(key)) { + message = errors[key]; + return true; + } + return false; + }); + if (message) { + response.status(403).send({ code: 403, error: message }); + return; + } + } + + if (error.name === "UnauthorizedError") { + debug("Unauthorized"); + response.status(401).send({ code: 401, error: error.message }); + } else if (error.message === "EmptyResponse") { + debug("Not found"); + response.status(404).send({ code: 404, error: error.message }); + } else if (error.message === "UserNotActive") { + debug("User not active"); + response.status(401).send({ code: 401, error: error.message }); + } else if (error.message === "PermissionsError") { + debug("Insufficient permissions"); + response.status(401).send({ code: 401, error: error.message }); + } else { + debug("Unexpected error"); + // debug(error); + response.status(403).send({ + code: 403, + error: error.name || error.message, + message: error.message, + }); + next(); + } +} |
