diff --git a/.gitignore b/.gitignore index b512c09..3091757 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -node_modules \ No newline at end of file +node_modules +coverage \ No newline at end of file diff --git a/index.js b/index.js deleted file mode 100644 index 4cc88b3..0000000 --- a/index.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('./lib'); \ No newline at end of file diff --git a/lib/handler.js b/lib/handler.js index b3f6a2e..9517e36 100644 --- a/lib/handler.js +++ b/lib/handler.js @@ -3,77 +3,86 @@ var _ = require('lodash'); var cache = {}; -function versionToFloat(v, noShorthand) { - if (_.isString(v)) { - v = v.replace('v', ''); +var versionToFloat = function (v, noShorthand) { - if (! noShorthand && v.length == 1) { - v = v + '.99'; + if (_.isString(v)) { + v = v.replace('v', ''); + + if (!noShorthand && v.length === 1) { + v = v + '.99'; + } } - } - return parseFloat(v); -} + return parseFloat(v); +}; -function cachedVersion(id, version) { - return cache[id] && cache[id][version]; -} +var cachedVersion = function (id, version) { -function detectVersionHandler(id, version, handlers) { - var handler; - var versions = Object.keys(handlers).sort() - .reverse(); + return cache[id] && cache[id][version]; +}; - _.some(versions, function testVersion(v) { - if (v <= version) { - handler = handlers[v]; - } +var detectVersionHandler = function (id, version, handlers) { + + var handler; + var versions = Object.keys(handlers).sort() + .reverse(); + + _.some(versions, function testVersion (v) { + + if (v <= version) { + handler = handlers[v]; + } + + return handler; + }); + + cacheVersionHandler(id, version, handler); return handler; - }); +}; - cacheVersionHandler(id, version, handler); +var cacheVersionHandler = function (id, version, handler) { - return handler; -} + if (id) { + cache[id] = cache[id] || {}; + cache[id][version] = handler; + } +}; -function cacheVersionHandler(id, version, handler) { - if (id) { - cache[id] = cache[id] || {}; - cache[id][version] = handler; - } -} +module.exports = function register (server) { -module.exports = function register(server) { - server.handler('versioned', function(route, handlers) { - var id = route.method + '::' + route.path; - var handlersIsArray = _.isArray(handlers); - var versions = {}; + server.handler('versioned', function (route, handlers) { - _.each(handlers, function(handler, version) { - version = versionToFloat(version, true); + var id = route.method + '::' + route.path; + var handlersIsArray = _.isArray(handlers); + var versions = {}; - if (handlersIsArray) { - version += 1; - } + _.each(handlers, function (handler, version) { - versions[version] = handler; - }); + version = versionToFloat(version, true); - var latest = Object.keys(versions).sort().pop(); + if (handlersIsArray) { + version += 1; + } - versions.latest = versions[latest]; + versions[version] = handler; + }); - return function(request, reply) { - var input = request.plugins.consistency.apiVersion; - var version = input !== 'latest' ? versionToFloat(input) : latest; - var handler = cachedVersion(id, version); + var latest = Object.keys(versions).sort().pop(); - if (! handler) { - handler = detectVersionHandler(id, version, versions) - } + versions.latest = versions[latest]; - return handler(request, reply); - }; - }); + return function (request, reply) { + + var input = request.plugins.consistency.apiVersion; + var version = input !== 'latest' ? versionToFloat(input) : latest; + var handler = cachedVersion(id, version); + + if (!handler) { + handler = detectVersionHandler(id, version, versions); + } + + return handler(request, reply); + }; + }); }; diff --git a/lib/index.js b/lib/index.js index 64a70dc..07879ca 100644 --- a/lib/index.js +++ b/lib/index.js @@ -4,41 +4,43 @@ var handler = require('./handler'); exports.register = function (server, options, next) { - server.ext('onPreHandler', function (request, reply) { - var uriParam = options.uriParam; - var params = request.params || {}; - var headerKey = options.customHeaderKey; - var headers = request.headers; - var acceptNamespace = options.acceptNamespace; - var acceptPattern = new RegExp('^application\/vnd\.' + acceptNamespace + '\.'); - var version; + server.ext('onPreHandler', function (request, reply) { - request.plugins.consistency = {}; + var uriParam = options.uriParam; + var params = {}; + params = request.params; + var headerKey = options.customHeaderKey; + var headers = request.headers; + var acceptNamespace = options.acceptNamespace; + var acceptPattern = new RegExp('^application\/vnd\.' + acceptNamespace + '\.'); + var version; - // URI versioning, if enabled - if (uriParam && params[uriParam]) { - version = params[uriParam]; - } else if (headerKey && headers[headerKey]) { - version = headers[headerKey]; - } else if (acceptNamespace && headers['accept'] && acceptPattern.test(headers['accept'])) { - version = headers['accept'].replace('application/vnd.' + acceptNamespace + '.', ''); - } else { - version = 'latest'; - } + request.plugins.consistency = {}; - // Default to latest - request.plugins.consistency.apiVersion = version; + // URI versioning, if enabled + if (uriParam && params[uriParam]) { + version = params[uriParam]; + } else if (headerKey && headers[headerKey]) { + version = headers[headerKey]; + } else if (acceptNamespace && headers.accept && acceptPattern.test(headers.accept)) { + version = headers.accept.replace('application/vnd.' + acceptNamespace + '.', ''); + } else { + version = 'latest'; + } - return reply.continue(); + // Default to latest + request.plugins.consistency.apiVersion = version; - }); + return reply.continue(); - handler(server, options); + }); - next(); + handler(server, options); + + next(); }; exports.register.attributes = { - pkg: require('../package.json') + name: 'zension' }; diff --git a/package.json b/package.json index f229a02..91d78a6 100644 --- a/package.json +++ b/package.json @@ -2,9 +2,9 @@ "name": "consistency", "version": "1.0.0", "description": "API versioning plugin for hapi.js", - "main": "index.js", + "main": "lib/index.js", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "test": "node node_modules/lab/bin/lab -a code -t 95 -m 15000 -L -v -C -r html -o coverage/coverage.html -r console -o stdout" }, "repository": { "type": "git", @@ -22,11 +22,11 @@ }, "homepage": "https://github.com/shakefon/consistency", "dependencies": { - "lodash": "^3.5.0" + "lodash": "3.x.x" }, "devDependencies": { - "code": "^1.3.0", - "hapi": "^8.4.0", - "lab": "^5.5.0" + "code": "1.x.x", + "hapi": "10.x.x", + "lab": "5.x.x" } } diff --git a/test/server.js b/test/server.js index c271a81..5d9c0a7 100644 --- a/test/server.js +++ b/test/server.js @@ -3,79 +3,82 @@ var consistency = require('../lib'); var server = new Hapi.Server(); -function reply(obj) { - return function(req, rep) { - rep(obj); - } -} +var reply = function (obj) { + + return function (req, rep) { + + rep(obj); + }; +}; server.connection({ port: 3000 }); server.register({ - register: consistency, - options: { - uriParam: 'apiVersion', - acceptNamespace: 'example', - customHeaderKey: 'api-version' - } -}, function registerRoutes() { - server.route({ - method: 'GET', - path: '/array', - handler: { - versioned: [ - reply({ - version: '1.0' - }), - reply({ - version: '2.0' - }), - reply({ - version: '3.0' - }) - ] - } - }); + register: consistency, + options: { + uriParam: 'apiVersion', + acceptNamespace: 'example', + customHeaderKey: 'api-version' + } +}, function registerRoutes () { + + server.route({ + method: 'GET', + path: '/array', + handler: { + versioned: [ + reply({ + version: '1.0' + }), + reply({ + version: '2.0' + }), + reply({ + version: '3.0' + }) + ] + } + }); - server.route({ - method: 'GET', - path: '/test', - handler: { - versioned: { - 'v1': reply({ - version: '1.0' - }), + server.route({ + method: 'GET', + path: '/test', + handler: { + versioned: { + 'v1': reply({ + version: '1.0' + }), - 'v2.0': reply({ - version: '2.0' - }), + 'v2.0': reply({ + version: '2.0' + }), - 'v1.5': reply({ - version: '1.5' - }) + 'v1.5': reply({ + version: '1.5' + }) + } } - } - }); + }); - server.route({ - method: 'GET', - path: '/url/{apiVersion?}', - handler: { - versioned: { - 'v1.0': reply({ - version: '1.0' - }), + server.route({ + method: 'GET', + path: '/url/{apiVersion?}', + handler: { + versioned: { + 'v1.0': reply({ + version: '1.0' + }), - 'v2.0': reply({ - version: '2.0' - }), + 'v2.0': reply({ + version: '2.0' + }), - 'v1.5': reply({ - version: '1.5' - }) + 'v1.5': reply({ + version: '1.5' + }) + } } - } - }); + }); }); module.exports = server; diff --git a/test/tests.js b/test/tests.js index e1e711d..3203d61 100644 --- a/test/tests.js +++ b/test/tests.js @@ -10,218 +10,249 @@ var beforeEach = lab.beforeEach; var afterEach = lab.afterEach; var server = require('./server'); -function runTests(options, version, done) { - server.inject(options, function (res) { - var response = res.result.version; - expect(response).to.exist(); - expect(response).to.be.a.string() - expect(parseFloat(response)).to.equal(version); - done(); - }); -} - -describe('Versioning', function() { - describe('custom header', function() { - it('should get version', function(done) { - runTests({ - url: '/url/v1.0' - }, 1.0, done); - }); +var runTests = function (options, version, done) { - it('should get version (shorthand)', function(done) { - runTests({ - url: '/url/v1' - }, 1.5, done); - }); + server.inject(options, function (res) { - it('should return closest version matching if version', function(done) { - runTests({ - url: '/url/v3.0' - }, 2.0, done); + var response = res.result.version; + expect(response).to.exist(); + expect(response).to.be.a.string(); + expect(parseFloat(response)).to.equal(version); + done(); }); +}; - it('should be optional to provide the v', function(done) { - runTests({ - url: '/url/1.5' - }, 1.5, done); - }); +describe('Versioning', function () { - it('should allow latest as version', function(done) { - runTests({ - url: '/test', - headers: { - 'api-version': 'latest' - } - }, 2.0, done); - }); + describe('custom header', function () { - it('should default to the latest if not provided', function(done) { - runTests({ - url: '/url/' - }, 2.0, done); - }); - }); - - describe('custom header', function() { - it('should get version', function(done) { - runTests({ - url: '/test', - headers: { - 'api-version': 'v1.0' - } - }, 1.0, done); - }); + it('should get version', function (done) { - it('should get version (shorthand)', function(done) { - runTests({ - url: '/test', - headers: { - 'api-version': 'v1' - } - }, 1.5, done); - }); + runTests({ + url: '/url/v1.0' + }, 1.0, done); + }); - it('should return closest version matching if version', function(done) { - runTests({ - url: '/test', - headers: { - 'api-version': 'v3.0' - } - }, 2.0, done); - }); + it('should get version (shorthand)', function (done) { - it('should be optional to provide the v', function(done) { - runTests({ - url: '/test', - headers: { - 'api-version': '1.5' - } - }, 1.5, done); - }); + runTests({ + url: '/url/v1' + }, 1.5, done); + }); - it('should allow latest as version', function(done) { - runTests({ - url: '/test', - headers: { - 'api-version': 'latest' - } - }, 2.0, done); - }); + it('should return closest version matching if version', function (done) { - it('should default to the latest if not provided', function(done) { - runTests({ - url: '/test' - }, 2.0, done); - }); - }); - - describe('accept header', function() { - var header = 'application/vnd.example.'; - - it('should get version', function(done) { - runTests({ - url: '/test', - headers: { - 'accept': header + 'v1.0' - } - }, 1.0, done); - }); + runTests({ + url: '/url/v3.0' + }, 2.0, done); + }); - it('should get version (shorthand)', function(done) { - runTests({ - url: '/test', - headers: { - 'accept': header + 'v1' - } - }, 1.5, done); - }); + it('should be optional to provide the v', function (done) { - it('should return closest version matching if version', function(done) { - runTests({ - url: '/test', - headers: { - 'accept': header + 'v3.0' - } - }, 2.0, done); - }); + runTests({ + url: '/url/1.5' + }, 1.5, done); + }); - it('should be optional to provide the v', function(done) { - runTests({ - url: '/test', - headers: { - 'accept': header + '1.5' - } - }, 1.5, done); - }); + it('should allow latest as version', function (done) { - it('should allow latest as version', function(done) { - runTests({ - url: '/test', - headers: { - 'accept': header + 'latest' - } - }, 2.0, done); - }); + runTests({ + url: '/test', + headers: { + 'api-version': 'latest' + } + }, 2.0, done); + }); - it('should default to the latest if not provided', function(done) { - runTests({ - url: '/test' - }, 2.0, done); - }); - }); - - describe('Works with array of handlers', function() { - it('should get version', function(done) { - runTests({ - url: '/array', - headers: { - 'api-version': 'v1.0' - } - }, 1.0, done); - }); + it('should default to the latest if not provided', function (done) { - it('should get version (shorthand)', function(done) { - runTests({ - url: '/array', - headers: { - 'api-version': 'v2' - } - }, 2.0, done); + runTests({ + url: '/url/' + }, 2.0, done); + }); }); - it('should return closest version matching if version', function(done) { - runTests({ - url: '/array', - headers: { - 'api-version': 'v4.0' - } - }, 3.0, done); - }); + describe('custom header', function () { + + it('should get version', function (done) { + + runTests({ + url: '/test', + headers: { + 'api-version': 'v1.0' + } + }, 1.0, done); + }); + + it('should get version (shorthand)', function (done) { + + runTests({ + url: '/test', + headers: { + 'api-version': 'v1' + } + }, 1.5, done); + }); + + it('should return closest version matching if version', function (done) { + + runTests({ + url: '/test', + headers: { + 'api-version': 'v3.0' + } + }, 2.0, done); + }); + + it('should be optional to provide the v', function (done) { + + runTests({ + url: '/test', + headers: { + 'api-version': '1.5' + } + }, 1.5, done); + }); + + it('should allow latest as version', function (done) { - it('should be optional to provide the v', function(done) { - runTests({ - url: '/array', - headers: { - 'api-version': '1' - } - }, 1, done); + runTests({ + url: '/test', + headers: { + 'api-version': 'latest' + } + }, 2.0, done); + }); + + it('should default to the latest if not provided', function (done) { + + runTests({ + url: '/test' + }, 2.0, done); + }); }); - it('should allow latest as version', function(done) { - runTests({ - url: '/array', - headers: { - 'api-version': 'latest' - } - }, 3.0, done); + describe('accept header', function () { + + var header = 'application/vnd.example.'; + + it('should get version', function (done) { + + runTests({ + url: '/test', + headers: { + 'accept': header + 'v1.0' + } + }, 1.0, done); + }); + + it('should get version (shorthand)', function (done) { + + runTests({ + url: '/test', + headers: { + 'accept': header + 'v1' + } + }, 1.5, done); + }); + + it('should return closest version matching if version', function (done) { + + runTests({ + url: '/test', + headers: { + 'accept': header + 'v3.0' + } + }, 2.0, done); + }); + + it('should be optional to provide the v', function (done) { + + runTests({ + url: '/test', + headers: { + 'accept': header + '1.5' + } + }, 1.5, done); + }); + + it('should allow latest as version', function (done) { + + runTests({ + url: '/test', + headers: { + 'accept': header + 'latest' + } + }, 2.0, done); + }); + + it('should default to the latest if not provided', function (done) { + + runTests({ + url: '/test' + }, 2.0, done); + }); }); - it('should default to the latest if not provided', function(done) { - runTests({ - url: '/array' - }, 3.0, done); + describe('Works with array of handlers', function () { + + it('should get version', function (done) { + + runTests({ + url: '/array', + headers: { + 'api-version': 'v1.0' + } + }, 1.0, done); + }); + + it('should get version (shorthand)', function (done) { + + runTests({ + url: '/array', + headers: { + 'api-version': 'v2' + } + }, 2.0, done); + }); + + it('should return closest version matching if version', function (done) { + + runTests({ + url: '/array', + headers: { + 'api-version': 'v4.0' + } + }, 3.0, done); + }); + + it('should be optional to provide the v', function (done) { + + runTests({ + url: '/array', + headers: { + 'api-version': '1' + } + }, 1, done); + }); + + it('should allow latest as version', function (done) { + + runTests({ + url: '/array', + headers: { + 'api-version': 'latest' + } + }, 3.0, done); + }); + + it('should default to the latest if not provided', function (done) { + + runTests({ + url: '/array' + }, 3.0, done); + }); }); - }); }); module.exports.lab = lab;