Skip to content

Commit

Permalink
Implement "bindAction" in router, and registerActionMiddleware
Browse files Browse the repository at this point in the history
  • Loading branch information
sgress454 committed Sep 23, 2016
1 parent b286ad8 commit 2c281d5
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 30 deletions.
5 changes: 5 additions & 0 deletions lib/app/Sails.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ function Sails() {
// Keep a hash of loaded actions
this._actions = {};

// Keep a hash of loaded action middleware
this._actionMiddleware = {};

// Build a Router instance (which will attach itself to the sails object)
__Router(this);

Expand Down Expand Up @@ -68,6 +71,7 @@ function Sails() {
this['delete'] = _.bind(this['delete'], this);
this.getActions = _.bind(this.getActions, this);
this.registerAction = _.bind(this.registerAction, this);
this.registerActionMiddleware = _.bind(this.registerActionMiddleware, this);
this.reloadModules = _.bind(this.reloadModules, this);
this.controller = this.initializeController(this);
}
Expand Down Expand Up @@ -100,6 +104,7 @@ Sails.prototype.reloadModules = require('./reload-modules');

Sails.prototype.getActions = require('./get-actions');
Sails.prototype.registerAction = require('./register-action');
Sails.prototype.registerActionMiddleware = require('./register-action-middleware');



Expand Down
17 changes: 1 addition & 16 deletions lib/app/private/controller/bind-route.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,22 +51,7 @@ module.exports = function interpretRouteSyntax (route) {
options = _.extend({}, options, _.omit(target, 'action'));
}

// Attempt to find an action with that identity.
var action = sails._actions[actionIdentity];
// If there is one, bind the given route address to the action.
if (action) {
// Make sure req.options.action is set to the identity of the action we're binding,
// for cases where the { action: 'foo.bar' } syntax wasn't used.
options.action = actionIdentity;
sails.router.bind(path, action, verb, options);
}
// Otherwise, log a message about the unknown action.
else {
sails.log.warn(
'Ignored attempt to bind route (' + path + ') to unknown target ::',
target
);
}
sails.router.bind(path, actionIdentity, verb, options);

};

34 changes: 34 additions & 0 deletions lib/app/register-action-middleware.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/**
* Sails.prototype.registerActionMiddleware()
*
* Register an action middleware with Sails.
*
* Action middleware runs before the action or actions
* specified by the middleware key.
*
* @param {fn} middleware The function to register
* @param {string} actions The identity of the action or actions that this middleware should apply to.
* Use * at the end for a wildcard; e.g. `user.*` will apply to any actions
* whose identity begins with `user.`.
*
*
* @api public
*/
module.exports = function registerAction(middleware, actions) {

// TODO -- update machine-as-action with a response type that calls `next`,
// so machine defs can be registered as middleware?
if (_.isFunction(middleware)) {
throw new Error('Attempted to register middleware for `' + actions + '` but the provided middleware was not a function (it was a ' + typeof(middleware) + ').');
}

// Get or create the array for this middleware key.
var middlewareForKey = this._actionMiddleware[actions] || [];

// Add this middleware to the array.
middlewareForKey.push(middleware);

// Assign the array back to the dictionary.
this._actionMiddleware[actions] = middlewareForKey;

};
86 changes: 72 additions & 14 deletions lib/router/bind.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

var _ = require('lodash');
var sailsUtil = require('sails-util');

var async = require('async');


/**
Expand Down Expand Up @@ -52,21 +52,12 @@ function bind( /* path, target, verb, options */ ) {
else if (_.isString(target) && target.match(/^(https?:|\/)/)) {
bindRedirect.apply(this, [path, target, verb, options]);
}
// Otherwise if the target is a string, it must be an action.
else if (_.isString(target)) {
// Get the action identity by replacing slashes with dots, removing `Controller` and lower-casing.
var actionIdentity = target.replace(/\//g, '.').replace(/Controller\./,'.').toLowerCase();
// If there's no loaded action with that identity, log a warning and continue.
if (!sails._actions[actionIdentity]) {
sails.log.warn('Ignored attempt to bind route (' + path + ') to unknown target ::', target);
return;
}
// If the string has slashes in it, log a warning.
if (target.indexOf('/') > -1) {
sails.log.warn('Using string route target syntax with nested legacy controllers is deprecated. '+
'Binding `' + target + '` to `' + path + '` for now, but you should really change this to { action: \'' + actionIdentity + '\' }.');
}
sails.controller.bindRoute({target: {action: actionIdentity}, path: path, verb: verb, options: options});
bindAction.apply(this, [path, target, verb, options]);
}
// If the target is a dictionary with `controller` or `action` properties,
// let the sails controller handle it.
else if (_.isPlainObject(target) && (target.controller || target.action)) {
sails.controller.bindRoute({path: path, target: target, verb: verb, options: options});
}
Expand Down Expand Up @@ -125,6 +116,73 @@ function bindRedirect(path, redirectTo, verb, options) {
}, verb, options]);
}

/**
* Bind a previously-loaded action to a URL.
* (which should be a URL or redirectable path.)
*
* @api private
*/
function bindAction(path, target, verb, options) {

var sails = this.sails;

// Normalize the action identity by replacing slashes with dots, removing `Controller` and lower-casing.
var actionIdentity = target.replace(/\//g, '.').replace(/Controller\./,'.').toLowerCase();

// If there's no loaded action with that identity, log a warning and continue.
if (!sails._actions[actionIdentity]) {
sails.log.warn('Ignored attempt to bind route (' + path + ') to unknown target ::', target);
return;
}

// If the string has slashes in it, log a warning.
if (target.indexOf('/') > -1) {
sails.log.warn('Using string route target syntax with nested legacy controllers is deprecated. '+
'Binding `' + target + '` to `' + path + '` for now, but you should really change this to { action: \'' + actionIdentity + '\' }.');
}

// Add "action" property to the route options.
_.extend(options || {}, {action: actionIdentity});

// Bind a function to the route which calls the specified action.
bind.apply(this,[path, function(req, res, next) {
// Loop through all of the registered action middleware, and find
// any that should apply to the action with the given identity.
var middlewareToRun = _.reduce(sails._actionMiddleware, function(memo, middlewareList, key) {
if (
// If the registered action middleware key is '*'...
key === '*' ||
// Or ends in '.*' so that the current action identity matches the wildcard...
(_.last(key) === '*' && (actionIdentity.indexOf(key.slice(0,-1)) === 0)) ||
// Or matches the current action identity exactly...
(actionIdentity === key)
) {
// Then add the action middleware from this key to the list of middleware
// to run before the action.
memo = memo.concat(middlewareList);
}
return memo;
}, []);

// Run any action middleware we found first.
async.eachSeries(middlewareToRun, function(middleware, next) {
// The `next` argument will just run the next middleware
// when called.
// TODO -- support sending 'route' or errors through `next`?
middleware(req, res, function() {
return next();
});
},
// Finally, after any action middleware has run,
// run the action itself.
function afterRunningActionMiddleware() {
return sails._actions[actionIdentity](req, res, function() {
throw new Error('`next` called in action function!');
});
});
}, verb, options]);
}


/**
* Recursively bind an array of targets in order
Expand Down

0 comments on commit 2c281d5

Please sign in to comment.