diff --git a/lib/server_middleware/api.js b/lib/server_middleware/api.js index b63d075..b813cca 100755 --- a/lib/server_middleware/api.js +++ b/lib/server_middleware/api.js @@ -1,22 +1,22 @@ const express = require('express'); const url = require('url'); const expressBodyParser = require('body-parser'); -const { DEFAULT_PARSER_OPTIONS } = require('../utility/body_parser'); -const { getControllerFiles, getControllerMiddleware, controllerMappingServerSide } = require('../utility/controllers'); +const { DEFAULT_PARSER_OPTIONS } = require('../utility/body_parser'); +const { getControllerFiles, getControllerMiddleware, middlewareHandler, controllerMappingServerSide } = require('../utility/controllers'); /** * Action handler middleware. * * @param ControllerClass - * @param methodName + * @param actionName * @param options * @returns {Function} */ -function actionHandler(ControllerClass, methodName, options) { +function actionHandler(ControllerClass, actionName, options) { return function (req, res, next) { const controller = new ControllerClass(req); - return Promise.resolve(controller[methodName]({params: req.params, body: req.body, query: req.query})) + return Promise.resolve(controller[actionName]({params: req.params, body: req.body, query: req.query})) .then(function (result) { res.result = options.successHandler(result, req); next(); @@ -37,16 +37,20 @@ function createControllerRouter(controllerFile, options) { const ControllerClass = require(controllerFile); const routes = ControllerClass.ROUTES || {}; - Object.keys(routes).forEach(function (methodName) { - if (ControllerClass.prototype[methodName]) { - const {path, verb} = routes[methodName]; + Object.keys(routes).forEach(function (actionName) { + if (ControllerClass.prototype[actionName]) { + const {path, verb} = routes[actionName]; const controllerMiddleware = getControllerMiddleware(ControllerClass); router[verb.toLowerCase()]( path, - ...controllerMiddleware.all, // Controller middleware for all actions - ...(controllerMiddleware.actions && controllerMiddleware.actions[methodName] || []), // Action only middleware - actionHandler(ControllerClass, methodName, options) // Handle controller action method. + function (req, res, next) { + return middlewareHandler(controllerMiddleware.all, req) + .then(() => middlewareHandler(controllerMiddleware.actions && controllerMiddleware.actions[actionName] || [], req)) + .then(() => next()) + .catch(next); + }, + actionHandler(ControllerClass, actionName, options) // Handle controller action method. ); } }); @@ -122,7 +126,11 @@ function injectAPI(options) { // Middleware for all API routes if (options.middleware) { - router.use(...(Array.isArray(options.middleware) ? options.middleware : [options.middleware])); + router.use(function (req, res, next) { + return middlewareHandler(options.middleware, req) + .then(() => next()) + .catch(next); + }); } // Inject Controller routes diff --git a/lib/utility/controllers.js b/lib/utility/controllers.js index 0428d01..5434288 100755 --- a/lib/utility/controllers.js +++ b/lib/utility/controllers.js @@ -94,6 +94,19 @@ function controllerMapping(directory, controllerValue) { return mapping; } +/** + * Middleware handler for express router. + * + * @param middleware + * @returns {Object} + */ +function middlewareHandler(middleware, req) { + middleware = Array.isArray(middleware) ? middleware : middleware && [middleware] || []; + return Promise.all(middleware.map(function (fn) { + return Promise.resolve(fn(req)); + })); +} + /** * Route Controller mapping for client side api plugin. * @@ -118,6 +131,7 @@ function controllerMappingClientSide(directory) { * @returns {Object} */ function controllerMappingServerSide(directory, req, options) { + const apiMiddleware = options.middleware; let ResponseMiddleware = options.responseMiddleware ? require(options.responseMiddleware.replace(options.aliasKey, options.srcDir)) : null; @@ -127,13 +141,27 @@ function controllerMappingServerSide(directory, req, options) { : ResponseMiddleware; return controllerMapping(options.directory, function (ControllerClass, actionName) { + const controllerMiddleware = getControllerMiddleware(ControllerClass); + return function ({params, body, query} = {}) { try { - return Promise.resolve(new ControllerClass(req)[actionName]({ - params: params || {}, - body: body || {}, - query: query || {} - })) + return middlewareHandler(apiMiddleware, req) + .then(function () { + return middlewareHandler(controllerMiddleware.all, req); + }) + .then(function () { + return middlewareHandler( + controllerMiddleware.actions && controllerMiddleware.actions[actionName] || [], + req + ); + }) + .then(function () { + return Promise.resolve(new ControllerClass(req)[actionName]({ + params: params || {}, + body: body || {}, + query: query || {} + })) + }) .then(function (result) { return options.successHandler(result); }) @@ -153,6 +181,7 @@ function controllerMappingServerSide(directory, req, options) { module.exports = { getControllerFiles, getControllerMiddleware, + middlewareHandler, controllerMappingClientSide, controllerMappingServerSide }; diff --git a/lib/utility/http_errors.js b/lib/utility/http_errors.js index 9a4c35d..00ca02c 100755 --- a/lib/utility/http_errors.js +++ b/lib/utility/http_errors.js @@ -26,3 +26,10 @@ global.NotFoundError = class extends Error { this.statusCode = 404; } }; + +global.InternalServerError = class extends Error { + constructor(message = 'Internal Server Error') { + super(message); + this.statusCode = 500; + } +}; diff --git a/tests/api.middleware.test.js b/tests/api.middleware.test.js index 07305ff..e546289 100644 --- a/tests/api.middleware.test.js +++ b/tests/api.middleware.test.js @@ -4,30 +4,16 @@ test.before(globalBeforeAll({ moduleOptions: { prefix: '/api/v2', middleware: [ - function (req, res, next) { + function (req) { req.locals = { test: true }; - - next(); - }, - function (req, res, next) { - if (req.query.middleware_response) { - return res.status(200).json(req.locals); - } - - next(); } ] } })); test.after(globalAfterAll()); -test('Test middleware for all api', async (t) => { - const {data} = await api.get('/users?middleware_response=true'); - t.true(data.test); -}); - test('Test middleware for a controller', async (t) => { const getProducts = await api.get('/products'); const getProduct = await api.get('/products/123123'); diff --git a/tests/fixtures/test_controller.js b/tests/fixtures/test_controller.js index 7595885..1f87a0f 100755 --- a/tests/fixtures/test_controller.js +++ b/tests/fixtures/test_controller.js @@ -93,17 +93,15 @@ TestController.ROUTES = { }; TestController.MIDDLEWARE = [ - function (req, res, next) { + function (req) { if (!req.locals) req.locals = {}; req.locals.controller_middleware = true; - next(); }, - ['allAction', function (req, res, next) { + ['allAction', function (req) { if (!req.locals) req.locals = {}; req.locals.action_middleware = true; - next(); }] ];