From 5578d596319a5df01ccf2c7275bbc4060722d859 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20Bajto=C5=A1?= Date: Tue, 11 Nov 2014 17:51:50 +0100 Subject: [PATCH] Implement app.middlewareFromConfig Implement a function registering a middleware using a factory function and a JSON config. Example: app.middlewareFromConfig(compression, { enabled: true, phase: 'initial', config: { threshold: 128 } }); --- lib/server-app.js | 45 ++++++++++++++++++++++++++ test/app.test.js | 80 +++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 112 insertions(+), 13 deletions(-) diff --git a/lib/server-app.js b/lib/server-app.js index 07097eb9..7f7cda16 100644 --- a/lib/server-app.js +++ b/lib/server-app.js @@ -1,3 +1,4 @@ +var assert = require('assert'); var express = require('express'); var merge = require('util')._extend; var PhaseList = require('loopback-phase').PhaseList; @@ -12,6 +13,47 @@ module.exports = function loopbackExpress() { return app; }; +/** + * Register a middleware using a factory function and a JSON config. + * + * **Example** + * + * ```js + * app.middlewareFromConfig(compression, { + * enabled: true, + * phase: 'initial', + * config: { + * threshold: 128 + * } + * }); + * ``` + * + * @param {function} factory The factory function creating a middleware handler. + * Typically a result of `require()` call, e.g. `require('compression')`. + * @param {Array|*} config The configuration. Either an array of arguments + * to pass to the factory function, or the value of the first argument + * when the factory expects a single argument only. + */ +proto.middlewareFromConfig = function(factory, config) { + assert(typeof factory === 'function', '"factory" must be a function'); + assert(typeof config === 'object', '"config" must be an object'); + assert(typeof config.phase === 'string' && config.phase, + '"config.phase" must be a non-empty string'); + + if (config.enabled === false) + return; + + var args = config.config; + if (args === undefined) { + args = []; + } else if (!Array.isArray(args)) { + args = [args]; + } + + var handler = factory.apply(null, args); + this.middleware(config.phase, handler); +}; + /** * Register a middleware handler to be executed in a given phase. * @param {string} name The phase name, e.g. "init" or "routes". @@ -23,6 +65,9 @@ module.exports = function loopbackExpress() { proto.middleware = function(name, handler) { this.lazyrouter(); + assert(typeof name === 'string' && name, '"name" must be a non-empty string'); + assert(typeof handler === 'function', '"handler" must be a function'); + var fullName = name; var handlerName = handler.name || '(anonymous)'; diff --git a/test/app.test.js b/test/app.test.js index 9323ea56..9c29e706 100644 --- a/test/app.test.js +++ b/test/app.test.js @@ -1,4 +1,5 @@ var path = require('path'); + var http = require('http'); var loopback = require('../'); var PersistedModel = loopback.PersistedModel; @@ -27,7 +28,7 @@ describe('app', function() { }); app.use(namedHandler('main')); - executeHandlers(function(err) { + executeMiddlewareHandlers(app, function(err) { if (err) return done(err); expect(steps).to.eql([ 'initial', 'session', 'auth', 'parse', @@ -42,7 +43,7 @@ describe('app', function() { app.middleware('routes:after', namedHandler('routes:after')); app.use(namedHandler('main')); - executeHandlers(function(err) { + executeMiddlewareHandlers(app, function(err) { if (err) return done(err); expect(steps).to.eql(['routes:before', 'main', 'routes:after']); done(); @@ -64,7 +65,7 @@ describe('app', function() { next(); }); - executeHandlers(function(err) { + executeMiddlewareHandlers(app, function(err) { if (err) return done(err); expect(steps).to.eql(['initial', 'error']); done(); @@ -78,7 +79,7 @@ describe('app', function() { next(expectedError); }); - executeHandlers(function(err) { + executeMiddlewareHandlers(app, function(err) { expect(err).to.equal(expectedError); done(); }); @@ -90,18 +91,59 @@ describe('app', function() { next(); }; } + }); - function executeHandlers(callback) { - var server = http.createServer(function(req, res) { - app.handle(req, res, callback); + describe.onServer('.middlewareFromConfig', function() { + it('provides API for loading middleware from JSON config', function(done) { + var steps = []; + var expectedConfig = { key: 'value' }; + + var handlerFactory = function() { + var args = Array.prototype.slice.apply(arguments); + return function(req, res, next) { + steps.push(args); + next(); + }; + }; + + // Config as an object (single arg) + app.middlewareFromConfig(handlerFactory, { + enabled: true, + phase: 'session', + config: expectedConfig }); - request(server) - .get('/test/url') - .end(function(err) { - if (err) return callback(err); - }); - } + // Config as a value (single arg) + app.middlewareFromConfig(handlerFactory, { + enabled: true, + phase: 'session:before', + config: 'before' + }); + + // Config as a list of args + app.middlewareFromConfig(handlerFactory, { + enabled: true, + phase: 'session:after', + config: ['after', 2] + }); + + // Disabled by configuration + app.middlewareFromConfig(handlerFactory, { + enabled: false, + phase: 'initial', + config: null + }); + + executeMiddlewareHandlers(app, function(err) { + if (err) return done(err); + expect(steps).to.eql([ + ['before'], + [expectedConfig], + ['after', 2] + ]); + done(); + }); + }); }); describe('app.model(Model)', function() { @@ -470,3 +512,15 @@ describe('app', function() { }); }); }); + +function executeMiddlewareHandlers(app, callback) { + var server = http.createServer(function(req, res) { + app.handle(req, res, callback); + }); + + request(server) + .get('/test/url') + .end(function(err) { + if (err) return callback(err); + }); +}