diff --git a/src/utils/errors.js b/src/utils/errors.js new file mode 100644 index 0000000..3f42ec5 --- /dev/null +++ b/src/utils/errors.js @@ -0,0 +1,50 @@ +const errorHandler = (err, environment) => { + // no stacktraces leaked to user + const responseJson = {} + console.error(err) + switch (err.code) { + case 'ER_DUP_ENTRY': + responseJson.message = 'Conflict' + responseJson.code = 409 + break + case 'BAD_REQUEST': + responseJson.message = 'Bad Request' + responseJson.code = 400 + break + case 'NOT_FOUND': + responseJson.message = 'Not Found' + responseJson.code = 404 + break + case 'UNPROCESSABLE_ENTITY': + responseJson.message = 'Unprocessable Entity' + responseJson.code = 422 + break + default: + responseJson.message = 'Server Error' + responseJson.code = 500 + break + } + + // development error handler + // will print stacktrace + if (environment === 'development' || environment === 'test') { + responseJson.error = err + } + return responseJson +} + +const error404 = () => { + const err = new Error('Not Found') + err.code = 'NOT_FOUND' + err.status = 404 + return err +} + +const error422 = (message = 'Unprocessable Entity') => { + const err = new Error(message) + err.code = 'UNPROCESSABLE_ENTITY' + err.status = 422 + return err +} + +export { error404, error422, errorHandler } diff --git a/src/utils/hasChildren.js b/src/utils/hasChildren.js new file mode 100644 index 0000000..ecdb8f8 --- /dev/null +++ b/src/utils/hasChildren.js @@ -0,0 +1,21 @@ +import { hasChildrenModel } from '../models/hasChildrenModel' + +const hasChildren = (req, res, next, config, { adapter, schema, table1, fieldNameTable1, uuidTable1, table2, fieldNameTable2 }) => { + return new Promise((resolve) => { + const conn = adapter.start(config) + resolve(hasChildrenModel({ adapter, schema, table1, fieldNameTable1, uuidTable1, table2, fieldNameTable2 }, conn) + .then((hasChildren) => { + const result = hasChildren === undefined || hasChildren.length === 0 ? [] : { hasChildren } + next(result) + return result + }) + .catch((err) => { + throw (err.message) + }) + .finally(() => { + adapter.end(conn) + })) + }) +} + +export { hasChildren } diff --git a/src/utils/hasChildrenValidator.js b/src/utils/hasChildrenValidator.js new file mode 100644 index 0000000..d42d3f6 --- /dev/null +++ b/src/utils/hasChildrenValidator.js @@ -0,0 +1,8 @@ +import { sendUnprocessableEntityResponse } from './responses' + +const hasChildrenValidator = (result, req, res, next, config) => { + return result.length === 0 + ? next(result) + : sendUnprocessableEntityResponse(res, config.environment) +} +export { hasChildrenValidator } diff --git a/src/utils/links.js b/src/utils/links.js new file mode 100644 index 0000000..673965d --- /dev/null +++ b/src/utils/links.js @@ -0,0 +1,23 @@ +const addLinks = (result, req, res, next, hasAddLinks, routes) => { + const links = { _links: routes } + + hasAddLinks ? next(Object.assign({}, links, result)) : next(result, req, res, next, routes) +} + +const getRoutes = ({ prefix, routes }) => routes + .filter(r => r.route) + .reduce((acc, route) => { + const capitalize = val => val.charAt(0).toUpperCase() + val.slice(1) + const entity = route.route.path + .split('/') + .map(val => val.replace(':', '')) + .map(val => val.replace('-', '')) + .map(capitalize) + .join('') + + const method = Object.keys(route.route.methods)[0] + const newEndpoint = { [method + entity]: `${method.toUpperCase()} - ${prefix}${route.route.path}` } + return { ...acc, ...newEndpoint } + }, {}) + +export { addLinks, getRoutes } diff --git a/src/utils/pagination.js b/src/utils/pagination.js new file mode 100644 index 0000000..dd039dc --- /dev/null +++ b/src/utils/pagination.js @@ -0,0 +1,12 @@ +const pagination = ({ limit, page }) => { + const validateLimit = Number.isNaN(Number(limit)) || limit === '' ? 100 : Math.abs(limit) + const validatePage = Number.isNaN(Number(page)) || page === '' ? 1 : Math.abs(page) + + const auxPage = Number.parseInt(validatePage) === 0 ? 1 : Number.parseInt(validatePage) + const auxLimit = Math.abs(Number.parseInt(validateLimit)) + + const offset = (Math.abs(auxPage) - 1) * auxLimit + return ` LIMIT ${auxLimit} OFFSET ${offset} ` +} + +export { pagination } diff --git a/src/utils/responses.js b/src/utils/responses.js new file mode 100644 index 0000000..f9787ed --- /dev/null +++ b/src/utils/responses.js @@ -0,0 +1,48 @@ +import { error422, errorHandler } from './errors.js' + +const sendOkResponse = (result, req, res) => { + res.status(200).json(result) +} +const sendCreatedResponse = (result, req, res) => { + res.status(201).json(result) +} + +const sendResponseNoContent = (result, req, res) => { + res.status(204).json(result) +} + +const sendResponseServerError = (res, err) => { + res.status(500).json(err) +} + +const sendResponseBadRequest = (res, err) => { + res.status(400).json(err) +} + +const sendResponseUnauthorized = (res, err) => { + return res.status(401).json(err) +} + +const sendResponseAccessDenied = (res, err) => { + return res.status(403).json(err) +} + +const sendResponseNotFound = (res, err) => { + res.status(404).json(err) +} + +const sendResponseUnprocessableEntity = (res, err) => { + res.status(422).json(err) +} + +const sendUnprocessableEntityResponse = (res, environment, err = error422()) => { + const error = errorHandler(err, environment) + res.status(422).json(error) +} + +const sendLoginSuccessfullResponse = (result, req, res) => { + res.status(201).json(result) + console.warn(`User ${result.user._data.username} has logged in`) +} + +export { sendLoginSuccessfullResponse, sendCreatedResponse, sendResponseAccessDenied, sendOkResponse, sendResponseBadRequest, sendResponseNoContent, sendResponseNotFound, sendResponseServerError, sendResponseUnauthorized, sendResponseUnprocessableEntity, sendUnprocessableEntityResponse }