Skip to content

Commit

Permalink
feat(middleware): change middleware flow to make it more enrich/error…
Browse files Browse the repository at this point in the history
… based
  • Loading branch information
ezypeeze committed Jul 16, 2018
1 parent 63408fb commit db639dd
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 36 deletions.
32 changes: 20 additions & 12 deletions lib/server_middleware/api.js
Original file line number Diff line number Diff line change
@@ -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();
Expand All @@ -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.
);
}
});
Expand Down Expand Up @@ -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
Expand Down
39 changes: 34 additions & 5 deletions lib/utility/controllers.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand All @@ -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;
Expand All @@ -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);
})
Expand All @@ -153,6 +181,7 @@ function controllerMappingServerSide(directory, req, options) {
module.exports = {
getControllerFiles,
getControllerMiddleware,
middlewareHandler,
controllerMappingClientSide,
controllerMappingServerSide
};
7 changes: 7 additions & 0 deletions lib/utility/http_errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
};
16 changes: 1 addition & 15 deletions tests/api.middleware.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down
6 changes: 2 additions & 4 deletions tests/fixtures/test_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}]
];

Expand Down

0 comments on commit db639dd

Please sign in to comment.