Skip to content

Commit

Permalink
Merge 18a843b into 6107af7
Browse files Browse the repository at this point in the history
  • Loading branch information
DonutEspresso committed May 22, 2018
2 parents 6107af7 + 18a843b commit eb73a0d
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 3 deletions.
115 changes: 113 additions & 2 deletions lib/install.js
Expand Up @@ -6,23 +6,43 @@ var _ = require('lodash');
var assert = require('assert-plus');
var vasync = require('vasync');
var verror = require('verror');
var compose = require('restify').helpers.compose;
var bunyan = require('bunyan');

// local globals
var LOG;


/**
* Install the enroute routes into the restify server.
*
* @param {object} opts Options object.
* @param {object} opts.enroute The parsed enroute configuration.
* @param {object} opts.log an optional logger
* @param {object} opts.server The restify server.
* @param {string} opts.basePath The basepath to resolve source files.
* @param {function} cb The callback.
* @returns {undefined}
*/
function install(opts, cb) {
assert.object(opts, 'opts');
assert.optionalObject(opts.log, 'opts.log');
assert.object(opts.enroute, 'opts.enroute');
assert.object(opts.server, 'opts.server');
assert.string(opts.basePath, 'opts.basePath');

var log;

if (opts.log) {
log = opts.log.child({ component : 'enroute' });
} else {
// only create default logger if one wasn't passed in.
if (!LOG) {
LOG = bunyan.createLogger({ name: 'enroute' });
}
log = LOG;
}

vasync.pipeline({arg: {}, funcs: [
// Read the routes from disk and parse them as functions
function getRoutes(ctx, cb1) {
Expand All @@ -32,6 +52,15 @@ function install(opts, cb) {
return cb1();
});

if (opts.enroute.hotReload) {
if (typeof opts.basePath !== 'undefined') {
log.info({ basedir: opts.basePath },
'hot reloading of routes is enabled for base dir');
} else {
log.info('hot reloading of routes is enabled.');
}
}

// go through each of the route names
_.forEach(opts.enroute.routes, function (methods, routeName) {
// go through each of the HTTP methods
Expand All @@ -42,20 +71,34 @@ function install(opts, cb) {
var route;

try {
var func = (opts.enroute.hotReload) ?
// restify middleware wrapper for hot reload
function enrouteHotReloadProxy(req, res, next) {
return reloadProxy({
basePath: opts.basePath,
method: method,
req: req,
res: res,
routeName: routeName,
sourceFile: sourceFile
}, next);
} : require(sourceFile);

route = {
name: routeName,
method: method,
func: require(sourceFile)
func: func
};
} catch (e) {
return cb1(new verror.VError({
name: 'EnrouteRequireError',
cause: e,
info: {
file: sourceFile,
route: routeName,
method: method
}
}, 'route function is invalid'));
}, 'failed to require file, possible syntax error'));
}
// if HTTP method is 'delete', since restify uses 'del'
// instead of 'delete', change it to 'del'
Expand Down Expand Up @@ -89,4 +132,72 @@ function install(opts, cb) {
});
}


/**
* using the restify handler composer, create a middleware on the fly that
* re-requires the file on every load executes it.
* @private
* @function reloadProxy
* @param {Object} opts an options object
* @param {String} opts.basePath The basepath to resolve source files.
* @param {String} opts.method http verb
* @param {Object} opts.log the enroute logger
* @param {Object} opts.req the request object
* @param {Object} opts.res the response object
* @param {String} opts.routeName the name of the restify route
* @param {String} opts.sourceFile the response object
* @param {Function} cb callback fn
* @returns {undefined}
*/
function reloadProxy(opts, cb) {
assert.object(opts, 'opts');
assert.object(opts.basePath, 'opts.basePath');
assert.string(opts.method, 'opts.method');
assert.object(opts.log, 'opts.log');
assert.object(opts.req, 'opts.req');
assert.object(opts.res, 'opts.res');
assert.string(opts.routeName, 'opts.routeName');
assert.string(opts.sourceFile, 'opts.sourceFile');
assert.func(cb, 'cb');

if (typeof opts.basePath !== 'undefined') {
//Delete code loaded from a specific base dir
Object.keys(require.cache).forEach(function (cacheKey) {
if (cacheKey.indexOf(opts.basePath) !== -1) {
delete require.cache[cacheKey];
}
});
} else {
//Delete all cached entries
Object.keys(require.cache).forEach(function (cacheKey) {
delete require.cache[cacheKey];
});
}

var handlers;

try {
handlers = require(opts.sourceFile);
} catch (e) {
var err = new verror.VError({
name: 'EnrouteRequireError',
cause: e,
info: {
file: opts.sourceFile,
route: opts.routeName,
method: opts.method
}
}, 'failed to require file, possible syntax error');

// now that the chain has failed, send back the require error.
return cb(err);
}

// if no require error, execute the handler chain. any errors that occur at
// runtime should be a runtime exception.
var handlerChain = compose(handlers);
return handlerChain(opts.req, opts.res, cb);
}


module.exports = install;
3 changes: 3 additions & 0 deletions lib/schemas.js
Expand Up @@ -99,6 +99,9 @@ module.exports = {
},
schemaVersion: {
type: 'number'
},
hotReload: {
type: 'boolean'
}
},
additionalProperties: false,
Expand Down
3 changes: 2 additions & 1 deletion package.json
Expand Up @@ -37,14 +37,15 @@
"jscs": "^3.0.7",
"mocha": "^3.1.2",
"nsp": "^2.6.2",
"restify": "^7.0.0",
"restify-clients": "^1.4.0",
"uuid": "^2.0.3"
},
"dependencies": {
"ajv": "^4.8.0",
"assert-plus": "^1.0.0",
"bunyan": "^1.8.12",
"lodash": "^4.16.4",
"restify": "^7.2.0",
"vasync": "^1.6.4",
"verror": "^1.6.0"
}
Expand Down

0 comments on commit eb73a0d

Please sign in to comment.