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
      }
    });
This commit is contained in:
Miroslav Bajtoš 2014-11-11 17:51:50 +01:00
parent 6aef836372
commit 5578d59631
2 changed files with 112 additions and 13 deletions

View File

@ -1,3 +1,4 @@
var assert = require('assert');
var express = require('express'); var express = require('express');
var merge = require('util')._extend; var merge = require('util')._extend;
var PhaseList = require('loopback-phase').PhaseList; var PhaseList = require('loopback-phase').PhaseList;
@ -12,6 +13,47 @@ module.exports = function loopbackExpress() {
return app; 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. * Register a middleware handler to be executed in a given phase.
* @param {string} name The phase name, e.g. "init" or "routes". * @param {string} name The phase name, e.g. "init" or "routes".
@ -23,6 +65,9 @@ module.exports = function loopbackExpress() {
proto.middleware = function(name, handler) { proto.middleware = function(name, handler) {
this.lazyrouter(); 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 fullName = name;
var handlerName = handler.name || '(anonymous)'; var handlerName = handler.name || '(anonymous)';

View File

@ -1,4 +1,5 @@
var path = require('path'); var path = require('path');
var http = require('http'); var http = require('http');
var loopback = require('../'); var loopback = require('../');
var PersistedModel = loopback.PersistedModel; var PersistedModel = loopback.PersistedModel;
@ -27,7 +28,7 @@ describe('app', function() {
}); });
app.use(namedHandler('main')); app.use(namedHandler('main'));
executeHandlers(function(err) { executeMiddlewareHandlers(app, function(err) {
if (err) return done(err); if (err) return done(err);
expect(steps).to.eql([ expect(steps).to.eql([
'initial', 'session', 'auth', 'parse', 'initial', 'session', 'auth', 'parse',
@ -42,7 +43,7 @@ describe('app', function() {
app.middleware('routes:after', namedHandler('routes:after')); app.middleware('routes:after', namedHandler('routes:after'));
app.use(namedHandler('main')); app.use(namedHandler('main'));
executeHandlers(function(err) { executeMiddlewareHandlers(app, function(err) {
if (err) return done(err); if (err) return done(err);
expect(steps).to.eql(['routes:before', 'main', 'routes:after']); expect(steps).to.eql(['routes:before', 'main', 'routes:after']);
done(); done();
@ -64,7 +65,7 @@ describe('app', function() {
next(); next();
}); });
executeHandlers(function(err) { executeMiddlewareHandlers(app, function(err) {
if (err) return done(err); if (err) return done(err);
expect(steps).to.eql(['initial', 'error']); expect(steps).to.eql(['initial', 'error']);
done(); done();
@ -78,7 +79,7 @@ describe('app', function() {
next(expectedError); next(expectedError);
}); });
executeHandlers(function(err) { executeMiddlewareHandlers(app, function(err) {
expect(err).to.equal(expectedError); expect(err).to.equal(expectedError);
done(); done();
}); });
@ -90,18 +91,59 @@ describe('app', function() {
next(); next();
}; };
} }
});
function executeHandlers(callback) { describe.onServer('.middlewareFromConfig', function() {
var server = http.createServer(function(req, res) { it('provides API for loading middleware from JSON config', function(done) {
app.handle(req, res, callback); 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) // Config as a value (single arg)
.get('/test/url') app.middlewareFromConfig(handlerFactory, {
.end(function(err) { enabled: true,
if (err) return callback(err); 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() { 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);
});
}