From ec25150b68dc6da75a0fa77d38588ab349e4fe7c Mon Sep 17 00:00:00 2001 From: Trevor Date: Wed, 20 Aug 2014 06:14:58 -0500 Subject: [PATCH 01/20] version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 16ed77a..b06650c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "swaggerize-express", - "version": "0.1.0-alpha.4", + "version": "0.1.0-alpha.5", "author": "Trevor Livingston ", "description": "Spec-first driven swagger express routing.", "keywords": [ From 91813be9ed483f1ce70d23450d6d6a11492e9003 Mon Sep 17 00:00:00 2001 From: Trevor Livingston Date: Thu, 21 Aug 2014 13:54:38 -0500 Subject: [PATCH 02/20] updated readme to remove --- README.md | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/README.md b/README.md index f47c21c..92b92f7 100644 --- a/README.md +++ b/README.md @@ -177,24 +177,6 @@ swaggerize --api config/api.json --models resources/models --handlers resources/ `--api` is required, but only one of `--models` or `--handlers` or `--tests` is required. -### Handler Signature - -A standard express handler signature: - -```javascript -function (req, res) { ... } -``` - -### Reply Function - -The `reply` function is provided to allow for model validation and error handling without monkey patching `res.send` -(or requiring `res.send` to be used vs `res.json`, etc). In addition to acting as a `res.send` method, it also provides -the following convenience properties: - -- `_raw` - the raw `response` object. -- `next()` - acts as `res.next()`. -- `redirect(url)` - acts as `res.redirect`. - ### Contribution In order to run the swaggerize-express unit tests, execute the following commands: From 7abadc974a4c29d5c8e86c549cfe124abb78fa41 Mon Sep 17 00:00:00 2001 From: Trevor Livingston Date: Thu, 21 Aug 2014 13:59:22 -0500 Subject: [PATCH 03/20] added test for output validation which could still be used in dev/test cases --- test/test-validation.js | 42 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 test/test-validation.js diff --git a/test/test-validation.js b/test/test-validation.js new file mode 100644 index 0000000..2045ef8 --- /dev/null +++ b/test/test-validation.js @@ -0,0 +1,42 @@ +'use strict'; + +var test = require('tape'), + validation = require('../lib/validation'); + +test('validation', function (t) { + var validator = validation.output({ + "id": "User", + "required": ["id", "name"], + "properties": { + "name": { + "type": "string" + }, + "id": { + "type": "integer" + } + } + }); + + t.test('output pass', function (t) { + t.plan(1); + + validator({ + id: 1, + name: 'Joe' + }, function (error) { + t.ok(!error); + }); + }); + + t.test('output fail', function (t) { + t.plan(1); + + validator({ + id: 'a', + name: 'Joe' + }, function (error) { + t.ok(error); + }); + }); + +}); From bdd1f2fe3c5715b184c52d05f18827a8f4c1978e Mon Sep 17 00:00:00 2001 From: Trevor Livingston Date: Thu, 21 Aug 2014 14:21:16 -0500 Subject: [PATCH 04/20] input validation tests --- lib/validation.js | 2 +- test/test-validation.js | 48 ++++++++++++++++++++++++++++++++++++----- 2 files changed, 44 insertions(+), 6 deletions(-) diff --git a/lib/validation.js b/lib/validation.js index 94e6b76..11203da 100644 --- a/lib/validation.js +++ b/lib/validation.js @@ -177,4 +177,4 @@ function coercion(type) { } module.exports.input = inputValidator; -module.exports.output = outputValidator; \ No newline at end of file +module.exports.output = outputValidator; diff --git a/test/test-validation.js b/test/test-validation.js index 2045ef8..0b7381d 100644 --- a/test/test-validation.js +++ b/test/test-validation.js @@ -4,7 +4,9 @@ var test = require('tape'), validation = require('../lib/validation'); test('validation', function (t) { - var validator = validation.output({ + var outputvalid, inputvalid; + + outputvalid = validation.output({ "id": "User", "required": ["id", "name"], "properties": { @@ -17,25 +19,61 @@ test('validation', function (t) { } }); + inputvalid = validation.input({ + paramType: 'query', + name: 'id', + type: 'integer', + required: true + }, 'string'); + + t.test('input pass', function (t) { + t.plan(1); + + inputvalid({ + param: function () { + return this.params.id; + }, + params: { + id: 1 + }, + }, {}, function (error) { + t.ok(!error, 'no error.'); + }); + }); + + t.test('input fail (not present)', function (t) { + t.plan(1); + + inputvalid({ + param: function () { + return undefined; + }, + params: { + }, + }, {}, function (error) { + t.ok(error, 'error.'); + }); + }); + t.test('output pass', function (t) { t.plan(1); - validator({ + outputvalid({ id: 1, name: 'Joe' }, function (error) { - t.ok(!error); + t.ok(!error, 'no error.'); }); }); t.test('output fail', function (t) { t.plan(1); - validator({ + outputvalid({ id: 'a', name: 'Joe' }, function (error) { - t.ok(error); + t.ok(error, 'error.'); }); }); From 9f3feed27ed4ee4c7faca331cc333937b07b8ec0 Mon Sep 17 00:00:00 2001 From: Trevor Livingston Date: Thu, 21 Aug 2014 14:35:16 -0500 Subject: [PATCH 05/20] additonal input validation test --- test/test-validation.js | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/test/test-validation.js b/test/test-validation.js index 0b7381d..f93a2ed 100644 --- a/test/test-validation.js +++ b/test/test-validation.js @@ -22,9 +22,8 @@ test('validation', function (t) { inputvalid = validation.input({ paramType: 'query', name: 'id', - type: 'integer', required: true - }, 'string'); + }, 'integer'); t.test('input pass', function (t) { t.plan(1); @@ -55,6 +54,21 @@ test('validation', function (t) { }); }); + t.test('input fail (wrong type)', function (t) { + t.plan(1); + + inputvalid({ + param: function () { + return this.params.id; + }, + params: { + id: 'a' + }, + }, {}, function (error) { + t.ok(error, 'error.'); + }); + }); + t.test('output pass', function (t) { t.plan(1); From b9c3c05700c05c9c60e029cef5283e81535bc336 Mon Sep 17 00:00:00 2001 From: Trevor Date: Sat, 23 Aug 2014 08:33:31 -0500 Subject: [PATCH 06/20] updated readme and version bump --- README.md | 8 +++++--- package.json | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 92b92f7..0a079e6 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,10 @@ # swaggerize-express +**Version:** `1.0.0-alpha.6` +**Stability:** `unstable` +**Changelog:** [https://github.com/krakenjs/swaggerize-express/blob/master/CHANGELOG.md](https://github.com/krakenjs/swaggerize-express/blob/master/CHANGELOG.md) + `swaggerize-express` is a "spec first" approach to building RESTful services with a [Swagger spec](https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md) and Express. @@ -13,8 +17,6 @@ and Express. - Input model validation. - Models and handlers stubs generator command (`swaggerize`). -`swaggerize-express` is currently `pre-release` and as a result may change without warning. - ### Why "Spec First" There are already a number of modules that help build REST services with express and swagger. However, @@ -68,7 +70,7 @@ server.listen(port, 'localhost', function () { }); ``` -Also checkout the [Quick Start Guide](QUICKSTART.md). +Also checkout the [Quick Start Guide](https://github.com/krakenjs/swaggerize-express/blob/master/QUICKSTART.md). ### Mount Path diff --git a/package.json b/package.json index b06650c..8a8bde9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "swaggerize-express", - "version": "0.1.0-alpha.5", + "version": "0.1.0-alpha.6", "author": "Trevor Livingston ", "description": "Spec-first driven swagger express routing.", "keywords": [ From d1fd4963a1ee941af466293b18617b2dff2f2490 Mon Sep 17 00:00:00 2001 From: Trevor Date: Sat, 23 Aug 2014 08:34:38 -0500 Subject: [PATCH 07/20] typo in readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0a079e6..450ddbc 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ # swaggerize-express -**Version:** `1.0.0-alpha.6` +**Version:** `0.1.0-alpha.6` **Stability:** `unstable` **Changelog:** [https://github.com/krakenjs/swaggerize-express/blob/master/CHANGELOG.md](https://github.com/krakenjs/swaggerize-express/blob/master/CHANGELOG.md) From 8c2539ce309554a8815215080b77f8bf0adb397a Mon Sep 17 00:00:00 2001 From: Trevor Date: Sat, 23 Aug 2014 08:35:27 -0500 Subject: [PATCH 08/20] updated format --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 450ddbc..46e5715 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,9 @@ # swaggerize-express -**Version:** `0.1.0-alpha.6` -**Stability:** `unstable` -**Changelog:** [https://github.com/krakenjs/swaggerize-express/blob/master/CHANGELOG.md](https://github.com/krakenjs/swaggerize-express/blob/master/CHANGELOG.md) +- **Version:** `0.1.0-alpha.6` +- **Stability:** `unstable` +- **Changelog:** [https://github.com/krakenjs/swaggerize-express/blob/master/CHANGELOG.md](https://github.com/krakenjs/swaggerize-express/blob/master/CHANGELOG.md) `swaggerize-express` is a "spec first" approach to building RESTful services with a [Swagger spec](https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md) and Express. From d3f4daba4ce042988f051a375c556af76b0d80e1 Mon Sep 17 00:00:00 2001 From: Trevor Date: Sat, 23 Aug 2014 12:24:36 -0500 Subject: [PATCH 09/20] breakup some express specifics from configuration --- lib/index.js | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/lib/index.js b/lib/index.js index 123c2db..1fe6c53 100644 --- a/lib/index.js +++ b/lib/index.js @@ -9,28 +9,19 @@ var assert = require('assert'), caller = require('caller'), expressroutes = require('./expressroutes'), utils = require('./utils'), - url = require('url'); + url = require('url'), + configure = require('./configure'); function swaggerize(options) { - var app, validation, basePath; + var app; - assert.ok(thing.isObject(options), 'Expected options to be an object.'); - assert.ok(thing.isObject(options.api), 'Expected an api definition.'); - - basePath = url.parse(options.api.basePath); - options.api.resourcePath = utils.prefix(options.api.resourcePath || '/', '/'); - basePath.path = basePath.pathname = options.api.resourcePath; - options.api.basePath = url.format(basePath); - - validation = schema.validate(options.api); - - assert.ifError(validation.error); - - if (thing.isString(options.handlers) || !options.handlers) { - options.handlers = options.handlers && path.resolve(options.handlers) || path.join(path.dirname(caller()), 'handlers'); + if (!options) { + options = {}; } - options.docs = options.docs || '/api-docs'; + options.baseDir = options.baseDir || path.dirname(caller()); + + options = configure(options); app = express(); @@ -44,6 +35,7 @@ function swaggerize(options) { Object.defineProperty(app, 'setUrl', { enumerable: true, value: function (value) { + var basePath = url.parse(options.api.basePath); value = url.parse(value); value.protocol && (basePath.protocol = value.protocol); From 52e0135d3b98ca553c90cfbd5b35c4761bf39194 Mon Sep 17 00:00:00 2001 From: Trevor Date: Sat, 23 Aug 2014 14:50:56 -0500 Subject: [PATCH 10/20] separate filecrawling from express specifics, rename some options, path param style replacement only in express. --- CHANGELOG.md | 4 +++ README.md | 8 +++--- lib/buildroutes.js | 2 +- lib/configure.js | 42 ++++++++++++++++++++++++++++++++ lib/expressroutes.js | 11 ++++----- lib/index.js | 8 +++--- test/test-configure.js | 25 +++++++++++++++++++ test/test-expressroutes.js | 50 +++++++++++++++++++++++--------------- test/test-routebuilder.js | 14 +++++++++++ test/test-swaggerize.js | 4 +-- 10 files changed, 131 insertions(+), 37 deletions(-) create mode 100644 lib/configure.js create mode 100644 test/test-configure.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 751670f..7af1123 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +### 1.0.0-rc.1 + +* `options.docs` is `options.docspath` and defaults to `/`. + ### 0.1.0-alpha.6 WARNING: Breaking changes! diff --git a/README.md b/README.md index 46e5715..3167ad8 100644 --- a/README.md +++ b/README.md @@ -33,15 +33,15 @@ var swaggerize = require('swaggerize-express'); app.use(swaggerize({ api: require('./api.json'), - docs: '/api-docs', - handlers: './handlers' + docspath: '/api-docs', + handlerspath: './handlers' })); ``` Options: - `api` - a valid Swagger 1.2 document. -- `docs` - the path to expose api docs for swagger-ui, etc. Defaults to `/api-docs`. +- `docspath` - the path to expose api docs for swagger-ui, etc. Defaults to `/`. - `handlers` - either a directory structure for route handlers or a premade object (see *Handlers Object* below). The base url for the api can also be updated via the `setUrl` function on the middleware. @@ -59,7 +59,7 @@ var server = http.createServer(app); var swagger = swaggerize({ api: require('./api.json'), - docs: '/api-docs', + docspath: '/api-docs', handlers: './handlers' }); diff --git a/lib/buildroutes.js b/lib/buildroutes.js index 45287ee..14ec5df 100644 --- a/lib/buildroutes.js +++ b/lib/buildroutes.js @@ -28,7 +28,7 @@ function buildroutes(options) { route = { name: operation.nickname, - path: utils.convertPath(path), + path: path, method: undefined, validators: [], handler: undefined diff --git a/lib/configure.js b/lib/configure.js new file mode 100644 index 0000000..800b917 --- /dev/null +++ b/lib/configure.js @@ -0,0 +1,42 @@ +'use strict'; + +var assert = require('assert'), + schema = require('./schema'), + thing = require('core-util-is'), + path = require('path'), + caller = require('caller'), + utils = require('./utils'), + url = require('url'), + buildroutes = require('./buildroutes'); + +function configure(options) { + var app, validation, basePath, routes; + + assert.ok(thing.isObject(options), 'Expected options to be an object.'); + assert.ok(thing.isObject(options.api), 'Expected an api definition.'); + + basePath = url.parse(options.api.basePath); + options.api.resourcePath = utils.prefix(options.api.resourcePath || '/', '/'); + basePath.path = basePath.pathname = options.api.resourcePath; + options.api.basePath = url.format(basePath); + + validation = schema.validate(options.api); + + assert.ifError(validation.error); + + options.docspath = options.docspath || '/'; + + if (thing.isString(options.handlers) || !options.handlers) { + options.handlers = options.handlers && path.resolve(options.handlers) || path.join(options.basedir || path.dirname(caller()), 'handlers'); + } + + routes = buildroutes(options); + + return { + api: options.api, + docspath: options.docspath, + routes: routes + }; +} + +module.exports = configure; diff --git a/lib/expressroutes.js b/lib/expressroutes.js index d974fa1..23ede5c 100644 --- a/lib/expressroutes.js +++ b/lib/expressroutes.js @@ -12,20 +12,19 @@ var path = require('path'), * @param options */ function expressroutes(router, options) { - var routes; + var routes = options.routes || []; - routes = buildroutes(options); - - router.get(options.api.resourcePath + utils.prefix(options.docs || '/api-docs', '/'), function (req, res) { + router.get(options.api.resourcePath + utils.prefix(options.docspath || '', '/'), function (req, res) { res.json(options.api); }); routes.forEach(function (route) { - var args; + var args, path; //If a handler exists, add it. if (route.handler) { - args = [options.api.resourcePath + utils.prefix(route.path, '/')]; + path = utils.convertPath(route.path); + args = [options.api.resourcePath + utils.prefix(path, '/')]; if (thing.isArray(route.handler)) { if (route.handler.length > 1) { diff --git a/lib/index.js b/lib/index.js index 1fe6c53..890618d 100644 --- a/lib/index.js +++ b/lib/index.js @@ -15,11 +15,9 @@ var assert = require('assert'), function swaggerize(options) { var app; - if (!options) { - options = {}; - } + assert.ok(thing.isObject(options), 'Expected options to be an object.'); - options.baseDir = options.baseDir || path.dirname(caller()); + options.basedir = path.dirname(caller()); options = configure(options); @@ -36,8 +34,8 @@ function swaggerize(options) { enumerable: true, value: function (value) { var basePath = url.parse(options.api.basePath); - value = url.parse(value); + value = url.parse(value); value.protocol && (basePath.protocol = value.protocol); value.host && (basePath.host = value.host); diff --git a/test/test-configure.js b/test/test-configure.js new file mode 100644 index 0000000..12228c7 --- /dev/null +++ b/test/test-configure.js @@ -0,0 +1,25 @@ +'use strict'; + +var test = require('tape'), + configure = require('../lib/configure'), + path = require('path'); + +test('configure', function (t) { + + t.test('fail no options', function (t) { + t.plan(1); + + t.throws(function () { + configure(); + }, 'throws exception.'); + }); + + t.test('fail no api definition', function (t) { + t.plan(1); + + t.throws(function () { + configure({}); + }, 'throws exception.'); + }); + +}); diff --git a/test/test-expressroutes.js b/test/test-expressroutes.js index 48fe40d..396b977 100644 --- a/test/test-expressroutes.js +++ b/test/test-expressroutes.js @@ -8,7 +8,7 @@ var test = require('tape'), test('express routes', function (t) { t.test('test api', function (t) { - t.plan(6); + t.plan(3); var app = express(), child = express(); @@ -17,17 +17,21 @@ test('express routes', function (t) { expressroutes(app, { api: require('./fixtures/api.json'), - handlers: require('path').join(__dirname, 'handlers') + docspath: '/api-docs', + routes: [ + { + method: 'get', + path: '/hello/:subject', + handler: function (req, res) {} + } + ] }); stack = Array.prototype.slice.call(parent._router.stack, 3); - t.strictEqual(stack.length, 5, 'routes added.'); + t.strictEqual(stack.length, 2, '2 routes added.'); t.strictEqual(stack[0].route.path, '/v1/greetings/api-docs', 'api-docs added.'); t.strictEqual(stack[1].route.path, '/v1/greetings/hello/:subject', 'hello added.'); - t.strictEqual(stack[2].route.path, '/v1/greetings/sub/:id', 'sub added.'); - t.strictEqual(stack[3].route.path, '/v1/greetings/sub/:id', 'sub added (head).'); - t.strictEqual(stack[4].route.path, '/v1/greetings/sub/:id/path', 'sub/path added.'); }); app.use(child); @@ -43,9 +47,9 @@ test('express routes', function (t) { expressroutes(app, { api: require('./fixtures/api.json'), - handlers: { - - } + docspath: '/api-docs', + validators: [], + routes: [] }); stack = Array.prototype.slice.call(parent._router.stack, 3); @@ -57,8 +61,8 @@ test('express routes', function (t) { app.use(child); }); - t.test('test variable filenames', function (t) { - t.plan(7); + t.test('test middlewares in handler', function (t) { + t.plan(4); var app = express(), child = express(); @@ -67,18 +71,26 @@ test('express routes', function (t) { expressroutes(app, { api: require('./fixtures/collections.json'), - handlers: require('path').join(__dirname, 'handlers') + docspath: '/api-docs', + routes: [ + { + method: 'get', + path: '/middlewares', + validators: [], + handler: [ + function m1(req, res, next) {}, + function (req, res) {} + ] + } + ] }); stack = Array.prototype.slice.call(parent._router.stack, 3); - t.strictEqual(stack.length, 4, 'three routes added.'); - t.strictEqual(stack[0].route.path, '/v1/collections/api-docs', 'api-docs added.'); - t.strictEqual(stack[1].route.path, '/v1/collections/stuffs', '/stuffs added.'); - t.strictEqual(stack[2].route.path, '/v1/collections/stuffs/:id', '/stuffs/:id added.'); - t.strictEqual(stack[3].route.path, '/v1/collections/middlewares', '/middlewares added.'); - t.strictEqual(stack[3].route.stack.length, 2, '/middlewares has middleware.'); - t.strictEqual(stack[3].route.stack[0].name, 'm1', '/middlewares has middleware named m1.'); + t.strictEqual(stack.length, 2, '2 routes added.'); + t.strictEqual(stack[1].route.path, '/v1/collections/middlewares', '/middlewares added.'); + t.strictEqual(stack[1].route.stack.length, 2, '/middlewares has middleware.'); + t.strictEqual(stack[1].route.stack[0].name, 'm1', '/middlewares has middleware named m1.'); }); app.use(child); diff --git a/test/test-routebuilder.js b/test/test-routebuilder.js index fd5989f..6dc8d9c 100644 --- a/test/test-routebuilder.js +++ b/test/test-routebuilder.js @@ -19,6 +19,7 @@ test('routebuilder', function (t) { t.ok(route.hasOwnProperty('name'), 'has name property.'); t.ok(route.hasOwnProperty('path'), 'has path property.'); t.ok(route.hasOwnProperty('validators'), 'has validators property.'); + t.ok(route.hasOwnProperty('handler'), 'has handler property.'); }); t.end(); @@ -36,11 +37,24 @@ test('routebuilder', function (t) { t.ok(route.hasOwnProperty('name'), 'has name property.'); t.ok(route.hasOwnProperty('path'), 'has path property.'); t.ok(route.hasOwnProperty('validators'), 'has validators property.'); + t.ok(route.hasOwnProperty('handler'), 'has handler property.'); }); t.end(); }); + t.test('filenames with path variables', function (t) { + var routes; + + routes = buildroutes({ api: require('./fixtures/collections.json'), handlers: path.join(__dirname, 'handlers') }); + + t.strictEqual(routes.length, 3, 'added 2 routes.'); + + t.strictEqual(routes[1].path, '/stuffs/{id}'); + + t.end(); + }); + t.test('bad dir', function (t) { t.plan(1); diff --git a/test/test-swaggerize.js b/test/test-swaggerize.js index da5ad41..e4e41bd 100644 --- a/test/test-swaggerize.js +++ b/test/test-swaggerize.js @@ -31,7 +31,7 @@ test('swaggycat valid input/output', function (t) { t.test('docs', function (t) { t.plan(2); - request(app).get('/v1/greetings/api-docs').end(function (error, response) { + request(app).get('/v1/greetings/').end(function (error, response) { t.ok(!error, 'no error.'); t.strictEqual(response.statusCode, 200, '200 status.'); }); @@ -77,7 +77,7 @@ test('swaggycat valid input/output', function (t) { }); -test('swaggycat invalid input', function (t) { +test('input validators', function (t) { var app = express(); From 25cee5701fe47bf9b96345920454a65fe7afa79f Mon Sep 17 00:00:00 2001 From: Trevor Date: Sat, 23 Aug 2014 22:01:18 -0500 Subject: [PATCH 11/20] only add routes that have handlers, renamed `validators` to `before` to reflect middleware. --- lib/buildroutes.js | 6 +++--- lib/expressroutes.js | 25 ++++++++++--------------- test/test-expressroutes.js | 5 +++-- test/test-routebuilder.js | 6 +++--- 4 files changed, 19 insertions(+), 23 deletions(-) diff --git a/lib/buildroutes.js b/lib/buildroutes.js index 14ec5df..b50326a 100644 --- a/lib/buildroutes.js +++ b/lib/buildroutes.js @@ -30,7 +30,7 @@ function buildroutes(options) { name: operation.nickname, path: path, method: undefined, - validators: [], + before: [], handler: undefined }; @@ -41,7 +41,7 @@ function buildroutes(options) { operation.parameters && operation.parameters.forEach(function (parameter) { var model = models && models[parameter.type] || parameter.type; - route.validators.push(validation.input(parameter, model)); + route.before.push(validation.input(parameter, model)); }); pathnames = []; @@ -55,7 +55,7 @@ function buildroutes(options) { route.handler = matchpath('$' + operation.method.toLowerCase(), pathnames, handlers[pathnames[0]]); - routes.push(route); + route.handler && routes.push(route); }); }); diff --git a/lib/expressroutes.js b/lib/expressroutes.js index 23ede5c..7ed79d3 100644 --- a/lib/expressroutes.js +++ b/lib/expressroutes.js @@ -21,24 +21,19 @@ function expressroutes(router, options) { routes.forEach(function (route) { var args, path; - //If a handler exists, add it. - if (route.handler) { - path = utils.convertPath(route.path); - args = [options.api.resourcePath + utils.prefix(path, '/')]; - - if (thing.isArray(route.handler)) { - if (route.handler.length > 1) { - Array.prototype.push.apply(route.validators, route.handler.slice(0, route.handler.length - 1)); - } - route.handler = route.handler[route.handler.length - 1]; + path = utils.convertPath(route.path); + args = [options.api.resourcePath + utils.prefix(path, '/')]; + + if (thing.isArray(route.handler)) { + if (route.handler.length > 1) { + Array.prototype.push.apply(route.before, route.handler.slice(0, route.handler.length - 1)); } - Array.prototype.push.apply(args, route.validators); - args.push(route.handler); - router[route.method].apply(router, args); - return; + route.handler = route.handler[route.handler.length - 1]; } - utils.debuglog('no matching handler for %s.', route.path); + Array.prototype.push.apply(args, route.before); + args.push(route.handler); + router[route.method].apply(router, args); }); } diff --git a/test/test-expressroutes.js b/test/test-expressroutes.js index 396b977..7a6d0e8 100644 --- a/test/test-expressroutes.js +++ b/test/test-expressroutes.js @@ -22,6 +22,7 @@ test('express routes', function (t) { { method: 'get', path: '/hello/:subject', + before: [], handler: function (req, res) {} } ] @@ -48,7 +49,7 @@ test('express routes', function (t) { expressroutes(app, { api: require('./fixtures/api.json'), docspath: '/api-docs', - validators: [], + before: [], routes: [] }); @@ -76,7 +77,7 @@ test('express routes', function (t) { { method: 'get', path: '/middlewares', - validators: [], + before: [], handler: [ function m1(req, res, next) {}, function (req, res) {} diff --git a/test/test-routebuilder.js b/test/test-routebuilder.js index 6dc8d9c..197d44b 100644 --- a/test/test-routebuilder.js +++ b/test/test-routebuilder.js @@ -12,13 +12,13 @@ test('routebuilder', function (t) { routes = buildroutes({ api: api, handlers: path.join(__dirname, 'handlers') }); - t.strictEqual(routes.length, 5, 'added 5 routes.'); + t.strictEqual(routes.length, 4, 'added 5 routes.'); routes.forEach(function (route) { t.ok(route.hasOwnProperty('method'), 'has method property.'); t.ok(route.hasOwnProperty('name'), 'has name property.'); t.ok(route.hasOwnProperty('path'), 'has path property.'); - t.ok(route.hasOwnProperty('validators'), 'has validators property.'); + t.ok(route.hasOwnProperty('before'), 'has before property.'); t.ok(route.hasOwnProperty('handler'), 'has handler property.'); }); @@ -36,7 +36,7 @@ test('routebuilder', function (t) { t.ok(route.hasOwnProperty('method'), 'has method property.'); t.ok(route.hasOwnProperty('name'), 'has name property.'); t.ok(route.hasOwnProperty('path'), 'has path property.'); - t.ok(route.hasOwnProperty('validators'), 'has validators property.'); + t.ok(route.hasOwnProperty('before'), 'has before property.'); t.ok(route.hasOwnProperty('handler'), 'has handler property.'); }); From 4ec2873e83adfb38cdea9d6c42c07d5651f25f78 Mon Sep 17 00:00:00 2001 From: Trevor Date: Sun, 24 Aug 2014 19:47:30 -0500 Subject: [PATCH 12/20] removed unnecessary defaulting of undefined parameters and provide better primitive validation. --- lib/validation.js | 45 +++++++++---------- package.json | 2 +- test/test-validation.js | 97 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 121 insertions(+), 23 deletions(-) diff --git a/lib/validation.js b/lib/validation.js index 11203da..2eae2bc 100644 --- a/lib/validation.js +++ b/lib/validation.js @@ -2,7 +2,8 @@ var thing = require('core-util-is'), schema = require('./schema'), - utils = require('./utils'); + utils = require('./utils'), + sformat = require('util').format; /** * Creates validation middleware for a parameter. @@ -27,7 +28,7 @@ function inputValidator(parameter, model) { if (!value && parameter.required) { utils.debuglog('required parameter \'%s\' missing.', parameter.name); res.statusCode = 400; - next(new Error('required parameter \'' + parameter.name + '\' missing.')); + next(new Error(sformat('required parameter \'%s\' missing.', parameter.name))); return; } @@ -92,47 +93,47 @@ function outputValidator(model) { * @returns {validate} */ function map(model) { - if (thing.isString(model)) { - model = { - type: model - }; - } return function validate(data, callback) { - var value, result, error; + var value, result, error, actual, type; - value = !thing.isObject(data) && model.id ? {} : data || def(model.type); + value = !thing.isObject(data) && model.id ? {} : data; - result = schema.validate(value, model); - result.valid || (error = result.error); + if (thing.isString(model)) { + actual = typeof value; + type = jsontype(model); + if (actual !== type) { + error = new Error(sformat('invalid type: %s (expected %s)', actual, model)); + } + } + else { + result = schema.validate(value, model); + result.valid || (error = result.error); + } callback(error); }; } /** - * Default value for a null value of type. + * Maps a type to a json type for primitive validation. * @param type - * @returns {*} + * @returns string */ -function def(type) { - if (!type) { - return undefined; - } - +function jsontype(type) { switch (type) { case 'integer': case 'float': case 'long': case 'double': case 'byte': - return 0; + return 'number'; case 'string': - return String(); + return 'string'; case 'boolean': - return false; + return 'boolean'; default: - return undefined; + return 'undefined'; } } diff --git a/package.json b/package.json index 8a8bde9..a327447 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "swaggerize-express", - "version": "0.1.0-alpha.6", + "version": "1.0.0-alpha.6", "author": "Trevor Livingston ", "description": "Spec-first driven swagger express routing.", "keywords": [ diff --git a/test/test-validation.js b/test/test-validation.js index f93a2ed..045905b 100644 --- a/test/test-validation.js +++ b/test/test-validation.js @@ -54,6 +54,103 @@ test('validation', function (t) { }); }); + t.test('input validation skip (not present, not required)', function (t) { + t.plan(1); + + validation.input({ + paramType: 'query', + name: 'id', + required: false + }, 'integer')({ + param: function () { + return undefined; + }, + params: { + }, + }, {}, function (error) { + t.ok(!error, 'no error.'); + }); + }); + + t.test('input coerce to float (pass)', function (t) { + t.plan(1); + + validation.input({ + paramType: 'query', + name: 'id', + required: true + }, 'float')({ + param: function () { + return this.params.id; + }, + params: { + id: '1.0' + }, + }, {}, function (error) { + error && console.error(error); + t.ok(!error, 'no error.'); + }); + }); + + t.test('input coerce to byte (pass)', function (t) { + t.plan(1); + + validation.input({ + paramType: 'query', + name: 'id', + required: true + }, 'byte')({ + param: function () { + return this.params.id; + }, + params: { + id: 'a' + }, + }, {}, function (error) { + error && console.error(error); + t.ok(!error, 'no error.'); + }); + }); + + t.test('input coerce to boolean (pass)', function (t) { + t.plan(1); + + validation.input({ + paramType: 'query', + name: 'id', + required: true + }, 'boolean')({ + param: function () { + return this.params.id; + }, + params: { + id: 1 + }, + }, {}, function (error) { + error && console.error(error); + t.ok(!error, 'no error.'); + }); + }); + + t.test('input coerce to string (pass)', function (t) { + t.plan(1); + + validation.input({ + paramType: 'query', + name: 'id', + required: true + }, 'string')({ + param: function () { + return this.params.id; + }, + params: { + id: 1 + }, + }, {}, function (error) { + t.ok(!error, 'no error.'); + }); + }); + t.test('input fail (wrong type)', function (t) { t.plan(1); From 9cc0d3fed15421d9eec0d06052637a42ee25df47 Mon Sep 17 00:00:00 2001 From: Trevor Livingston Date: Tue, 26 Aug 2014 13:10:22 -0500 Subject: [PATCH 13/20] more tests for validation and configuration --- test/test-configure.js | 19 +++++++++++++++++-- test/test-validation.js | 41 ++++++++++++++++++++++++++++++++++------- 2 files changed, 51 insertions(+), 9 deletions(-) diff --git a/test/test-configure.js b/test/test-configure.js index 12228c7..141a798 100644 --- a/test/test-configure.js +++ b/test/test-configure.js @@ -1,11 +1,12 @@ 'use strict'; var test = require('tape'), + thing = require('core-util-is'), configure = require('../lib/configure'), path = require('path'); test('configure', function (t) { - + t.test('fail no options', function (t) { t.plan(1); @@ -16,10 +17,24 @@ test('configure', function (t) { t.test('fail no api definition', function (t) { t.plan(1); - + t.throws(function () { configure({}); }, 'throws exception.'); }); + t.test('api', function (t) { + t.plan(5); + + var options = configure({ + api: require('./fixtures/api.json') + }); + + t.ok(thing.isObject(options), 'returns object.'); + t.ok(thing.isObject(options.api), 'returns options.api object.'); + t.ok(thing.isString(options.docspath), 'returns options.docspath string.'); + t.ok(thing.isArray(options.routes), 'returns options.routes array.'); + t.strictEqual(options.routes.length, 4, 'routes.length 4.'); + }); + }); diff --git a/test/test-validation.js b/test/test-validation.js index 045905b..bf89e32 100644 --- a/test/test-validation.js +++ b/test/test-validation.js @@ -166,23 +166,50 @@ test('validation', function (t) { }); }); - t.test('output pass', function (t) { +t.test('output pass', function (t) { t.plan(1); outputvalid({ id: 1, - name: 'Joe' + name: 'Test' }, function (error) { - t.ok(!error, 'no error.'); + t.ok(!error, 'error.'); }); }); - t.test('output fail', function (t) { - t.plan(1); + t.test('output fail (schema fail)', function (t) { + t.plan(3); + + outputvalid({ + id: 1, + name: 2 + }, function (error) { + t.ok(error, 'error.'); + }); + + outputvalid({ + id: 'Test', + name: 1 + }, function (error) { + t.ok(error, 'error.'); + }); + + outputvalid('Test', function (error) { + t.ok(error, 'error.'); + }); + }); + + t.test('output fail (missing required)', function (t) { + t.plan(2); + + outputvalid({ + id: 1 + }, function (error) { + t.ok(error, 'error.'); + }); outputvalid({ - id: 'a', - name: 'Joe' + name: 'Test' }, function (error) { t.ok(error, 'error.'); }); From 0c4de22cd71fcbb31e070124f3758c3dbe30d313 Mon Sep 17 00:00:00 2001 From: Trevor Livingston Date: Tue, 26 Aug 2014 13:46:04 -0500 Subject: [PATCH 14/20] takes on core route building and this becomes express specific --- .gitmodules | 3 - README.md | 14 +-- bin/lib/swaggerize.js | 2 +- lib/buildroutes.js | 84 -------------- lib/configure.js | 42 ------- lib/expressroutes.js | 38 ++++++- lib/index.js | 6 +- lib/readhandlers.js | 70 ------------ lib/schema/index.js | 41 ------- lib/schema/swagger-spec | 1 - lib/validation.js | 181 ------------------------------ package.json | 6 +- test/test-configure.js | 40 ------- test/test-expressroutes.js | 6 +- test/test-routebuilder.js | 66 ----------- test/test-schema.js | 101 ----------------- test/test-swaggerize.js | 8 +- test/test-validation.js | 218 ------------------------------------- 18 files changed, 48 insertions(+), 879 deletions(-) delete mode 100644 lib/buildroutes.js delete mode 100644 lib/configure.js delete mode 100644 lib/readhandlers.js delete mode 100644 lib/schema/index.js delete mode 160000 lib/schema/swagger-spec delete mode 100644 lib/validation.js delete mode 100644 test/test-configure.js delete mode 100644 test/test-routebuilder.js delete mode 100644 test/test-schema.js delete mode 100644 test/test-validation.js diff --git a/.gitmodules b/.gitmodules index 5ae3e97..e69de29 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +0,0 @@ -[submodule "lib/schema/swagger-spec"] - path = lib/schema/swagger-spec - url = https://github.com/wordnik/swagger-spec.git diff --git a/README.md b/README.md index 3167ad8..cc08502 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ # swaggerize-express -- **Version:** `0.1.0-alpha.6` +- **Version:** `1.0.0-rc.1` - **Stability:** `unstable` - **Changelog:** [https://github.com/krakenjs/swaggerize-express/blob/master/CHANGELOG.md](https://github.com/krakenjs/swaggerize-express/blob/master/CHANGELOG.md) @@ -34,7 +34,7 @@ var swaggerize = require('swaggerize-express'); app.use(swaggerize({ api: require('./api.json'), docspath: '/api-docs', - handlerspath: './handlers' + handlers: './handlers' })); ``` @@ -178,13 +178,3 @@ swaggerize --api config/api.json --models resources/models --handlers resources/ ``` `--api` is required, but only one of `--models` or `--handlers` or `--tests` is required. - -### Contribution - -In order to run the swaggerize-express unit tests, execute the following commands: - -```bash -$ git submodule update --init --recursive -$ npm install -$ npm test -``` diff --git a/bin/lib/swaggerize.js b/bin/lib/swaggerize.js index 1edae20..bc80f1a 100644 --- a/bin/lib/swaggerize.js +++ b/bin/lib/swaggerize.js @@ -4,7 +4,7 @@ var minimist = require('minimist'), fs = require('fs'), path = require('path'), mkdirp = require('mkdirp'), - schema = require('../../lib/schema'), + schema = require('swaggerize-builder/lib/schema'), create = require('./create'); module.exports = function (argp) { diff --git a/lib/buildroutes.js b/lib/buildroutes.js deleted file mode 100644 index b50326a..0000000 --- a/lib/buildroutes.js +++ /dev/null @@ -1,84 +0,0 @@ -'use strict'; - -var utils = require('./utils'), - validation = require('./validation'), - readhandlers = require('./readhandlers'); - -/** - * Convert definition of api to something we can work with. - * @param options - * @returns {Array} - */ -function buildroutes(options) { - var routes, handlers; - - handlers = readhandlers(options.handlers); - - routes = []; - - options.api.apis.forEach(function (def) { - var path, models; - - path = def.path; - - models = options.api.models; - - def.operations.forEach(function (operation) { - var route, pathnames, model; - - route = { - name: operation.nickname, - path: path, - method: undefined, - before: [], - handler: undefined - }; - - route.method = operation.method.toLowerCase(); - - model = models && models[operation.type] || operation.type; - - operation.parameters && operation.parameters.forEach(function (parameter) { - var model = models && models[parameter.type] || parameter.type; - - route.before.push(validation.input(parameter, model)); - }); - - pathnames = []; - - //Figure out the names from the params. - path.split('/').forEach(function (element) { - if (element) { - pathnames.push(element); - } - }); - - route.handler = matchpath('$' + operation.method.toLowerCase(), pathnames, handlers[pathnames[0]]); - - route.handler && routes.push(route); - }); - }); - - return routes; -} - -/** - * Match a route handler to a given path name set. - * @param method - * @param pathnames - * @param handlers - * @returns {*} - */ -function matchpath(method, pathnames, handlers) { - if (!handlers) { - return null; - } - if (pathnames.length > 1) { - pathnames.shift(); - return matchpath(method, pathnames, handlers[pathnames[0]]); - } - - return handlers[pathnames[0]] ? handlers[pathnames[0]] : handlers[method]; -} - -module.exports = buildroutes; diff --git a/lib/configure.js b/lib/configure.js deleted file mode 100644 index 800b917..0000000 --- a/lib/configure.js +++ /dev/null @@ -1,42 +0,0 @@ -'use strict'; - -var assert = require('assert'), - schema = require('./schema'), - thing = require('core-util-is'), - path = require('path'), - caller = require('caller'), - utils = require('./utils'), - url = require('url'), - buildroutes = require('./buildroutes'); - -function configure(options) { - var app, validation, basePath, routes; - - assert.ok(thing.isObject(options), 'Expected options to be an object.'); - assert.ok(thing.isObject(options.api), 'Expected an api definition.'); - - basePath = url.parse(options.api.basePath); - options.api.resourcePath = utils.prefix(options.api.resourcePath || '/', '/'); - basePath.path = basePath.pathname = options.api.resourcePath; - options.api.basePath = url.format(basePath); - - validation = schema.validate(options.api); - - assert.ifError(validation.error); - - options.docspath = options.docspath || '/'; - - if (thing.isString(options.handlers) || !options.handlers) { - options.handlers = options.handlers && path.resolve(options.handlers) || path.join(options.basedir || path.dirname(caller()), 'handlers'); - } - - routes = buildroutes(options); - - return { - api: options.api, - docspath: options.docspath, - routes: routes - }; -} - -module.exports = configure; diff --git a/lib/expressroutes.js b/lib/expressroutes.js index 7ed79d3..e77e224 100644 --- a/lib/expressroutes.js +++ b/lib/expressroutes.js @@ -2,7 +2,6 @@ var path = require('path'), utils = require('./utils'), - buildroutes = require('./buildroutes'), thing = require('core-util-is'); /** @@ -12,26 +11,55 @@ var path = require('path'), * @param options */ function expressroutes(router, options) { - var routes = options.routes || []; + var routes, routes = options.routes || []; router.get(options.api.resourcePath + utils.prefix(options.docspath || '', '/'), function (req, res) { res.json(options.api); }); routes.forEach(function (route) { - var args, path; + var args, path, before; path = utils.convertPath(route.path); args = [options.api.resourcePath + utils.prefix(path, '/')]; + before = []; + + route.validators.forEach(function (validator) { + var parameter, validate; + + parameter = validator.parameter; + validate = validator.validate; + + before.push(function validateInput(req, res, next) { + var value, isPath; + + isPath = parameter.paramType === 'path' || parameter.paramType === 'query'; + value = isPath ? req.param(parameter.name) : req.body; + + validate(value, function (error, newvalue) { + if (error) { + res.statusCode = 400; + next(error); + return; + } + + if (isPath) { + req.params[parameter.name] = newvalue; + } + + next(); + }); + }); + }); if (thing.isArray(route.handler)) { if (route.handler.length > 1) { - Array.prototype.push.apply(route.before, route.handler.slice(0, route.handler.length - 1)); + Array.prototype.push.apply(before, route.handler.slice(0, route.handler.length - 1)); } route.handler = route.handler[route.handler.length - 1]; } - Array.prototype.push.apply(args, route.before); + Array.prototype.push.apply(args, before); args.push(route.handler); router[route.method].apply(router, args); }); diff --git a/lib/index.js b/lib/index.js index 890618d..87950bd 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,7 +1,6 @@ 'use strict'; var assert = require('assert'), - schema = require('./schema'), express = require('express'), thing = require('core-util-is'), path = require('path'), @@ -10,7 +9,7 @@ var assert = require('assert'), expressroutes = require('./expressroutes'), utils = require('./utils'), url = require('url'), - configure = require('./configure'); + builder = require('swaggerize-builder'); function swaggerize(options) { var app; @@ -19,7 +18,7 @@ function swaggerize(options) { options.basedir = path.dirname(caller()); - options = configure(options); + options = builder(options); app = express(); @@ -54,7 +53,6 @@ function swaggerize(options) { function mount(options) { return function onmount(parent) { parent._router.stack.pop(); - expressroutes(parent._router, options); }; } diff --git a/lib/readhandlers.js b/lib/readhandlers.js deleted file mode 100644 index 52fb6b9..0000000 --- a/lib/readhandlers.js +++ /dev/null @@ -1,70 +0,0 @@ -'use strict'; - -var assert = require('assert'), - thing = require('core-util-is'), - path = require('path'), - fs = require('fs'); - -/** - * Reads the given path and requires all .js files. - * @param path - * @returns {{}} - */ -function read(dir) { - var routes, obj; - - if (thing.isString(dir)) { - assert.ok(fs.existsSync(dir), 'Specifed or default \'handlers\' directory does not exist.'); - - routes = {}; - - fs.readdirSync(dir).forEach(function (name) { - var abspath, key, stat; - - abspath = path.join(dir, name); - stat = fs.statSync(abspath); - key = name.replace(/\.js/, ''); - - if (stat.isFile()) { - if (name.match(/^.*\.(js)$/)) { - obj = require(abspath); - - if (!routes[key]) { - routes[key] = {}; - } - - Object.keys(obj).forEach(function (k) { - routes[key][isHttpMethod(k) ? '$' + k.toLowerCase() : k] = obj[k]; - }); - } - } - if (stat.isDirectory()) { - routes[key] = read(abspath); - } - }); - - return routes; - } - return dir; -} - -/** - * Determines if the given method is a supported HTTP method. - * @param method - * @returns {boolean} - */ -function isHttpMethod(method) { - return (typeof method === 'string') && { - get: 'GET', - post: 'POST', - put: 'PUT', - delete: 'DELETE', - head: 'HEAD', - options: 'OPTIONS', - trace: 'TRACE', - connect: 'CONNECT', - patch: 'PATCH' - }.hasOwnProperty(method.toLowerCase()); -} - -module.exports = read; \ No newline at end of file diff --git a/lib/schema/index.js b/lib/schema/index.js deleted file mode 100644 index b46cdd6..0000000 --- a/lib/schema/index.js +++ /dev/null @@ -1,41 +0,0 @@ -'use strict'; - -var tv4 = require('tv4'), - assert = require('assert'), - fs = require('fs'), - path = require('path'); - -var schemaPath, baseSchemaPath, baseSchema, modelSchema; - -schemaPath = path.join(__dirname, 'swagger-spec/schemas/v1.2'); -baseSchemaPath = path.join(schemaPath, 'apiDeclaration.json'); -modelSchema = require(path.join(schemaPath, 'modelsObject')); - -assert.ok(fs.existsSync(schemaPath)); -assert.ok(fs.existsSync(baseSchemaPath)); - -baseSchema = require(baseSchemaPath); - -fs.readdirSync(schemaPath).forEach(function (file) { - var schema; - - schema = require(path.join(schemaPath, file)); - - tv4.addSchema(schema); -}); - -module.exports = { - /** - * Validate against an optional schema, defaulting to base api schema. - * @param data - * @param schema - * @returns {*} - */ - validate: function validate(data, schema) { - var results; - - results = tv4.validateResult(data, schema || baseSchema, true); - - return results; - } -}; diff --git a/lib/schema/swagger-spec b/lib/schema/swagger-spec deleted file mode 160000 index 6d932fc..0000000 --- a/lib/schema/swagger-spec +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 6d932fcb84323a3e5704cf1abdee443e1086ca69 diff --git a/lib/validation.js b/lib/validation.js deleted file mode 100644 index 2eae2bc..0000000 --- a/lib/validation.js +++ /dev/null @@ -1,181 +0,0 @@ -'use strict'; - -var thing = require('core-util-is'), - schema = require('./schema'), - utils = require('./utils'), - sformat = require('util').format; - -/** - * Creates validation middleware for a parameter. - * @param parameter - * @param model - * @returns {validateInput} - */ -function inputValidator(parameter, model) { - var coerce, validate, isPath; - - validate = model && map(model); - - isPath = parameter.paramType === 'path' || parameter.paramType === 'query'; - - if (parameter.type !== 'string') { - coerce = coercion(model); - } - - return function validateInput(req, res, next) { - var value = isPath ? req.param(parameter.name) : req.body; - - if (!value && parameter.required) { - utils.debuglog('required parameter \'%s\' missing.', parameter.name); - res.statusCode = 400; - next(new Error(sformat('required parameter \'%s\' missing.', parameter.name))); - return; - } - - if (validate) { - coerce && (value = coerce(value)); - isPath && (req.params[parameter.name] = value); - - if (!value && !parameter.required) { - next(); - return; - } - - validate(value, function (error) { - if (error) { - utils.debuglog('error validating model schema \'%s\' for \'%s\'.', model, parameter.name); - res.statusCode = 400; - next(new Error(error.message)); - return; - } - - next(); - }); - - return; - } - - next(); - }; -} - -/** - * Create an output validator for the given model. - * @param model - * @returns {Function} - */ -function outputValidator(model) { - var coerce, validate; - - validate = model && map(model); - coerce = validate && coercion(model); - - return function validateOutput(data, callback) { - var value; - - if (validate) { - value = data && coerce ? coerce(data) : data; - - validate(value, function (error) { - if (error) { - callback(error); - return; - } - callback(null); - }); - } - }; -} - -/** - * Maps a type to a schema. - * @param type - * @returns {validate} - */ -function map(model) { - - return function validate(data, callback) { - var value, result, error, actual, type; - - value = !thing.isObject(data) && model.id ? {} : data; - - if (thing.isString(model)) { - actual = typeof value; - type = jsontype(model); - if (actual !== type) { - error = new Error(sformat('invalid type: %s (expected %s)', actual, model)); - } - } - else { - result = schema.validate(value, model); - result.valid || (error = result.error); - } - - callback(error); - }; -} - -/** - * Maps a type to a json type for primitive validation. - * @param type - * @returns string - */ -function jsontype(type) { - switch (type) { - case 'integer': - case 'float': - case 'long': - case 'double': - case 'byte': - return 'number'; - case 'string': - return 'string'; - case 'boolean': - return 'boolean'; - default: - return 'undefined'; - } -} - -/** - * Returns a function that coerces a type. - * Coercion of doubles and longs are not supported in Javascript and strings should be used instead for 64bit numbers. - * @param type - */ -function coercion(type) { - var fn; - - switch (type) { - case 'integer': - case 'float': - case 'long': - case 'double': - fn = function (data) { - if (isNaN(data)) { - return data; - } - return Number(data); - }; - break; - case 'string': - fn = String; - break; - case 'byte': - fn = function (data) { - return isNaN(data) ? new Buffer(data)[0] : Number(data); - }; - break; - case 'boolean': - fn = Boolean; - break; - case 'date': - case 'dateTime': - fn = Date.parse; - break; - } - - return fn; -} - -module.exports.input = inputValidator; -module.exports.output = outputValidator; diff --git a/package.json b/package.json index a327447..1fe4029 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "swaggerize-express", - "version": "1.0.0-alpha.6", + "version": "1.0.0-rc.1", "author": "Trevor Livingston ", "description": "Spec-first driven swagger express routing.", "keywords": [ @@ -26,14 +26,14 @@ "node": "0.10.x" }, "dependencies": { + "swaggerize-builder": "^1.0.0", "async": "^0.9.0", "caller": "^0.0.1", "core-util-is": "^1.0.1", "debuglog": "^1.0.1", "lodash": "^2.4.1", "minimist": "^0.2.0", - "mkdirp": "^0.5.0", - "tv4": "^1.1.0" + "mkdirp": "^0.5.0" }, "peerDependencies": { "express": "^4.0.0" diff --git a/test/test-configure.js b/test/test-configure.js deleted file mode 100644 index 141a798..0000000 --- a/test/test-configure.js +++ /dev/null @@ -1,40 +0,0 @@ -'use strict'; - -var test = require('tape'), - thing = require('core-util-is'), - configure = require('../lib/configure'), - path = require('path'); - -test('configure', function (t) { - - t.test('fail no options', function (t) { - t.plan(1); - - t.throws(function () { - configure(); - }, 'throws exception.'); - }); - - t.test('fail no api definition', function (t) { - t.plan(1); - - t.throws(function () { - configure({}); - }, 'throws exception.'); - }); - - t.test('api', function (t) { - t.plan(5); - - var options = configure({ - api: require('./fixtures/api.json') - }); - - t.ok(thing.isObject(options), 'returns object.'); - t.ok(thing.isObject(options.api), 'returns options.api object.'); - t.ok(thing.isString(options.docspath), 'returns options.docspath string.'); - t.ok(thing.isArray(options.routes), 'returns options.routes array.'); - t.strictEqual(options.routes.length, 4, 'routes.length 4.'); - }); - -}); diff --git a/test/test-expressroutes.js b/test/test-expressroutes.js index 7a6d0e8..b363d35 100644 --- a/test/test-expressroutes.js +++ b/test/test-expressroutes.js @@ -22,7 +22,7 @@ test('express routes', function (t) { { method: 'get', path: '/hello/:subject', - before: [], + validators: [], handler: function (req, res) {} } ] @@ -49,7 +49,7 @@ test('express routes', function (t) { expressroutes(app, { api: require('./fixtures/api.json'), docspath: '/api-docs', - before: [], + validators: [], routes: [] }); @@ -77,7 +77,7 @@ test('express routes', function (t) { { method: 'get', path: '/middlewares', - before: [], + validators: [], handler: [ function m1(req, res, next) {}, function (req, res) {} diff --git a/test/test-routebuilder.js b/test/test-routebuilder.js deleted file mode 100644 index 197d44b..0000000 --- a/test/test-routebuilder.js +++ /dev/null @@ -1,66 +0,0 @@ -'use strict'; - -var test = require('tape'), - api = require('./fixtures/api.json'), - path = require('path'), - buildroutes = require('../lib/buildroutes'); - -test('routebuilder', function (t) { - - t.test('build', function (t) { - var routes; - - routes = buildroutes({ api: api, handlers: path.join(__dirname, 'handlers') }); - - t.strictEqual(routes.length, 4, 'added 5 routes.'); - - routes.forEach(function (route) { - t.ok(route.hasOwnProperty('method'), 'has method property.'); - t.ok(route.hasOwnProperty('name'), 'has name property.'); - t.ok(route.hasOwnProperty('path'), 'has path property.'); - t.ok(route.hasOwnProperty('before'), 'has before property.'); - t.ok(route.hasOwnProperty('handler'), 'has handler property.'); - }); - - t.end(); - }); - - t.test('collections', function (t) { - var routes; - - routes = buildroutes({ api: require('./fixtures/collections.json'), handlers: path.join(__dirname, 'handlers') }); - - t.strictEqual(routes.length, 3, 'added 2 routes.'); - - routes.forEach(function (route) { - t.ok(route.hasOwnProperty('method'), 'has method property.'); - t.ok(route.hasOwnProperty('name'), 'has name property.'); - t.ok(route.hasOwnProperty('path'), 'has path property.'); - t.ok(route.hasOwnProperty('before'), 'has before property.'); - t.ok(route.hasOwnProperty('handler'), 'has handler property.'); - }); - - t.end(); - }); - - t.test('filenames with path variables', function (t) { - var routes; - - routes = buildroutes({ api: require('./fixtures/collections.json'), handlers: path.join(__dirname, 'handlers') }); - - t.strictEqual(routes.length, 3, 'added 2 routes.'); - - t.strictEqual(routes[1].path, '/stuffs/{id}'); - - t.end(); - }); - - t.test('bad dir', function (t) { - t.plan(1); - - t.throws(function () { - buildroutes({ api: api, handlers: 'asdf' }); - }, 'throws error for bad directory.'); - }); - -}); diff --git a/test/test-schema.js b/test/test-schema.js deleted file mode 100644 index fe5598c..0000000 --- a/test/test-schema.js +++ /dev/null @@ -1,101 +0,0 @@ -'use strict'; - -var test = require('tape'), - schema = require('../lib/schema'), - apiDefinition = require('./fixtures/api.json'); - -test('schema', function (t) { - - t.test('good api', function (t) { - t.plan(1); - - var results = schema.validate(apiDefinition); - - t.ok(results.valid, 'no errors'); - }); - - t.test('bad api', function (t) { - t.plan(2); - - var results = schema.validate({ - "swaggerVersion": "1.2", - "basePath": "http://localhost:8000/greetings", - "apis": [ - { - "path": "/hello/{subject}", - "operations": [ - { - "method": "GET", - "summary": "Greet our subject with hello!", - "type": "string", - "parameters": [ - { - "name": "subject", - "description": "The subject to be greeted.", - "required": true, - "type": "string", - "paramType": "path" - } - ] - } - ] - } - ] - }); - - t.ok(!results.valid, 'bad'); - t.ok(results.error, 'has error.'); - }); - - t.test('good model', function (t) { - t.plan(1); - - var modelSchema = { - "id": "User", - "required": ["id", "name"], - "properties": { - "name": { - "type": "string" - }, - "id": { - "type": "integer", - "format": "int64" - } - } - }; - - var results = schema.validate({ - "id": 123, - "name": "John Doe" - }, modelSchema); - - t.ok(results.valid, 'no errors'); - }); - - t.test('bad model', function (t) { - t.plan(2); - - var modelSchema = { - "id": "User", - "required": ["id", "name"], - "properties": { - "name": { - "type": "string" - }, - "id": { - "type": "integer", - "format": "int64" - } - } - }; - - var results = schema.validate({ - "id": "asdf", - "name": "John Doe" - }, modelSchema); - - t.ok(!results.valid, 'bad'); - t.ok(results.error, 'has error.'); - }); - -}); diff --git a/test/test-swaggerize.js b/test/test-swaggerize.js index e4e41bd..cd6147d 100644 --- a/test/test-swaggerize.js +++ b/test/test-swaggerize.js @@ -86,14 +86,14 @@ test('input validators', function (t) { handlers: { sub: { '{id}': { - $get: function (req, reply) { - reply('foobar'); + $get: function (req, res) { + res.send('foobar'); } } }, goodbye: { - $get: function (req, reply) { - reply('baz'); + $get: function (req, res) { + res.send('baz'); } } } diff --git a/test/test-validation.js b/test/test-validation.js deleted file mode 100644 index bf89e32..0000000 --- a/test/test-validation.js +++ /dev/null @@ -1,218 +0,0 @@ -'use strict'; - -var test = require('tape'), - validation = require('../lib/validation'); - -test('validation', function (t) { - var outputvalid, inputvalid; - - outputvalid = validation.output({ - "id": "User", - "required": ["id", "name"], - "properties": { - "name": { - "type": "string" - }, - "id": { - "type": "integer" - } - } - }); - - inputvalid = validation.input({ - paramType: 'query', - name: 'id', - required: true - }, 'integer'); - - t.test('input pass', function (t) { - t.plan(1); - - inputvalid({ - param: function () { - return this.params.id; - }, - params: { - id: 1 - }, - }, {}, function (error) { - t.ok(!error, 'no error.'); - }); - }); - - t.test('input fail (not present)', function (t) { - t.plan(1); - - inputvalid({ - param: function () { - return undefined; - }, - params: { - }, - }, {}, function (error) { - t.ok(error, 'error.'); - }); - }); - - t.test('input validation skip (not present, not required)', function (t) { - t.plan(1); - - validation.input({ - paramType: 'query', - name: 'id', - required: false - }, 'integer')({ - param: function () { - return undefined; - }, - params: { - }, - }, {}, function (error) { - t.ok(!error, 'no error.'); - }); - }); - - t.test('input coerce to float (pass)', function (t) { - t.plan(1); - - validation.input({ - paramType: 'query', - name: 'id', - required: true - }, 'float')({ - param: function () { - return this.params.id; - }, - params: { - id: '1.0' - }, - }, {}, function (error) { - error && console.error(error); - t.ok(!error, 'no error.'); - }); - }); - - t.test('input coerce to byte (pass)', function (t) { - t.plan(1); - - validation.input({ - paramType: 'query', - name: 'id', - required: true - }, 'byte')({ - param: function () { - return this.params.id; - }, - params: { - id: 'a' - }, - }, {}, function (error) { - error && console.error(error); - t.ok(!error, 'no error.'); - }); - }); - - t.test('input coerce to boolean (pass)', function (t) { - t.plan(1); - - validation.input({ - paramType: 'query', - name: 'id', - required: true - }, 'boolean')({ - param: function () { - return this.params.id; - }, - params: { - id: 1 - }, - }, {}, function (error) { - error && console.error(error); - t.ok(!error, 'no error.'); - }); - }); - - t.test('input coerce to string (pass)', function (t) { - t.plan(1); - - validation.input({ - paramType: 'query', - name: 'id', - required: true - }, 'string')({ - param: function () { - return this.params.id; - }, - params: { - id: 1 - }, - }, {}, function (error) { - t.ok(!error, 'no error.'); - }); - }); - - t.test('input fail (wrong type)', function (t) { - t.plan(1); - - inputvalid({ - param: function () { - return this.params.id; - }, - params: { - id: 'a' - }, - }, {}, function (error) { - t.ok(error, 'error.'); - }); - }); - -t.test('output pass', function (t) { - t.plan(1); - - outputvalid({ - id: 1, - name: 'Test' - }, function (error) { - t.ok(!error, 'error.'); - }); - }); - - t.test('output fail (schema fail)', function (t) { - t.plan(3); - - outputvalid({ - id: 1, - name: 2 - }, function (error) { - t.ok(error, 'error.'); - }); - - outputvalid({ - id: 'Test', - name: 1 - }, function (error) { - t.ok(error, 'error.'); - }); - - outputvalid('Test', function (error) { - t.ok(error, 'error.'); - }); - }); - - t.test('output fail (missing required)', function (t) { - t.plan(2); - - outputvalid({ - id: 1 - }, function (error) { - t.ok(error, 'error.'); - }); - - outputvalid({ - name: 'Test' - }, function (error) { - t.ok(error, 'error.'); - }); - }); - -}); From af32a67c6ca4508b051e6b883c7e95ae6b77d1a0 Mon Sep 17 00:00:00 2001 From: Trevor Livingston Date: Tue, 26 Aug 2014 13:50:25 -0500 Subject: [PATCH 15/20] removed utils --- lib/expressroutes.js | 4 +- lib/index.js | 2 - lib/utils.js | 63 ----------------------------- test/test-utils.js | 95 -------------------------------------------- 4 files changed, 2 insertions(+), 162 deletions(-) delete mode 100644 lib/utils.js delete mode 100644 test/test-utils.js diff --git a/lib/expressroutes.js b/lib/expressroutes.js index e77e224..aaad718 100644 --- a/lib/expressroutes.js +++ b/lib/expressroutes.js @@ -1,7 +1,7 @@ 'use strict'; var path = require('path'), - utils = require('./utils'), + utils = require('swaggerize-builder/lib/utils'), thing = require('core-util-is'); /** @@ -20,7 +20,7 @@ function expressroutes(router, options) { routes.forEach(function (route) { var args, path, before; - path = utils.convertPath(route.path); + path = route.path.replace(/{([^}]+)}/g, ':$1'); args = [options.api.resourcePath + utils.prefix(path, '/')]; before = []; diff --git a/lib/index.js b/lib/index.js index 87950bd..fba6d1e 100644 --- a/lib/index.js +++ b/lib/index.js @@ -4,10 +4,8 @@ var assert = require('assert'), express = require('express'), thing = require('core-util-is'), path = require('path'), - fs = require('fs'), caller = require('caller'), expressroutes = require('./expressroutes'), - utils = require('./utils'), url = require('url'), builder = require('swaggerize-builder'); diff --git a/lib/utils.js b/lib/utils.js deleted file mode 100644 index e4cc7db..0000000 --- a/lib/utils.js +++ /dev/null @@ -1,63 +0,0 @@ -'use strict'; - -var pkg = require('../package.json'); - -module.exports = { - debuglog: require('debuglog')(pkg.name), - - convertPath: function (path) { - return path.replace(/{([^}]+)}/g, ':$1'); - }, - - endsWith: function (haystack, needle) { - if (!haystack || !needle) { - return false; - } - - if (needle.length === 1) { - return haystack[haystack.length - 1] === needle; - } - - return haystack.slice(haystack.length - needle.length) === needle; - }, - - prefix: function (str, pre) { - str = str || ''; - if (str.indexOf(pre) === 0) { - return str; - } - - str = pre + str; - return str; - }, - - unprefix: function (str, pre) { - str = str || ''; - if (str.indexOf(pre) === 0) { - str = str.substr(pre.length); - return str; - } - - return str; - }, - - suffix: function (str, suff) { - str = str || ''; - if (this.endsWith(str, suff)) { - return str; - } - - str = str + suff; - return str; - }, - - unsuffix: function (str, suff) { - str = str || ''; - if (this.endsWith(str, suff)) { - str = str.substr(0, str.length - suff.length); - return str; - } - - return str; - } -}; diff --git a/test/test-utils.js b/test/test-utils.js deleted file mode 100644 index ab3bab5..0000000 --- a/test/test-utils.js +++ /dev/null @@ -1,95 +0,0 @@ -'use strict'; - -var test = require('tape'), - utils = require('../lib/utils'); - -test('utils', function (t) { - - t.test('convertPath', function (t) { - t.plan(1); - - var path = utils.convertPath('/foo/{id}/asdf/{name}'); - - t.strictEqual(path, '/foo/:id/asdf/:name', 'is converted.'); - }); - - t.test('prefix', function (t) { - t.plan(3); - - var str = 'foobar'; - - str = utils.prefix(str, 'foo'); - - t.equal(str, 'foobar', 'string had prefix so is the same.'); - - str = 'bar'; - - str = utils.prefix(str, 'foo'); - - t.equal(str, 'foobar', 'string did not have prefix so was changed.'); - - t.equal(utils.prefix(undefined, 'foo'), 'foo', 'handled undefined.'); - }); - - t.test('unprefix', function (t) { - t.plan(3); - - var str = 'foobar'; - - str = utils.unprefix(str, 'foo'); - - t.equal(str, 'bar', 'string had prefix so is changed.'); - - str = 'bar'; - - str = utils.unprefix(str, 'foo'); - - t.equal(str, 'bar', 'string did not have prefix so was not changed.'); - - t.equal(utils.unprefix(undefined, 'foo'), '', 'handled undefined.'); - }); - - t.test('suffix', function (t) { - t.plan(3); - - var str = 'foobar'; - - str = utils.suffix(str, 'bar'); - - t.equal(str, 'foobar', 'string had suffix so is the same.'); - - str = 'foo'; - - str = utils.suffix(str, 'bar'); - - t.equal(str, 'foobar', 'string did not have suffix so was changed.'); - - t.equal(utils.suffix(undefined, 'foo'), 'foo', 'handled undefined.'); - }); - - t.test('unsuffix', function (t) { - t.plan(3); - - var str = 'foobar'; - - str = utils.unsuffix(str, 'bar'); - - t.equal(str, 'foo', 'string had suffix so is changed.'); - - str = 'foo'; - - str = utils.unsuffix(str, 'bar'); - - t.equal(str, 'foo', 'string did not have suffix so was not changed.'); - - t.equal(utils.unsuffix(undefined, 'foo'), '', 'handled undefined.'); - }); - - t.test('ends with', function (t) { - t.plan(2); - t.ok(utils.endsWith('foobar', 'bar'), 'foobar ends with bar'); - t.ok(!utils.endsWith('foobar', 'x'), 'foobar doesn\'t end with x'); - }); - - -}); From 9bba049395096f172dd1489c1e9ffb62fd05d4e6 Mon Sep 17 00:00:00 2001 From: Trevor Livingston Date: Tue, 26 Aug 2014 14:29:16 -0500 Subject: [PATCH 16/20] Cleanup unused variables. --- .gitmodules | 0 bin/swaggerize.js | 3 +-- lib/expressroutes.js | 2 +- test/test-expressroutes.js | 3 +-- test/test-swaggerize.js | 18 +++++++++--------- 5 files changed, 12 insertions(+), 14 deletions(-) delete mode 100644 .gitmodules diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index e69de29..0000000 diff --git a/bin/swaggerize.js b/bin/swaggerize.js index 14d2dd7..dc3d544 100755 --- a/bin/swaggerize.js +++ b/bin/swaggerize.js @@ -1,8 +1,7 @@ #!/usr/bin/env node 'use strict'; -var minimist = require('minimist'), - swaggerize = require('swaggerize-express/bin/lib/swaggerize'); +var swaggerize = require('swaggerize-express/bin/lib/swaggerize'); var result = swaggerize(process.argv); diff --git a/lib/expressroutes.js b/lib/expressroutes.js index aaad718..c9ef0f7 100644 --- a/lib/expressroutes.js +++ b/lib/expressroutes.js @@ -11,7 +11,7 @@ var path = require('path'), * @param options */ function expressroutes(router, options) { - var routes, routes = options.routes || []; + var routes = options.routes || []; router.get(options.api.resourcePath + utils.prefix(options.docspath || '', '/'), function (req, res) { res.json(options.api); diff --git a/test/test-expressroutes.js b/test/test-expressroutes.js index b363d35..aced579 100644 --- a/test/test-expressroutes.js +++ b/test/test-expressroutes.js @@ -2,8 +2,7 @@ var test = require('tape'), expressroutes = require('../lib/expressroutes'), - express = require('express'), - request = require('supertest'); + express = require('express'); test('express routes', function (t) { diff --git a/test/test-swaggerize.js b/test/test-swaggerize.js index cd6147d..4dd5997 100644 --- a/test/test-swaggerize.js +++ b/test/test-swaggerize.js @@ -5,27 +5,27 @@ var test = require('tape'), express = require('express'), request = require('supertest'); -test('swaggycat valid input/output', function (t) { +test('swagger valid input/output', function (t) { var app = express(); - var swaggycat = swaggerize({ + var swagger = swaggerize({ api: require('./fixtures/api.json') }); - app.use(swaggycat); + app.use(swagger); t.test('api', function (t) { t.plan(5); - t.ok(swaggycat.hasOwnProperty('_api'), 'has _api property.'); - t.ok(swaggycat._api, '_api is an object.'); + t.ok(swagger.hasOwnProperty('_api'), 'has _api property.'); + t.ok(swagger._api, '_api is an object.'); - t.ok(swaggycat.hasOwnProperty('setUrl'), 'has setUrl property.'); - t.strictEqual(typeof swaggycat.setUrl, 'function', 'setUrl is a function.'); + t.ok(swagger.hasOwnProperty('setUrl'), 'has setUrl property.'); + t.strictEqual(typeof swagger.setUrl, 'function', 'setUrl is a function.'); - swaggycat.setUrl('http://localhost:8080'); + swagger.setUrl('http://localhost:8080'); - t.strictEqual(swaggycat._api.basePath, 'http://localhost:8080/v1/greetings'); + t.strictEqual(swagger._api.basePath, 'http://localhost:8080/v1/greetings'); }); t.test('docs', function (t) { From 7596ba119986ee0e74f46b9862040924e9a56efc Mon Sep 17 00:00:00 2001 From: Trevor Livingston Date: Tue, 26 Aug 2014 15:17:57 -0500 Subject: [PATCH 17/20] Updated to not pass `docspath` to builder. --- lib/index.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/index.js b/lib/index.js index fba6d1e..c4066dd 100644 --- a/lib/index.js +++ b/lib/index.js @@ -10,17 +10,19 @@ var assert = require('assert'), builder = require('swaggerize-builder'); function swaggerize(options) { - var app; + var app, config; assert.ok(thing.isObject(options), 'Expected options to be an object.'); options.basedir = path.dirname(caller()); - options = builder(options); + config = builder(options); + + config.docspath = options.docspath || '/'; app = express(); - app.once('mount', mount(options)); + app.once('mount', mount(config)); Object.defineProperty(app, '_api', { enumerable: false, @@ -30,13 +32,13 @@ function swaggerize(options) { Object.defineProperty(app, 'setUrl', { enumerable: true, value: function (value) { - var basePath = url.parse(options.api.basePath); + var basePath = url.parse(config.api.basePath); value = url.parse(value); value.protocol && (basePath.protocol = value.protocol); value.host && (basePath.host = value.host); - options.api.basePath = url.format(basePath); + config.api.basePath = url.format(basePath); } }); From dcf5a7a507fb46d5cd12eea62e93acaad70f5c32 Mon Sep 17 00:00:00 2001 From: Trevor Date: Tue, 26 Aug 2014 22:02:06 -0500 Subject: [PATCH 18/20] updated package --- package.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 1fe4029..929d7f0 100644 --- a/package.json +++ b/package.json @@ -2,9 +2,12 @@ "name": "swaggerize-express", "version": "1.0.0-rc.1", "author": "Trevor Livingston ", - "description": "Spec-first driven swagger express routing.", + "description": "Spec-first REST services with Swagger and Express.", "keywords": [ "swagger", + "swagger-node", + "swagger-express", + "swagger-ui", "express", "node", "node.js", @@ -27,7 +30,6 @@ }, "dependencies": { "swaggerize-builder": "^1.0.0", - "async": "^0.9.0", "caller": "^0.0.1", "core-util-is": "^1.0.1", "debuglog": "^1.0.1", From 8de60ce656ee5ead5a1076a01a8eb21e4982a4b2 Mon Sep 17 00:00:00 2001 From: Trevor Livingston Date: Wed, 27 Aug 2014 11:30:16 -0500 Subject: [PATCH 19/20] Update to accept builder's array of routes instead of object. --- lib/index.js | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/index.js b/lib/index.js index c4066dd..d900f63 100644 --- a/lib/index.js +++ b/lib/index.js @@ -10,19 +10,20 @@ var assert = require('assert'), builder = require('swaggerize-builder'); function swaggerize(options) { - var app, config; + var app; assert.ok(thing.isObject(options), 'Expected options to be an object.'); + assert.ok(thing.isObject(options.api), 'Expected an api definition.'); options.basedir = path.dirname(caller()); - config = builder(options); + options.routes = builder(options); - config.docspath = options.docspath || '/'; + options.docspath = options.docspath || '/'; app = express(); - app.once('mount', mount(config)); + app.once('mount', mount(options)); Object.defineProperty(app, '_api', { enumerable: false, @@ -32,13 +33,13 @@ function swaggerize(options) { Object.defineProperty(app, 'setUrl', { enumerable: true, value: function (value) { - var basePath = url.parse(config.api.basePath); + var basePath = url.parse(options.api.basePath); value = url.parse(value); value.protocol && (basePath.protocol = value.protocol); value.host && (basePath.host = value.host); - config.api.basePath = url.format(basePath); + options.api.basePath = url.format(basePath); } }); From b2a4b5dba030d65f254a5005d5ae6bc826972754 Mon Sep 17 00:00:00 2001 From: Trevor Livingston Date: Wed, 27 Aug 2014 12:43:49 -0500 Subject: [PATCH 20/20] updated readme --- README.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index cc08502..9e00fd1 100644 --- a/README.md +++ b/README.md @@ -86,11 +86,13 @@ handlers |--baz.js ``` -Matches: +Routes as: -- `foo.js : /foo` -- `foo/bar.js : /foo/bar` -- `baz.js : /baz` +``` +foo.js => /foo +foo/bar.js => /foo/bar +baz.js => /baz +``` ### Path Parameters