Merge pull request #786 from strongloop/feature/define-middleware-phases
Implement `app.defineMiddlewarePhases`
This commit is contained in:
commit
4474f8b029
|
@ -54,6 +54,77 @@ proto.middlewareFromConfig = function(factory, config) {
|
|||
this.middleware(config.phase, handler);
|
||||
};
|
||||
|
||||
/**
|
||||
* Register (new) middleware phases.
|
||||
*
|
||||
* If all names are new, then the phases are added just before "routes" phase.
|
||||
* Otherwise the provided list of names is merged with the existing phases
|
||||
* in such way that the order of phases is preserved.
|
||||
*
|
||||
* **Examples**
|
||||
*
|
||||
* ```js
|
||||
* // built-in phases:
|
||||
* // initial, session, auth, parse, routes, files, final
|
||||
*
|
||||
* app.defineMiddlewarePhases('custom');
|
||||
* // new list of phases
|
||||
* // initial, session, auth, parse, custom, routes, files, final
|
||||
*
|
||||
* app.defineMiddlewarePhases([
|
||||
* 'initial', 'postinit', 'preauth', 'routes', 'subapps'
|
||||
* ]);
|
||||
* // new list of phases
|
||||
* // initial, postinit, preauth, session, auth, parse, custom,
|
||||
* // routes, subapps, files, final
|
||||
* ```
|
||||
*
|
||||
* @param {string|Array.<string>} nameOrArray A phase name or a list of phase
|
||||
* names to add.
|
||||
*/
|
||||
proto.defineMiddlewarePhases = function(nameOrArray) {
|
||||
this.lazyrouter();
|
||||
if (!Array.isArray(nameOrArray)) {
|
||||
this._requestHandlingPhases.addBefore('routes', nameOrArray);
|
||||
return;
|
||||
}
|
||||
|
||||
var sourcePhases = nameOrArray;
|
||||
if (!sourcePhases.length) return;
|
||||
|
||||
var targetPhases = this._requestHandlingPhases.getPhaseNames();
|
||||
var targetIx = targetPhases.indexOf(nameOrArray[0]);
|
||||
|
||||
if (targetIx === -1) {
|
||||
// the first new phase does not match any existing one
|
||||
// add all phases before "routes"
|
||||
sourcePhases.forEach(function(it) {
|
||||
this._requestHandlingPhases.addBefore('routes', it);
|
||||
}, this);
|
||||
return;
|
||||
}
|
||||
|
||||
// merge (zip) two arrays of phases
|
||||
for (var sourceIx = 1; sourceIx < sourcePhases.length; sourceIx++) {
|
||||
var nameToAdd = sourcePhases[sourceIx];
|
||||
var previousPhase = sourcePhases[sourceIx - 1];
|
||||
var existingIx = targetPhases.indexOf(nameToAdd, targetIx);
|
||||
if (existingIx === -1) {
|
||||
// A new phase - try to add it after the last one,
|
||||
// unless it was already registered
|
||||
if (targetPhases.indexOf(nameToAdd) !== -1) {
|
||||
throw new Error('Phase ordering conflict: cannot add "' + nameToAdd +
|
||||
'" after "' + previousPhase + '", because the opposite order was ' +
|
||||
' already specified');
|
||||
}
|
||||
this._requestHandlingPhases.addAfter(previousPhase, nameToAdd);
|
||||
} else {
|
||||
// An existing phase - move the pointer
|
||||
targetIx = existingIx;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Register a middleware handler to be executed in a given phase.
|
||||
* @param {string} name The phase name, e.g. "init" or "routes".
|
||||
|
|
|
@ -146,6 +146,64 @@ describe('app', function() {
|
|||
});
|
||||
});
|
||||
|
||||
describe.onServer('.defineMiddlewarePhases(nameOrArray)', function() {
|
||||
var app;
|
||||
beforeEach(function() {
|
||||
app = loopback();
|
||||
});
|
||||
|
||||
it('adds the phase just before "routes" by default', function(done) {
|
||||
app.defineMiddlewarePhases('custom');
|
||||
verifyMiddlewarePhases(['custom', 'routes'], done);
|
||||
});
|
||||
|
||||
it('adds an array of phases just before "routes"', function(done) {
|
||||
app.defineMiddlewarePhases(['custom1', 'custom2']);
|
||||
verifyMiddlewarePhases(['custom1', 'custom2', 'routes'], done);
|
||||
});
|
||||
|
||||
it('merges phases preserving the order', function(done) {
|
||||
app.defineMiddlewarePhases([
|
||||
'initial',
|
||||
'postinit', 'preauth', // add
|
||||
'auth', 'routes',
|
||||
'subapps', // add
|
||||
'final',
|
||||
'last' // add
|
||||
]);
|
||||
verifyMiddlewarePhases([
|
||||
'initial',
|
||||
'postinit', 'preauth', // new
|
||||
'auth', 'routes',
|
||||
'subapps', // new
|
||||
'files', 'final',
|
||||
'last' // new
|
||||
], done);
|
||||
});
|
||||
|
||||
it('throws helpful error on ordering conflict', function() {
|
||||
app.defineMiddlewarePhases(['first', 'second']);
|
||||
expect(function() { app.defineMiddlewarePhases(['second', 'first']); })
|
||||
.to.throw(/ordering conflict.*first.*second/);
|
||||
});
|
||||
|
||||
function verifyMiddlewarePhases(names, done) {
|
||||
var steps = [];
|
||||
names.forEach(function(it) {
|
||||
app.middleware(it, function(req, res, next) {
|
||||
steps.push(it);
|
||||
next();
|
||||
});
|
||||
});
|
||||
|
||||
executeMiddlewareHandlers(app, function(err) {
|
||||
if (err) return done(err);
|
||||
expect(steps).to.eql(names);
|
||||
done();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
describe('app.model(Model)', function() {
|
||||
var app, db;
|
||||
beforeEach(function() {
|
||||
|
|
Loading…
Reference in New Issue