-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #12 from transcovo/monitor-route
TRAN-8673 Add monitorRoute middleware
- Loading branch information
Showing
5 changed files
with
184 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
--require co-mocha | ||
--recursive | ||
--recursive | ||
-R spec |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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']); | ||
}); | ||
}); | ||
}); |