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);
|
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.
|
* 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".
|
||||||
|
|
|
@ -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() {
|
describe('app.model(Model)', function() {
|
||||||
var app, db;
|
var app, db;
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
|
|
Loading…
Reference in New Issue