Skip to content

Commit

Permalink
Refactor route handling into single middleware module.
Browse files Browse the repository at this point in the history
Tidy up some logging.
  • Loading branch information
Yakov Khalinsky committed Feb 13, 2015
1 parent 4f36f94 commit bbe1a8b
Show file tree
Hide file tree
Showing 7 changed files with 124 additions and 59 deletions.
6 changes: 5 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
var drakov = require('./lib/drakov');

exports.run = drakov.run;
module.exports = {
run: drakov.run,
stop: drakov.stop,
middleware: require('./lib/middleware')
};
4 changes: 0 additions & 4 deletions lib/content.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
var lodash = require('lodash');
var logger = require('./logger');

var mediaTypeRe = /^\s*([^;]+)/i;

Expand Down Expand Up @@ -66,19 +65,16 @@ exports.matches = function( httpReq, specReq ) {
var specMediaType = getMediaTypeFromSpecReq( specReq );
if ( httpMediaType === specMediaType ) {
if ( !hasHeaders( httpReq, specReq ) ){
logger.log('[WARN]'.red, 'Skip. Different headers');
return false;
}

if ( isBodyEqual( httpReq, specReq, httpMediaType ) ) {
return true;
} else {
logger.log('[WARN]'.red, 'Skip. Different body');
return false;
}

} else {
logger.log('[WARN]'.red, 'Skip. Different content-types ', httpMediaType, ' !== ', specMediaType );
return false;
}
};
39 changes: 5 additions & 34 deletions lib/drakov.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
var express = require('express');
var async = require('async');
var glob = require('glob');

require('colors');

var logger = require('./logger');
var requestUtils = require('./request');
var responseUtils = require('./response');
var file = require('./file');
var static = require('./static');
var setup = require('./setup');

