/** * 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(); } }