UNPKG

rest-hapi

Version:
617 lines (562 loc) 19.2 kB
'use strict' const Boom = require('@hapi/boom') const handlerHelper = require('./handler-helper') const config = require('../config') const { truncatedStringify } = require('./log-util') // TODO: add bulk delete/delete many // TODO: consolidate eventLog functionality // TODO-DONE: make returns more consistent/return all reply's // TODO: make sure pre and post is supported for appropriate endpoints // TODO: handle errors/status responses appropriately // TODO: include option to set all default fields to NULL so they exist and are returned with queries // TODO: possibly refactor/remove routeOptions // TODO: apply .lean() before any exec() to speed up execution time when returning data // TODO: possibly execute .toJSON() on all return data to reduce data size // TODO: look into using glue // TODO: abstract mongoose logic into CRUD utility methods that can be called directly with rest-hapi plugin // TODO:(cont) This will allow users to CRUD data in extra endpoints using rest-hapi functions. module.exports = function() { return { /** * Handles incoming GET requests to /RESOURCE * @param model: A mongoose model. * @param options: Options object. * @param logger: A logging object. * @returns {Function} A handler function */ generateListHandler: generateListHandler, /** * Handles incoming GET requests to /RESOURCE/{_id} * @param model: A mongoose model. * @param options: Options object. * @param logger: A logging object. * @returns {Function} A handler function */ generateFindHandler: generateFindHandler, /** * Handles incoming POST requests to /RESOURCE * @param model: A mongoose model. * @param options: Options object. * @param logger: A logging object. * @returns {Function} A handler function */ generateCreateHandler: generateCreateHandler, /** * Handles incoming DELETE requests to /RESOURCE/{_id} * @param model: A mongoose model. * @param options: Options object. * @param logger: A logging object. * @returns {Function} A handler function */ generateDeleteHandler: generateDeleteHandler, /** * Handles incoming UPDATE requests to /RESOURCE/{_id} * @param model: A mongoose model. * @param options: Options object. * @param logger: A logging object. * @returns {Function} A handler function */ generateUpdateHandler: generateUpdateHandler, /** * Handles incoming PUT requests to /OWNER_RESOURCE/{ownerId}/CHILD_RESOURCE/{childId} * @param ownerModel: A mongoose model. * @param association: An object containing the association data/child mongoose model. * @param options: Options object. * @param logger: A logging object. * @returns {Function} A handler function */ generateAssociationAddOneHandler: generateAssociationAddOneHandler, /** * Handles incoming DELETE requests to /OWNER_RESOURCE/{ownerId}/CHILD_RESOURCE/{childId} * @param ownerModel: A mongoose model. * @param association: An object containing the association data/child mongoose model. * @param options: Options object. * @param logger: A logging object. * @returns {Function} A handler function */ generateAssociationRemoveOneHandler: generateAssociationRemoveOneHandler, /** * Handles incoming POST requests to /OWNER_RESOURCE/{ownerId}/CHILD_RESOURCE * @param ownerModel: A mongoose model. * @param association: An object containing the association data/child mongoose model. * @param options: Options object. * @param logger: A logging object. * @returns {Function} A handler function */ generateAssociationAddManyHandler: generateAssociationAddManyHandler, /** * Handles incoming DELETE requests to /OWNER_RESOURCE/{ownerId}/CHILD_RESOURCE * @param ownerModel: A mongoose model. * @param association: An object containing the association data/child mongoose model. * @param options: Options object. * @param logger: A logging object. * @returns {Function} A handler function */ generateAssociationRemoveManyHandler: generateAssociationRemoveManyHandler, /** * Handles incoming GET requests to /OWNER_RESOURCE/{ownerId}/CHILD_RESOURCE * @param ownerModel: A mongoose model. * @param association: An object containing the association data/child mongoose model. * @param options: Options object. * @param logger: A logging object. * @returns {Function} A handler function */ generateAssociationGetAllHandler: generateAssociationGetAllHandler } } /** * Handles incoming GET requests to /RESOURCE * @param model: A mongoose model. * @param options: Options object. * @param logger: A logging object. * @returns {Function} A handler function */ function generateListHandler(model, options, logger) { const Log = logger.bind() options = options || {} return async function(request, h) { try { if (config.truncateLogs) { Log.info( 'params(%s)\nquery(%s)\npayload(%s)\n', truncatedStringify(request.params, config.truncateStringLength), truncatedStringify(request.query, config.truncateStringLength), truncatedStringify(request.payload, config.truncateStringLength) ) } else { Log.info( 'params(%s)\nquery(%s)\npayload(%s)\n', JSON.stringify(request.params, null, 2), JSON.stringify(request.query, null, 2), JSON.stringify(request.payload, null, 2) ) } const result = await handlerHelper.listHandler(model, request, Log) delete result.pageData return h.response(result).code(200) } catch (err) { handleError(err, Log) } } } /** * Handles incoming GET requests to /RESOURCE/{_id} * @param model: A mongoose model. * @param options: Options object. * @param logger: A logging object. * @returns {Function} A handler function */ function generateFindHandler(model, options, logger) { const Log = logger.bind() options = options || {} return async function(request, h) { try { if (config.truncateLogs) { Log.info( 'params(%s)\nquery(%s)\npayload(%s)\n', truncatedStringify(request.params, config.truncateStringLength), truncatedStringify(request.query, config.truncateStringLength), truncatedStringify(request.payload, config.truncateStringLength) ) } else { Log.info( 'params(%s)\nquery(%s)\npayload(%s)\n', JSON.stringify(request.params, null, 2), JSON.stringify(request.query, null, 2), JSON.stringify(request.payload, null, 2) ) } const result = await handlerHelper.findHandler( model, request.params._id, request, Log ) return h.response(result).code(200) } catch (err) { handleError(err, Log) } } } /** * Handles incoming POST requests to /RESOURCE * @param model: A mongoose model. * @param options: Options object. * @param logger: A logging object. * @returns {Function} A handler function */ function generateCreateHandler(model, options, logger) { const Log = logger.bind() options = options || {} return async function(request, h) { try { if (config.truncateLogs) { Log.info( 'params(%s)\nquery(%s)\npayload(%s)\n', truncatedStringify(request.params, config.truncateStringLength), truncatedStringify(request.query, config.truncateStringLength), truncatedStringify(request.payload, config.truncateStringLength) ) } else { Log.info( 'params(%s)\nquery(%s)\npayload(%s)\n', JSON.stringify(request.params, null, 2), JSON.stringify(request.query, null, 2), JSON.stringify(request.payload, null, 2) ) } const result = await handlerHelper.createHandler(model, request, Log) return h.response(result).code(201) } catch (err) { handleError(err, Log) } } } /** * Handles incoming UPDATE requests to /RESOURCE/{_id} * @param model: A mongoose model. * @param options: Options object. * @param logger: A logging object. * @returns {Function} A handler function */ function generateUpdateHandler(model, options, logger) { const Log = logger.bind() options = options || {} return async function(request, h) { try { if (config.truncateLogs) { Log.info( 'params(%s)\nquery(%s)\npayload(%s)\n', truncatedStringify(request.params, config.truncateStringLength), truncatedStringify(request.query, config.truncateStringLength), truncatedStringify(request.payload, config.truncateStringLength) ) } else { Log.info( 'params(%s)\nquery(%s)\npayload(%s)\n', JSON.stringify(request.params, null, 2), JSON.stringify(request.query, null, 2), JSON.stringify(request.payload, null, 2) ) } const result = await handlerHelper.updateHandler( model, request.params._id, request, Log ) return h.response(result).code(200) } catch (err) { handleError(err, Log) } } } /** * Handles incoming DELETE requests to /RESOURCE/{_id} or /RESOURCE * @param model: A mongoose model. * @param options: Options object. * @param logger: A logging object. * @returns {Function} A handler function */ function generateDeleteHandler(model, options, logger) { const Log = logger.bind() options = options || {} return async function(request, h) { try { if (config.truncateLogs) { Log.info( 'params(%s)\nquery(%s)\npayload(%s)\n', truncatedStringify(request.params, config.truncateStringLength), truncatedStringify(request.query, config.truncateStringLength), truncatedStringify(request.payload, config.truncateStringLength) ) } else { Log.info( 'params(%s)\nquery(%s)\npayload(%s)\n', JSON.stringify(request.params, null, 2), JSON.stringify(request.query, null, 2), JSON.stringify(request.payload, null, 2) ) } if (request.params._id) { const hardDelete = request.payload ? request.payload.hardDelete : false await handlerHelper.deleteOneHandler( model, request.params._id, hardDelete, request, Log ) } else { await handlerHelper.deleteManyHandler(model, request, Log) } return h.response().code(204) } catch (err) { handleError(err, Log) } } } /** * Handles incoming PUT requests to /OWNER_RESOURCE/{ownerId}/CHILD_RESOURCE/{childId} * @param ownerModel: A mongoose model. * @param association: An object containing the association data/child mongoose model. * @param options: Options object. * @param logger: A logging object. * @returns {Function} A handler function */ function generateAssociationAddOneHandler( ownerModel, association, options, logger ) { const Log = logger.bind() const associationName = association.include.as const childModel = association.include.model const addMethodName = 'addOne' + associationName[0].toUpperCase() + associationName.slice(1, -1) return async function(request, h) { try { if (config.truncateLogs) { Log.info( addMethodName + ' + params(%s)\nquery(%s)\npayload(%s)\n', truncatedStringify(request.params, config.truncateStringLength), truncatedStringify(request.query, config.truncateStringLength), truncatedStringify(request.payload, config.truncateStringLength) ) } else { Log.info( addMethodName + ' + params(%s)\nquery(%s)\npayload(%s)\n', JSON.stringify(request.params, null, 2), JSON.stringify(request.query, null, 2), JSON.stringify(request.payload, null, 2) ) } await handlerHelper.addOneHandler( ownerModel, request.params.ownerId, childModel, request.params.childId, associationName, request, Log ) return h.response().code(204) } catch (err) { handleError(err, Log) } } } /** * Handles incoming DELETE requests to /OWNER_RESOURCE/{ownerId}/CHILD_RESOURCE/{childId} * @param ownerModel: A mongoose model. * @param association: An object containing the association data/child mongoose model. * @param options: Options object. * @param logger: A logging object. * @returns {Function} A handler function */ function generateAssociationRemoveOneHandler( ownerModel, association, options, logger ) { const Log = logger.bind() const associationName = association.include.as const childModel = association.include.model const removeMethodName = 'removeOne' + associationName[0].toUpperCase() + associationName.slice(1, -1) return async function(request, h) { try { if (config.truncateLogs) { Log.info( removeMethodName + ' + params(%s)\nquery(%s)\npayload(%s)\n', truncatedStringify(request.params, config.truncateStringLength), truncatedStringify(request.query, config.truncateStringLength), truncatedStringify(request.payload, config.truncateStringLength) ) } else { Log.info( removeMethodName + ' + params(%s)\nquery(%s)\npayload(%s)\n', JSON.stringify(request.params, null, 2), JSON.stringify(request.query, null, 2), JSON.stringify(request.payload, null, 2) ) } await handlerHelper.removeOneHandler( ownerModel, request.params.ownerId, childModel, request.params.childId, associationName, request, Log ) return h.response().code(204) } catch (err) { handleError(err, Log) } } } /** * Handles incoming DELETE requests to /OWNER_RESOURCE/{ownerId}/CHILD_RESOURCE * @param ownerModel: A mongoose model. * @param association: An object containing the association data/child mongoose model. * @param options: Options object. * @param logger: A logging object. * @returns {Function} A handler function */ function generateAssociationAddManyHandler( ownerModel, association, options, logger ) { const Log = logger.bind() const associationName = association.include.as const childModel = association.include.model const addMethodName = 'addMany' + associationName[0].toUpperCase() + associationName.slice(1) return async function(request, h) { try { if (config.truncateLogs) { Log.info( addMethodName + ' + params(%s)\nquery(%s)\npayload(%s)\n', truncatedStringify(request.params, config.truncateStringLength), truncatedStringify(request.query, config.truncateStringLength), truncatedStringify(request.payload, config.truncateStringLength) ) } else { Log.info( addMethodName + ' + params(%s)\nquery(%s)\npayload(%s)\n', JSON.stringify(request.params, null, 2), JSON.stringify(request.query, null, 2), JSON.stringify(request.payload, null, 2) ) } await handlerHelper.addManyHandler( ownerModel, request.params.ownerId, childModel, associationName, request, Log ) return h.response().code(204) } catch (err) { handleError(err, Log) } } } // TODO: need to make sure removeMany calls are sequential, otherwise errors occur /** * Handles incoming POST requests to /OWNER_RESOURCE/{ownerId}/CHILD_RESOURCE * @param ownerModel: A mongoose model. * @param association: An object containing the association data/child mongoose model. * @param options: Options object. * @param logger: A logging object. * @returns {Function} A handler function */ function generateAssociationRemoveManyHandler( ownerModel, association, options, logger ) { const Log = logger.bind() const associationName = association.include.as const childModel = association.include.model const removeMethodName = 'removeMany' + associationName[0].toUpperCase() + associationName.slice(1) return async function(request, h) { try { if (config.truncateLogs) { Log.info( removeMethodName + ' + params(%s)\nquery(%s)\npayload(%s)\n', truncatedStringify(request.params, config.truncateStringLength), truncatedStringify(request.query, config.truncateStringLength), truncatedStringify(request.payload, config.truncateStringLength) ) } else { Log.info( removeMethodName + ' + params(%s)\nquery(%s)\npayload(%s)\n', JSON.stringify(request.params, null, 2), JSON.stringify(request.query, null, 2), JSON.stringify(request.payload, null, 2) ) } await handlerHelper.removeManyHandler( ownerModel, request.params.ownerId, childModel, associationName, request, Log ) return h.response().code(204) } catch (err) { handleError(err, Log) } } } /** * Handles incoming GET requests to /OWNER_RESOURCE/{ownerId}/CHILD_RESOURCE * @param ownerModel: A mongoose model. * @param association: An object containing the association data/child mongoose model. * @param options: Options object. * @param logger: A logging object. * @returns {Function} A handler function */ function generateAssociationGetAllHandler( ownerModel, association, options, logger ) { const Log = logger.bind() const associationName = association.include.as const childModel = association.include.model const getAllMethodName = association.getAllMethodName || 'get' + associationName[0].toUpperCase() + associationName.slice(1) return async function(request, h) { try { if (config.truncateLogs) { Log.info( getAllMethodName + ' + params(%s)\nquery(%s)\npayload(%s)\n', truncatedStringify(request.params, config.truncateStringLength), truncatedStringify(request.query, config.truncateStringLength), truncatedStringify(request.payload, config.truncateStringLength) ) } else { Log.info( getAllMethodName + ' + params(%s)\nquery(%s)\npayload(%s)\n', JSON.stringify(request.params, null, 2), JSON.stringify(request.query, null, 2), JSON.stringify(request.payload, null, 2) ) } const result = await handlerHelper.getAllHandler( ownerModel, request.params.ownerId, childModel, associationName, request, Log ) return h.response(result).code(200) } catch (err) { handleError(err, Log) } } } function handleError(err, logger) { if (!err.isBoom) { logger.error(err) throw Boom.badImplementation('There was an error processing the request.') } else { throw err } }