Expand All @@ -17,7 +14,7 @@ exports.run = function(argv, cb) {

logger.setStealthMode(argv.stealthmode);

console.log('\n', ' Bootstrapping '.bold.inverse, '\n');
console.log(' DRAKOV STARTED '.green.bold.inverse);

var app = express();

Expand All @@ -31,44 +28,18 @@ exports.run = function(argv, cb) {
app.use(responseUtils.delayedResponse(argv.delay));
app.use(responseUtils.allowMethods(argv.method));

app.use(require('./middleware')(argv.sourceFiles));

if (argv.staticPaths) {
static.setupRoutes(app, argv.staticPaths, argv.pathDelimiter);
}

var startServer = function (cb){
server = setup.startServer(argv, app, cb);
};

glob(argv.sourceFiles, {} , function (err, files) {
if (err) {
console.error('Failed to parse contracts path.', err);
process.exit(1);
}

var seriesFunctions = files.map(function(filePath){
return file.readFile(app, filePath);
});

seriesFunctions.push(startServer);

if (cb) {
seriesFunctions.push(cb);
}

async.series(seriesFunctions, function(err) {
if (err) {
console.error('Drakov returned an error.', err);
process.exit(1);
}
});

});

server = setup.startServer(argv, app, cb);
};

exports.stop = function(cb) {
server.close(function() {
console.log('\n', ' DRAKOV STOPPED '.red.bold.inverse);
console.log(' DRAKOV STOPPED '.red.bold.inverse);
if (cb) {
cb();
}
Expand Down
100 changes: 100 additions & 0 deletions lib/middleware.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
var glob = require('glob');
var fs = require('fs');
var protagonist = require('protagonist');
var async = require('async');
var pathToRegexp = require('path-to-regexp');

var urlParser = require('./url-parser');
var route = require('./route');

var ROUTE_MAP = null;

var parseAction = function(uriTemplate, action) {
var parsedUrl = urlParser.parse(uriTemplate);
var key = parsedUrl.url;

ROUTE_MAP[key] = ROUTE_MAP[key] || { urlExpression: parsedUrl.url, methods: {} };
ROUTE_MAP[key].methods[action.method] = ROUTE_MAP[key].methods[action.method] || [];

var routeHandlers = route.getRouteHandlers(key, uriTemplate, action);
Array.prototype.push.apply(ROUTE_MAP[key].methods[action.method], routeHandlers);
};

var parseBlueprint = function(filePath) {
return function(cb) {
var data = fs.readFileSync(filePath, {encoding: 'utf8'});
protagonist.parse(data, function(err, result) {
if (err) {
console.log(err);
return;
}

result.ast.resourceGroups.forEach(function(resourceGroup){
resourceGroup.resources.forEach(function(resource){
resource.actions.forEach(function(action){
parseAction(resource.uriTemplate, action);
});
});
});
cb();
});
};
};

var setup = function(sourceFiles, cb) {
ROUTE_MAP = {};
glob(sourceFiles, {} , function (err, files) {
if (err) {
console.error('Failed to parse contracts path.', err);
process.exit(1);
}

var asyncFunctions = [];

files.forEach(function(filePath) {
asyncFunctions.push(parseBlueprint(filePath));
});

async.series(asyncFunctions, function(err) {
cb(err);
});
});

};

module.exports = function(sourceFiles) {
var runMiddleware = function(req, res, next) {
var handlers = null;
Object.keys(ROUTE_MAP).forEach(function(urlPattern) {
var regex = pathToRegexp(urlPattern);

// req.path allows us to delegate query string handling to the route handler functions
var match = regex.exec(req.path);
if (match) {
handlers = ROUTE_MAP[urlPattern].methods[req.method.toUpperCase()];
}
});

if (handlers) {
handlers.forEach(function(handler) {
handler(req, res, next);
});
} else {
next();
}
};

return function(req, res, next) {
if (!ROUTE_MAP) {
setup(sourceFiles, function(err) {
if (err) {
next(err);
return;
}
runMiddleware(req, res, next);
});
} else {
runMiddleware(req, res, next);
}
};
};
29 changes: 11 additions & 18 deletions lib/route.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,18 @@
var logger = require('./logger');
var content = require('./content');
var blueprintUrlParser = require('./url-parser');

exports.setRoute = function (app, method, routeUrl, action) {
exports.getRouteHandlers = function (method, uriTemplate, action) {

var getResponseHandler = function (specPairs) {

return function (httpReq, httpResp, next) {
return function (req, res, next) {
var matchRequests= function matchRequests(specPair){
if (content.matches(httpReq, specPair.request)) {
logger.log('[DRAKOV]'.red, method.green, routeUrl.yellow, ( specPair.request && specPair.request.description ? specPair.request.description : action.name).blue);
if (content.matches(req, specPair.request)) {
logger.log('[DRAKOV]'.red, action.method.green, uriTemplate.yellow, (specPair.request && specPair.request.description ? specPair.request.description : action.name).blue);

specPair.response.headers.forEach(function (header) {
httpResp.set(header.name, header.value);
res.set(header.name, header.value);
});
httpResp.status(+specPair.response.name).send(specPair.response.body);
res.status(+specPair.response.name).send(specPair.response.body);
return true;
}

Expand All @@ -24,17 +22,13 @@ exports.setRoute = function (app, method, routeUrl, action) {
if(!specPairs.some(matchRequests)){
next();
}

};
};

var parsedUrl = blueprintUrlParser.parse(routeUrl);

action.examples.forEach(function (example) {

var routeHandlers = action.examples.map(function (example) {
var specPairs = [];

var makePair = function ( response, index ){
var makePair = function (response, index){
var specPair = {
response: response,
request: 'undefined' === typeof example.responses[index] ? null : example.requests[index]
Expand All @@ -43,11 +37,10 @@ exports.setRoute = function (app, method, routeUrl, action) {
specPairs.push(specPair);
};

logger.log('[LOG]'.white, 'Setup Route:', method.green, parsedUrl.url.yellow, action.name.blue);

example.responses.forEach(makePair);

app[method.toLowerCase()](parsedUrl.url, getResponseHandler(specPairs));

return getResponseHandler(specPairs);
});

return routeHandlers;
};
4 changes: 2 additions & 2 deletions lib/setup.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ exports.isSSL = false;
exports.startServer = function (argv, app, cb) {

var startCb = function() {
console.log('\n', (' Drakov ' + version + ' ').bold.inverse, 'server listening on port ' + argv.serverPort.toString().bold.red);
console.log((' Drakov ' + version + ' ').bold.inverse, 'Listening on port ' + argv.serverPort.toString().bold.red);
if (argv.stealthmode) {
console.log('\n', ' STEALTH MODE '.grey.bold.inverse, 'running silently'.grey, '\n');
console.log(' STEALTH MODE '.grey.bold.inverse, 'running silently'.grey);
}

if (cb) {
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"glob": "^4.3.5",
"lodash": "^3.1.0",
"optimist": "0.6.1",
"path-to-regexp": "^1.0.3",
"protagonist": "^0.17.1"
},
"devDependencies": {
Expand Down

0 comments on commit bbe1a8b

Please sign in to comment.