New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

restify-enroute

Package Overview
Dependencies
Maintainers
3
Versions
13
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

restify-enroute - npm Package Compare versions

Comparing version 2.0.1 to 3.0.1

lib/install.js

285

lib/index.js

@@ -1,249 +0,50 @@

/*
* restify-enroute
* dynamically inject routes into restify app from configuration file
*
* Copyright (c) 2015 Netflix, Inc.
* Licensed under the MIT license.
*/
'use strict';
var path = require('path');
var assert = require('assert-plus');
var Verror = require('verror');
var shell = require('shelljs');
var restify = require('restify');
var vasync = require('vasync');
/**
* checks if the path is device rooted, that is, if it has [drive letter]:\ at its beggining.
* @private
* @param {String} fpath
* The file path to check
* @returns {Boolean}
* Whether the path is rooted or not.
*/
function isWin32DeviceRoot(fpath) {
// Only for win32
if (process.platform !== 'win32') {
return false;
}
var install = require('./install');
var parser = require('./parser');
// Any drive letter followed by :\
var deviceRoot = /^[A-Za-z]:\\/;
return deviceRoot.test(fpath);
}
function requireHandlerFunc(filePath, log) {
var scriptPath = filePath;
var handlerFunc;
if (!scriptPath.endsWith('.js')) {
scriptPath += '.js';
}
try {
handlerFunc = require(scriptPath);
} catch (err) {
var errObj = new Verror(err, 'Fail to load %s',
scriptPath);
log.error(errObj);
throw errObj;
}
return handlerFunc;
}
/**
* insert restify expiry middleware after each
* handler function to prevent expired request
* to continue process
* @public
* @param {Array} middlewareArr middleware handler array
* @param {Object} expiry expiry plugin middleware config
* @returns {Array} middleware handler array that has expiry plugin
*/
function appendExpiryCheck(middlewareArr, expiry) {
var finalHdlList = [];
if (Array.isArray(middlewareArr)) {
middlewareArr.forEach(function (fn) {
finalHdlList.push(fn);
finalHdlList.push(restify.requestExpiry(expiry));
module.exports = {
/**
* Installs configuration driven routes onto a restify server. Note only
* one of opts.config or opts.configPath is needed.
*
* exports
*
* @param {object} opts Options object.
* @param {string} [opts.config] The POJO of the config you want to
* validate.
* @param {string} [opts.configPath] The path to the data on disk to
* validate.
* @param {string} opts.server The restify server to install the routes
* onto.
* @param {function} cb The callback f(err, result)
* @returns {undefined}
*/
install: function (opts, cb) {
assert.object(opts, 'opts');
assert.func(cb, 'cb');
// asserts of other inputs done by parse and installRoutes repectively
vasync.pipeline({arg: {}, funcs: [
function parse(ctx, _cb) {
parser.parse(opts, function (err, config) {
ctx.config = config;
return _cb(err);
});
},
function installRoutes(ctx, _cb) {
install({
enroute: ctx.config,
server: opts.server
}, function (err) {
return _cb(err);
});
}
]}, function (err, res) {
return cb(err);
});
} else {
finalHdlList = middlewareArr;
finalHdlList.push(restify.requestExpiry(expiry));
}
return finalHdlList;
}
/**
* getRouteConfigObject description]
* @param {Object} routeConf an array of route config file path routeConf.filePath or
* an array of route definition object routeConf.routeDefinitionObj
* @param {Object} log bunyan log object
* @returns {Object} route definition object
*/
function getRouteConfigObject(routeConf, log) {
assert.optionalString(routeConf.filePath, 'file path need to be a string');
assert.optionalObject(routeConf.routeDefinitionObj,
'route definition is an object');
assert.optionalString(routeConf.baseUrl, 'base url needs to be a string');
var configFilePath;
var baseurl = routeConf.baseUrl || '';
var routeConfObj = {baseUrl : baseurl};
var appRoot = shell.pwd();
if (routeConf.filePath) {
configFilePath =
path.normalize(path.join(appRoot, routeConf.filePath));
try {
//route injection is at server start up time, therefore it's
//needs to be synchronize load
/*eslint-disable global-require*/
routeConfObj.routeDefinitionObj = require(configFilePath);
/*eslint-enable */
} catch (err) {
var errObj = new Verror(err, 'problem load route configuration file at %s',
configFilePath);
log.error(errObj);
throw errObj;
}
} else {
//if user didn't provide route configuration file path
//here we should have a rout definition object
routeConfObj.routeDefinitionObj = routeConf.routeDefinitionObj;
}
return routeConfObj;
}
/**
* take the handlers from a route config file and setup routes into
* restify server
* @public
* @function createRoute
* @param {Object} options
* @param {Object} options.server required options restify server
* @param {Object} options.log required bunyan log object
* @param {Object} options.preMiddleware optional,
* options.preMiddleware
* a list of middleware called before route handler
* @param {Object} options.postMiddleware optional,
* a list of middleware called after route handler
* @param {Array of Object} options.routeConf required,
* array of object contains route configuration file relative path to app root
* and optional base url path
* for example, one set of route configuration
* [{filePath : ./etc/routes/route.json, baseUrl : 'v1/myaccount/'}
* or route definition object, for example
* [{routeDefinitionObj : routeObj, baseUrl : 'v1/myaccount/'}
* user should provide either configuration file path or an route definition path
* @param {Function} cb optional callback function
* @returns {undefined}
*/
function createRoute(options, cb) {
assert.object(options.server, 'options.server restify server');
assert.object(options.log, 'options.log bunyan log');
assert.arrayOfObject(options.routeConf,
'options.routeConf route conifguration files');
assert.optionalString(options.scriptPath,
'options.scriptPath, optionally for test');
assert.optionalObject(options.expiry,
'options.expiry, optionally check expiry after each middleware');
var server = options.server;
var log = options.log;
var scriptRoot = options.scriptPath;
var preMiddleware = options.preMiddleware;
var postMiddleware = options.postMiddleware;
var expiry = options.expiry;
options.routeConf.forEach(function (routeConf) {
var routeConfObj = getRouteConfigObject(routeConf, options.log);
assert.object(routeConfObj, 'Missing route config object');
assert.object(routeConfObj.routeDefinitionObj, 'Missing route definition object');
assert.optionalString(routeConfObj.baseUrl, 'base url needs to be a string');
var baseUrl = routeConfObj.baseUrl;
var routes;
var routelist = {};
routes = routeConfObj.routeDefinitionObj.routes;
Object.keys(routes).forEach(function (rpath) {
//deliminate route path and method with }
var routePath = baseUrl + '/' + rpath;
Object.keys(routes[rpath]).forEach(function (method) {
var actions = [];
//var routeKey;
var scriptFpath = routes[rpath][method].source;
var handlerFunc;
if (scriptRoot) {
scriptFpath = path.normalize(path.join(scriptRoot,
scriptFpath));
} else {
scriptFpath = path.resolve(scriptFpath);
// make sure the path casing obeys the casing of the parent
if (isWin32DeviceRoot(scriptFpath) &&
scriptFpath.substr(0, 2).toLowerCase() === module.filename.substr(0, 2).toLowerCase()) {
scriptFpath = module.filename.substr(0, 2) + scriptFpath.substr(2);
}
}
//routeKey = routePath + '}' + method;
if (typeof routelist[routePath] !== 'undefined') {
throw new Error('Route path exist' + rpath
+ ' method ' + method);
}
handlerFunc = requireHandlerFunc(scriptFpath, log);
//routelist[routeKey] = handlerFunc;
if (preMiddleware) {
if (Array.isArray(preMiddleware)) {
actions = actions.concat(preMiddleware);
} else {
actions.push(preMiddleware);
}
}
if (handlerFunc) {
if (Array.isArray(handlerFunc)) {
actions = actions.concat(handlerFunc);
} else {
actions.push(handlerFunc);
}
}
if (postMiddleware) {
if (Array.isArray(postMiddleware)) {
actions = actions.concat(postMiddleware);
} else {
actions.push(postMiddleware);
}
}
if (expiry) {
actions = appendExpiryCheck(actions, expiry);
}
server[method.toLowerCase()](routePath, actions);
});
});
});
if (cb && typeof cb === 'function') {
cb();
}
}
module.exports = {
createRoute : createRoute
},
validate: parser.parse
};
{
"name": "restify-enroute",
"version": "2.0.1",
"version": "3.0.1",
"main": "./lib/index.js",
"description": "dynamically inject routes into restify app from configuration file",
"description": "Config driven restify route creation",
"homepage": "http://www.restify.com",
"author": {
"name": "Tsubomi Imamura",
"email": "tsubomi.imamura@gmail.com"
"name": "Yunong Xiao"
},

@@ -34,19 +33,20 @@ "repository": {

"devDependencies": {
"chai": "^3.2.0",
"eslint": "^0.24.1",
"jscs": "^1.13.1",
"mocha": "^2.2.5",
"nsp": "^2.1.0"
"chai": "^3.5.0",
"coveralls": "^2.11.14",
"eslint": "^3.8.1",
"istanbul": "^0.4.5",
"jscs": "^3.0.7",
"mocha": "^3.1.2",
"nsp": "^2.6.2",
"restify": "^4.0.0",
"restify-clients": "^1.4.0",
"uuid": "^2.0.3"
},
"dependencies": {
"app-root-path": "^1.0.0",
"assert-plus": "^0.1.5",
"body-parser": "^1.14.1",
"bunyan": "^1.5.1",
"dtrace-provider": "^0.6.0",
"restify": "^4.1.1",
"restify-clients": "^1.1.1",
"shelljs": "^0.5.3",
"ajv": "^4.8.0",
"assert-plus": "^1.0.0",
"lodash": "^4.16.4",
"vasync": "^1.6.4",
"verror": "^1.6.0"
}
}

@@ -1,29 +0,48 @@

# enroute
This module provides convenience way to specify restify route in a declarative JSON format.
## Route Configuration file Example
# restify-enroute
[![NPM Version](https://img.shields.io/npm/v/restify-enroute.svg)](https://npmjs.org/package/restify-enroute)
[![Build Status](https://travis-ci.org/restify/enroute.svg?branch=master)](https://travis-ci.org/restify/enroute)
[![Coverage Status](https://coveralls.io/repos/restify/enroute/badge.svg?branch=master)](https://coveralls.io/r/restify/enroute?branch=master)
[![Dependency Status](https://david-dm.org/restify/enroute.svg)](https://david-dm.org/restify/enroute)
[![devDependency Status](https://david-dm.org/restify/enroute/dev-status.svg)](https://david-dm.org/restify/enroute#info=devDependencies)
[![bitHound Score](https://www.bithound.io/github/restify/enroute/badges/score.svg)](https://www.bithound.io/github/restify/enroute/master)
[![NSP Status](https://img.shields.io/badge/NSP%20status-no%20vulnerabilities-green.svg)](https://travis-ci.org/restify/enroute)
This module provides configuration driven route installation for restify.
Instead of having to declare routes in code, you can create a confiuration file
like this:
```json
{
"schemaVersion": 1,
"routes": {
"acceptcookies": {
"foo": {
"get": {
"source": "./test/etc/fooGet.js"
},
"post": {
"source": "./test/etc/fooPost.js"
},
"put": {
"source": "./routes/putAcceptCookies.js"
"source": "./test/etc/fooPut.js"
},
"get": {
"source": "./routes/getAcceptCookies.js"
"delete": {
"source": "./test/etc/fooDelete.js"
},
"head": {
"source": "./test/etc/fooHead.js"
},
"patch": {
"source": "./test/etc/fooPatch.js"
},
"options": {
"source": "./test/etc/fooOptions.js"
}
},
"accountInfo": {
"bar": {
"get": {
"source": "./routes/getAccountInfo.js"
}
},
"cancelPlan": {
"source": "./test/etc/barGet.js"
},
"post": {
"source": "./routes/postCancelPlan.js"
"source": "./test/etc/barPost.js"
}
},
"accountInfo/old": {
"get": {
"source": "./routes/getAccountInfoOld.js"
}
}

@@ -33,117 +52,114 @@ }

```
The second level keys "acceptcookies", "accountInfo", "cancelPlan", "accountInfo/old" is url path.
The third level keys are HTTP method names.
The value of "source" is the route handler script's relative path from app root.
This declares the route name, http method, and handler file on disk. this
module will install these routes onto a restify server for you. The
corresponding handler file would look like:
## Usage Example
```javascript
enroute.createRoute({
//restify server object
server: server,
//bunyan log object
log: log,
//array of route config object, the object includes route configuration file path
//and an optional baseUrl String, it will be prepend
//for every route path specified in route configuration file. user can also provide
//route definition object directly by doing
//routeConf: [{routeDefinitionObj : routeObj}],
routeConf: [{filePath : routeScriptRootPath + '/route.json'}],
//user can specify an array of universal middleware function before all routes
preMiddleware : preHandlerArr,
//user can specify an array of universal middleware function after all routes
postMiddleware : postMiddleware,
//the root path of all route scripts, it can be different from route.json path
scriptPath : routeScriptRootPath
}, function() {
//user can specify optional callback function
client.get('/hello',
function(err, req, res, obj) {
if (err) {
done(err);
}
assert.equal(obj.data, 'Hello world!',
'Hello World endpoint response.');
done();
});
});
});
module.exports = function handler(req, res, next) {
res.send(200, 'Hello World');
next()
};
```
Routes can be injected into Restify server either before or after server start.
## API
Synopsis: `install(opts, cb)`
## Route Handler Script Example
```javascript
'use strict';
// Node core modules
var url = require('url');
Installs routes as defined in opts into a restify server, invokes the callback
when done.
* `opts`: The options object containing
* `opts.server` The restify server to install the routes on to.
* `[opts.config]` The POJO of the enroute config.
* `[opts.configPath]` The path to the enroute config on disk.
* `cb` The callback. Returns `Error` if there's an error installing the routes.
Note only one of `opts.config` or `opts.configPath` is needed. The module will
either read in the file from disk, or use a pre-populated POJO.
// Third party modules specified in package.json
var _ = require('lodash');
var falcor = require('falcor');
var verror = require('verror');
### Example
```javascript
const enroute = require('restify-enroute');
const restify = require('restify');
function accountInfo(req, res, next) {
var queryparams = req.queryParams;
// bunyan logger
var log = req.log;
// metrics client;
var metrics = req.metrics;
// api client
var apiClient = req.apiClient;
const CONFIG = {
schemaVersion: 1,
routes: {
foo: {
get: {
source: './test/etc/fooGet.js'
},
post: {
source: './test/etc/fooPost.js'
},
delete: {
source: './test/etc/fooDelete.js'
},
head: {
source: './test/etc/fooHead.js'
},
}
}
};
// Make some api requests
const server = restify.createServer();
// install routes with enroute
enroute.install({
config: CONFIG,
server: server
}, function (err) {
if (err) {
console.error('unable to install routes');
} else {
console.log('routes installed');
SERVER.listen(1337);
}
});
```
// send back response
res.send(200);
Synopsis: `validate(opts, cb)`
return next();
}
Parse and validate a enroute config. This will verify that the config
is valid and return a POJO with the properties. Note only one of opts.config
or opts.configPath is needed.
module.exports = accountInfo;
```
Route definitions must export as single function or an array of functions where the signature conforms to f(req, res, next). This is identical to writing a restify handler function. The req object contains request state, the api client, logging and metric clients that can be leveraged by the user land code in the endpoint script. The res object is used to send back the response to the client, and next() must be invoked when the script has completed.
* `opts` The options object containing
* `[opts.config]` The POJO of the config you want to validate.
* `[opts.configPath]` The path to the config on disk to validate.
* `cb` The callback f(err, validatedConfig). Returns `Error` if there's an
* error parsing or validating the config
Instead of a single function, route definitions can export an array of chained function handlers; increasing the modularity of their endpoint scripts.
### Example
```javascript
'use strict';
// Node core modules
var url = require('url');
const enroute = require('restify-enroute');
const CONFIG = {
schemaVersion: 1,
routes: {
foo: {
get: {
source: './test/etc/fooGet.js'
},
post: {
source: './test/etc/fooPost.js'
},
delete: {
source: './test/etc/fooDelete.js'
},
head: {
source: './test/etc/fooHead.js'
},
}
}
};
// Third party modules specified in metadata dependencies/floatDependencies fields
var _ = require('lodash');
var falcor = require('falcor');
var verror = require('verror');
var postHandler = require('somePostHandler');
function somePreHandler(req, res, next) {
return next();
}
function accountInfo(req, res, next) {
var queryparams = req.queryParams;
// bunyan logger
var log = req.log;
// metrics client;
var metrics = req.metrics;
// api client
var apiClient = req.apiClient;
// Make some api requests
// send back response
res.send(200);
return next();
}
module.exports = [somePreHandler, accountInfo, postHandler];
const server = restify.createServer();
// install routes with enroute
enroute.validate({
config: CONFIG
}, function (err) {
if (err) {
console.error('unable to install routes');
} else {
console.log('config successfully validated');
}
});
```
Notice that the functions can be both defined within the script itself, but can also be a module. Critically, every function in the chain must invoke ```next()```, otherwise the request will hang.
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc