Skip to content

Commit

Permalink
Merge pull request #12 from transcovo/monitor-route
Browse files Browse the repository at this point in the history
TRAN-8673 Add monitorRoute middleware
  • Loading branch information
GillesRasigade committed Jan 12, 2017
2 parents c7b7391 + 319e299 commit efdfd60
Show file tree
Hide file tree
Showing 5 changed files with 184 additions and 6 deletions.
4 changes: 3 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const sslRedirect = require('./src/middleware/sslRedirect');
const language = require('./src/middleware/language');
const i18n = require('./src/middleware/i18n');
const blacklistIPs = require('./src/middleware/blacklistIPs');
const monitorRoute = require('./src/middleware/monitorRoute');

module.exports = {
childLogger,
Expand All @@ -17,7 +18,8 @@ module.exports = {
sslRedirect,
language,
i18n,
blacklistIPs
blacklistIPs,
monitorRoute
};

// // //
10 changes: 6 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "express-middleware",
"version": "1.5.0",
"version": "1.6.0",
"description": "Set of middlewares for Chauffeur-Privé",
"keywords": [
"express",
Expand All @@ -26,17 +26,19 @@
"coveralls": "~2.11.6",
"eslint": "~1.10.3",
"eslint-config-cp": "transcovo/eslint-config-cp#1.1.0",
"express": "^4.14.0",
"istanbul": "~0.4.2",
"mocha": "~2.4.5",
"mocha-lcov-reporter": "~1.0.0",
"sinon": "~1.17.3"
"sinon": "~1.17.3",
"supertest": "^2.0.1"
},
"scripts": {
"test": "npm run lint && npm run coverage",
"lint": "eslint .",
"start": "npm test",
"coverage": "istanbul cover _mocha -- --recursive -R spec",
"coverage-html": "istanbul cover _mocha -- --recursive -R spec",
"coverage": "istanbul cover _mocha",
"coverage-html": "istanbul cover _mocha",
"mocha": "mocha"
},
"repository": {
Expand Down
61 changes: 61 additions & 0 deletions src/middleware/monitorRoute.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
'use strict';

const assert = require('assert');
const { get } = require('lodash');

module.exports = setup;
// // //

const PATH_REPLACE_IDS = /([\da-f]{24}|\d+)/g;
const PATH_SEPARATOR = /\/+/g;

/**
* Extract the path slug with possible ids removed from the original path
* @param {Request} req Express request
* @return {string} Route identifier
*/
function getRouteIdentifier(req) {
const method = get(req, 'method', 'na');
const protocol = get(req, 'protocol', 'na');
const path = `${get(req, 'path', 'na')}`
.substr(1)
.replace(PATH_REPLACE_IDS, ':id')
.replace(PATH_SEPARATOR, '_')
;

return `${protocol}.${method}.${path}`;
}

/**
* Increment the metric for the specific route
* @param {object} metrics Helper to send increment metric
* @param {Request} req Express request
* @return {void}
*/
function increment(metrics, req) {
const routeIdentifier = getRouteIdentifier(req);

metrics.increment(routeIdentifier);
}

/**
* Send a metric for a specific route activity
* @param {Object} metrics The metric (must have a .increment method)
* @return {Function} the middleware
*/
function setup(metrics) {
assert.ok(metrics, 'metrics is missing');
assert(typeof metrics.increment === 'function', 'Metrics must have a "increment" method');

/**
* The middleware
* @param {Object} req Express request
* @param {Object} res Express response
* @param {Function} next Express next handler
* @returns {void}
*/
return function middleware(req, res, next) {
increment(metrics, req);
next();
};
}
3 changes: 2 additions & 1 deletion test/mocha.opts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
--require co-mocha
--recursive
--recursive
-R spec
112 changes: 112 additions & 0 deletions test/src/middleware/monitorRoute.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
'use strict';
/* eslint no-unused-expressions:0 */

const expect = require('chai').expect;
const sinon = require('sinon');
const express = require('express');
const supertest = require('supertest');

const monitorRoute = require('../../../src/middleware/monitorRoute');

describe('monitor route middleware - monitorRoute.js', () => {
const sandbox = sinon.sandbox.create();
let stub;
const metrics = {
increment: () => null
};
const req = {
protocol: 'https',
method: 'GET',
path: '/users'
};

beforeEach(() => {
stub = sandbox.stub(metrics, 'increment');
});

afterEach(() => {
sandbox.restore();
});

it('throws assertion error when metric is missing', () => {
expect(monitorRoute).to.throw('AssertionError');
});

it('throws assertion error when metric has no increment method', () => {
expect(() => {
monitorRoute({});
}).to.throw('AssertionError');
});

it('must return a function', () => {
const middleware = monitorRoute(metrics);
expect(middleware).to.be.instanceof(Function);
});

it('must call the next middle', done => {
const middleware = monitorRoute(metrics);
middleware(req, null, done);
});

it('increments the metric for the given route', done => {
const middleware = monitorRoute(metrics);
middleware(req, null, () => {
expect(stub.calledOnce).to.be.equal(true);
expect(stub.args[0]).to.be.deep.equal(['https.GET.users']);
done();
});
});

it('removes the numeric ids from the path', done => {
const middleware = monitorRoute(metrics);
middleware(Object.assign({}, req, {
path: '/users/123456'
}), null, () => {
expect(stub.calledOnce).to.be.equal(true);
expect(stub.args[0]).to.be.deep.equal(['https.GET.users_:id']);
done();
});
});

it('removes the ObjectId from the path', done => {
const middleware = monitorRoute(metrics);
middleware(Object.assign({}, req, {
path: '/users/507f1f77bcf86cd799439011'
}), null, () => {
expect(stub.calledOnce).to.be.equal(true);
expect(stub.args[0]).to.be.deep.equal(['https.GET.users_:id']);
done();
});
});

it('removes all possibles ids from the path', done => {
const middleware = monitorRoute(metrics);
middleware(Object.assign({}, req, {
path: '/drivers/123456/ride/507f1f77bcf86cd799439011'
}), null, () => {
expect(stub.calledOnce).to.be.equal(true);
expect(stub.args[0]).to.be.deep.equal(['https.GET.drivers_:id_ride_:id']);
done();
});
});

describe('express integration', () => {
const app = express();
const request = supertest(app);

it('sends metrics properly with all the request stack', function* it() {
const router = new express.Router();
router.get('/user/:user_id', (_req, res) => {
res.send('ok');
});
app.use(monitorRoute(metrics));
app.use('/test', router);

const { text } = yield request.get('/test/user/507f1f77bcf86cd799439011');
expect(text).to.deep.equal('ok');

expect(stub.calledOnce).to.be.equal(true);
expect(stub.args[0]).to.be.deep.equal(['http.GET.test_user_:id']);
});
});
});

0 comments on commit efdfd60

Please sign in to comment.