diff --git a/index.js b/index.js index a1b3530..6ed6eda 100644 --- a/index.js +++ b/index.js @@ -58,9 +58,6 @@ class API { // Init callback this._cb - // Middleware stack - this._middleware = [] - // Error middleware stack this._errors = [] @@ -73,27 +70,31 @@ class API { // Global error status (used for response parsing errors) this._errorStatus = 500 - } // end constructor + // Methods + this._methods = ['get','post','put','patch','delete','options','head','any'] + // Convenience methods for METHOD + this._methods.forEach(m => { + this[m] = (...a) => this.METHOD(m.toUpperCase(),...a) + }) + } // end constructor - // Convenience methods (path, handler) - get(p,h) { this.METHOD('GET',p,h) } - post(p,h) { this.METHOD('POST',p,h) } - put(p,h) { this.METHOD('PUT',p,h) } - patch(p,h) { this.METHOD('PATCH',p,h) } - delete(p,h) { this.METHOD('DELETE',p,h) } - options(p,h) { this.METHOD('OPTIONS',p,h) } - head(p,h) { this.METHOD('HEAD',p,h) } - any(p,h) { this.METHOD('ANY',p,h) } + // METHOD: Adds method, middleware, and handlers to routes + METHOD(method,...args) { + // Extract path if provided, otherwise default to global wildcard + let path = typeof args[0] === 'string' ? args.shift() : '/*' - // METHOD: Adds method and handler to routes - METHOD(method, path, handler) { + // Extract the execution stack + let stack = args.map((fn,i) => { + if (typeof fn === 'function' && (fn.length === 3 || (fn.length === 2 && i === args.length-1))) + return fn + throw new ConfigurationError('Route-based middleware must have 3 parameters') + }) - if (typeof handler !== 'function') { - throw new ConfigurationError(`No route handler specified for ${method} method on ${path} route.`) - } + if (stack.length === 0) + throw new ConfigurationError(`No handler or middleware specified for ${method} method on ${path} route.`) // Ensure method is an array let methods = Array.isArray(method) ? method : method.split(',') @@ -105,46 +106,100 @@ class API { let route = this._prefix.concat(parsedPath) // For root path support - if (route.length === 0) { route.push('')} + if (route.length === 0) { route.push('') } // Keep track of path variables let pathVars = {} + // Make a local copy of routes + let routes = this._routes + + // Create a local stack for inheritance + let _stack = {} + // Loop through the paths for (let i=0; i { if (typeof _method === 'string') { + + if (routes['ROUTES']) { + + // Wildcard routes + if (routes['ROUTES']['*']) { + + // Inherit middleware + if (routes['ROUTES']['*']['MIDDLEWARE']) { + _stack[method] = _stack[method] ? + _stack[method].concat(routes['ROUTES']['*']['MIDDLEWARE'].stack) + : routes['ROUTES']['*']['MIDDLEWARE'].stack + } + + // Inherit methods and ANY + if (routes['ROUTES']['*']['METHODS'] && routes['ROUTES']['*']['METHODS']) { + ['ANY',method].forEach(m => { + if (routes['ROUTES']['*']['METHODS'][m]) { + _stack[method] = _stack[method] ? + _stack[method].concat(routes['ROUTES']['*']['METHODS'][m].stack) + : routes['ROUTES']['*']['METHODS'][m].stack + } + }) // end for + } + } + + // Matching routes + if (routes['ROUTES'][route[i]]) { + + // Inherit middleware + if (end && routes['ROUTES'][route[i]]['MIDDLEWARE']) { + _stack[method] = _stack[method] ? + _stack[method].concat(routes['ROUTES'][route[i]]['MIDDLEWARE'].stack) + : routes['ROUTES'][route[i]]['MIDDLEWARE'].stack + } + + // Inherit ANY methods (DISABLED) + // if (end && routes['ROUTES'][route[i]]['METHODS'] && routes['ROUTES'][route[i]]['METHODS']['ANY']) { + // _stack[method] = _stack[method] ? + // _stack[method].concat(routes['ROUTES'][route[i]]['METHODS']['ANY'].stack) + // : routes['ROUTES'][route[i]]['METHODS']['ANY'].stack + // } + } + } + // Add the route to the global _routes this.setRoute( this._routes, - (i === route.length-1 ? { - ['__'+_method.trim().toUpperCase()]: { - vars: pathVars, - handler: handler, - route: '/'+parsedPath.join('/'), - path: '/'+this._prefix.concat(parsedPath).join('/') } - } : {}), + _method.trim().toUpperCase(), + (end ? { + vars: pathVars, + stack, + inherited: _stack[method] ? _stack[method] : [], + route: '/'+parsedPath.join('/'), + path: '/'+this._prefix.concat(parsedPath).join('/') + } : null), route.slice(0,i+1) ) + } }) // end methods loop + routes = routes['ROUTES'][route[i]] } // end for loop } // end main METHOD function - - // RUN: This runs the routes async run(event,context,cb) { @@ -162,44 +217,24 @@ class API { // Parse the request await request.parseRequest() - // Loop through the middleware and await response - for (const mw of this._middleware) { - // Only run middleware if in processing state + // Loop through the execution stack + for (const fn of request._stack) { + // Only run if in processing state if (response._state !== 'processing') break - // Init for matching routes - let matched = false - - // Test paths if they are supplied - for (const path of mw[0]) { - if ( - path === request.path || // If exact path match - path === request.route || // If exact route match - // If a wildcard match - (path.substr(-1) === '*' && new RegExp('^' + path.slice(0, -1) + '.*$').test(request.route)) - ) { - matched = true - break - } - } - - if (mw[0].length > 0 && !matched) continue - - // Promisify middleware await new Promise(async r => { - let rtn = await mw[1](request,response,() => { r() }) - if (rtn) response.send(rtn) - if (response._state === 'done') r() // if state is done, resolve promise + try { + let rtn = await fn(request,response,() => { r() }) + if (rtn) response.send(rtn) + if (response._state === 'done') r() // if state is done, resolve promise + } catch(e) { + await this.catchErrors(e,response) + r() // resolve the promise + } }) } // end for - // Execute the primary handler if in processing state - if (response._state === 'processing') { - let rtn = await request._handler(request,response) - if (rtn) response.send(rtn) - } - } catch(e) { await this.catchErrors(e,response) } @@ -214,8 +249,6 @@ class API { // Catch all async/sync errors async catchErrors(e,response,code,detail) { - // console.log('\n\n------------------------\n',e,'\n------------------------\n\n'); - // Error messages should never be base64 encoded response._isBase64 = false @@ -297,24 +330,34 @@ class API { // Middleware handler - use(path) { + use(...args) { // Extract routes - let routes = typeof path === 'string' ? Array.of(path) : (Array.isArray(path) ? path : []) + let routes = typeof args[0] === 'string' ? Array.of(args.shift()) : (Array.isArray(args[0]) ? args.shift() : ['/*']) + + // Init middleware stack + let middleware = [] // Add func args as middleware - for (let arg in arguments) { - if (typeof arguments[arg] === 'function') { - if (arguments[arg].length === 3) { - this._middleware.push([routes,arguments[arg]]) - } else if (arguments[arg].length === 4) { - this._errors.push(arguments[arg]) + for (let arg in args) { + if (typeof args[arg] === 'function') { + if (args[arg].length === 3) { + middleware.push(args[arg]) + } else if (args[arg].length === 4) { + this._errors.push(args[arg]) } else { throw new ConfigurationError('Middleware must have 3 or 4 parameters') } } } + // Add middleware to path + if (middleware.length > 0) { + routes.forEach(route => { + this.METHOD('__MW__',route,...middleware) + }) + } + } // end use @@ -333,28 +376,58 @@ class API { return path.trim().replace(/^\/(.*?)(\/)*$/,'$1').split('/').filter(x => x.trim() !== '') } - // Recursive function to create routes object - setRoute(obj, value, path) { - if (typeof path === 'string') { - path = path.split('.') - } - - if (path.length > 1){ + // Recursive function to create/merge routes object + setRoute(obj, method, value, path) { + if (path.length > 1) { let p = path.shift() - if (obj[p] === null) { - obj[p] = {} - } - this.setRoute(obj[p], value, path) + if (p === '*') { throw new ConfigurationError('Wildcards can only be at the end of a route definition.') } + this.setRoute(obj['ROUTES'][p], method, value, path) } else { - if (obj[path[0]] === null) { - obj[path[0]] = value - } else { - obj[path[0]] = Object.assign(value,obj[path[0]]) + // Create routes and add path if they don't exist + if (!obj['ROUTES']) obj['ROUTES'] = {} + if (!obj['ROUTES'][path[0]]) obj['ROUTES'][path[0]] = {} + + // If a value exists in this iteration + if (value !== null) { + + // TEMP: debug + // value._STACK = value.stack.map(x => x.name) + // value._STACK2 = value.inherited.map(x => x.name) + + // If mounting middleware + if (method === '__MW__') { + // Merge stacks if middleware exists + if (obj['ROUTES'][path[0]]['MIDDLEWARE']) { + value.stack = obj['ROUTES'][path[0]]['MIDDLEWARE'].stack.concat(value.stack) + value.vars = UTILS.mergeObjects(obj['ROUTES'][path[0]]['MIDDLEWARE'].vars,value.vars) + } + + // Add/Update the middleware + obj['ROUTES'][path[0]]['MIDDLEWARE'] = value + + // Else if mounting a regular route + } else { + + // Create the methods section if it doesn't exist + if (!obj['ROUTES'][path[0]]['METHODS']) obj['ROUTES'][path[0]]['METHODS'] = {} + + // Merge stacks if method exists + if (obj['ROUTES'][path[0]]['METHODS'][method]) { + value.stack = obj['ROUTES'][path[0]]['METHODS'][method].stack.concat(value.stack) + value.vars = UTILS.mergeObjects(obj['ROUTES'][path[0]]['METHODS'][method].vars,value.vars) + } + + // Add/Update the method + obj['ROUTES'][path[0]]['METHODS'] = Object.assign( + {},obj['ROUTES'][path[0]]['METHODS'],{ [method]: value } + ) + + } } + } } // end setRoute - // Load app packages app(packages) { diff --git a/lib/request.js b/lib/request.js index deaf5ca..d610284 100644 --- a/lib/request.js +++ b/lib/request.js @@ -31,6 +31,9 @@ class REQUEST { // Init the handler this._handler + // Init the execution stack + this._stack + // Expose Namespaces this.namespace = this.ns = app._app @@ -183,16 +186,17 @@ class REQUEST { for (let i=0; i this.params[y] = path[x]) } // end for // Set the route used this.route = route.route - // Set the handler - this._handler = route.handler + // Set the execution stack + this._stack = route.inherited.concat(route.stack) } else { this.app._errorStatus = 405 @@ -250,7 +255,7 @@ class REQUEST { // Recusive wildcard function validWildcard(wc) { - return Object.keys(wc[wc.length-1]).length > 1 + return Object.keys(wc[wc.length-1]['METHODS']).length > 1 || (wc.length > 1 && this.validWildcard(wc.slice(0,-1))) } diff --git a/lib/utils.js b/lib/utils.js index a12dc7d..744a63f 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -104,12 +104,13 @@ exports.statusLookup = status => { // Parses routes into readable array const extractRoutes = (routes,table=[]) => { - for (let route in routes) { - if (/^__[A-Z]+$/.test(route)) { - table.push([route.replace(/^__/,''),routes[route].path]) - } else { - extractRoutes(routes[route],table) + // Loop through all routes + for (let route in routes['ROUTES']) { + // Add methods + for (let method in routes['ROUTES'][route]['METHODS']) { + table.push([method,routes['ROUTES'][route]['METHODS'][method].path]) } + extractRoutes(routes['ROUTES'][route],table) } return table } @@ -141,6 +142,15 @@ exports.deepMerge = (a,b) => { return a } +exports.mergeObjects = (obj1,obj2) => + Object.keys(Object.assign({},obj1,obj2)).reduce((acc,key) => { + if (obj1[key] !== obj2[key]) { + return Object.assign(acc,{ [key]: obj1[key] ? obj1[key].concat(obj2[key]) : obj2[key] }) + } else { + return Object.assign(acc,{ [key]: obj1[key] ? obj1[key] : obj2[key] }) + } + },{}) + // Concats values from an array to ',' separated string exports.fromArray = val => val && val instanceof Array ? val.toString() : undefined diff --git a/test/middleware.js b/test/middleware.js index 58424df..e91c0e4 100644 --- a/test/middleware.js +++ b/test/middleware.js @@ -11,6 +11,7 @@ const api4 = require('../index')({ version: 'v1.0' }) const api5 = require('../index')({ version: 'v1.0' }) const api6 = require('../index')({ version: 'v1.0' }) const api7 = require('../index')({ version: 'v1.0' }) +const api8 = require('../index')({ version: 'v1.0' }) let event = { httpMethod: 'get', @@ -57,32 +58,35 @@ api.use(function(req,res,next) { }); -api2.use('/test',function(req,res,next) { +api2.use('/test',function testMiddlware(req,res,next) { req.testMiddleware = true next() }) -api2.use('/test/*',function(req,res,next) { +api2.use('/test/*',function testMiddlewareWildcard(req,res,next) { req.testMiddlewareWildcard = true next() }) -api2.use('/test/:param1',function(req,res,next) { +api2.use('/test/:param1',function testMiddlewareParam(req,res,next) { req.testMiddlewareParam = true next() }) -api2.use('/test/testing',function(req,res,next) { - req.testMiddlewarePath = true - next() -}) +// This test is deprecated +// api2.use('/test/testing',function testMiddlewarePath(req,res,next) { +// req.testMiddlewarePath = true +// next() +// }) -api2.use('/test/error',function(req,res,next) { +api2.use('/test/error',function testMiddlwareError(req,res,next) { + // console.log('API2 ERROR MIDDLEWARE'); res.error(401,'Not Authorized') }) -api3.use(['/test','/test/:param1','/test2/*'],function(req,res,next) { +api3.use(['/test','/test/:param1','/test2/*'],function arrayBasedMiddleware(req,res,next) { + // console.log('API3 MIDDLEWARE:',req.path); req.testMiddlewareAll = true next() }) @@ -128,6 +132,7 @@ api7.use((req,res,next) => { next() }) + /******************************************************************************/ /*** DEFINE TEST ROUTES ***/ /******************************************************************************/ @@ -153,7 +158,7 @@ api2.get('/test2/:test', function(req,res) { res.status(200).json({ method: 'get', middleware: req.testMiddleware ? true : false, middlewareWildcard: req.testMiddlewareWildcard ? true : false, middlewareParam: req.testMiddlewareParam ? true : false, middlewarePath: req.testMiddlewarePath ? true : false }) }) -api2.get('/test/xyz', function(req,res) { +api2.get('/test/xyz', function testXYZ(req,res) { res.status(200).json({ method: 'get', middleware: req.testMiddleware ? true : false, middlewareWildcard: req.testMiddlewareWildcard ? true : false, middlewareParam: req.testMiddlewareParam ? true : false, middlewarePath: req.testMiddlewarePath ? true : false }) }) @@ -224,6 +229,23 @@ api7.get('/test', (req,res) => { res.status(200).send('route response') }) +api8.get('/test/one', middleware1, (req,res) => { + res.status(200).json({ + method: 'get', + middleware1: req.middleware1 ? true : false, + middleware2: req.middleware2 ? true : false + }) +}) + +api8.get('/test/two', middleware1, middleware2, (req,res) => { + res.status(200).json({ + method: 'get', + middleware1: req.middleware1 ? true : false, + middleware2: req.middleware2 ? true : false + }) +}) + + /******************************************************************************/ /*** BEGIN TESTS ***/ /******************************************************************************/ @@ -270,14 +292,20 @@ describe('Middleware Tests:', function() { expect(result).to.deep.equal({ multiValueHeaders: { 'content-type': ['application/json'] }, statusCode: 200, body: '{"method":"get","middleware":false,"middlewareWildcard":true,"middlewareParam":false,"middlewarePath":false}', isBase64Encoded: false }) }) // end it + // it('Parameter match', async function() { + // let _event = Object.assign({},event,{ path: '/test/testing' }) + // let result = await new Promise(r => api2.run(_event,{},(e,res) => { r(res) })) + // console.log(JSON.stringify(api2._routes,null,2)); + // expect(result).to.deep.equal({ multiValueHeaders: { 'content-type': ['application/json'] }, statusCode: 200, body: '{"method":"get","middleware":false,"middlewareWildcard":true,"middlewareParam":true,"middlewarePath":true}', isBase64Encoded: false }) + // }) // end it + it('Parameter match', async function() { let _event = Object.assign({},event,{ path: '/test/testing' }) let result = await new Promise(r => api2.run(_event,{},(e,res) => { r(res) })) - expect(result).to.deep.equal({ multiValueHeaders: { 'content-type': ['application/json'] }, statusCode: 200, body: '{"method":"get","middleware":false,"middlewareWildcard":true,"middlewareParam":true,"middlewarePath":true}', isBase64Encoded: false }) + expect(result).to.deep.equal({ multiValueHeaders: { 'content-type': ['application/json'] }, statusCode: 200, body: '{"method":"get","middleware":false,"middlewareWildcard":true,"middlewareParam":true,"middlewarePath":false}', isBase64Encoded: false }) }) // end it - it('Matching path (array)', async function() { let _event = Object.assign({},event,{ path: '/test' }) let result = await new Promise(r => api3.run(_event,{},(e,res) => { r(res) })) @@ -358,4 +386,32 @@ describe('Middleware Tests:', function() { }) // end it + it('Route-based middleware (single)', async function() { + let _event = Object.assign({},event,{ path: '/test/one' }) + let result = await new Promise(r => api8.run(_event,{},(e,res) => { r(res) })) + expect(result).to.deep.equal({ + multiValueHeaders: { 'content-type': ['application/json'] }, + statusCode: 200, + body: '{"method":"get","middleware1":true,"middleware2":false}', + isBase64Encoded: false }) + }) // end it + + it('Route-based middleware (two middlewares)', async function() { + let _event = Object.assign({},event,{ path: '/test/two' }) + let result = await new Promise(r => api8.run(_event,{},(e,res) => { r(res) })) + expect(result).to.deep.equal({ + multiValueHeaders: { 'content-type': ['application/json'] }, + statusCode: 200, + body: '{"method":"get","middleware1":true,"middleware2":true}', + isBase64Encoded: false }) + }) // end it + + + // api8.get('/test/error', (req,res) => {}, (req,res) => { + // res.status(200).json({ + // method: 'get' + // }) + // }) + + }) // end MIDDLEWARE tests diff --git a/test/routes.js b/test/routes.js index 8f078b4..8e24dbc 100644 --- a/test/routes.js +++ b/test/routes.js @@ -65,58 +65,51 @@ api.options('/test', function(req,res) { res.status(200).json({ method: 'options', status: 'ok' }) }) +api.get('/test/:testX', function(req,res,next) { + next() +}) + api.get('/test/:test', function(req,res) { - // console.log(req) - res.status(200).json({ method: 'get', status: 'ok', param: req.params.test }) + res.status(200).json({ method: 'get', status: 'ok', param: req.params.test, param2: req.params.testX }) }) api.post('/test/:test', function(req,res) { - // console.log(req) res.status(200).json({ method: 'post', status: 'ok', param: req.params.test }) }) api.put('/test/:test', function(req,res) { - // console.log(req) res.status(200).json({ method: 'put', status: 'ok', param: req.params.test }) }) api.patch('/test/:test', function(req,res) { - // console.log(req) res.status(200).json({ method: 'patch', status: 'ok', param: req.params.test }) }) api.delete('/test/:test', function(req,res) { - // console.log(req) res.status(200).json({ method: 'delete', status: 'ok', param: req.params.test }) }) api.options('/test/:test', function(req,res) { - // console.log(req) res.status(200).json({ method: 'options', status: 'ok', param: req.params.test }) }) api.patch('/test/:test/:test2', function(req,res) { - // console.log(req) res.status(200).json({ method: 'patch', status: 'ok', params: req.params }) }) api.delete('/test/:test/:test2', function(req,res) { - // console.log(req) res.status(200).json({ method: 'delete', status: 'ok', params: req.params }) }) api.get('/test/:test/query', function(req,res) { - // console.log(req) res.status(200).json({ method: 'get', status: 'ok', param: req.params.test, query: req.query, multiValueQuery: req.multiValueQuery }) }) api.post('/test/:test/query', function(req,res) { - // console.log(req) res.status(200).json({ method: 'post', status: 'ok', param: req.params.test, query: req.query, multiValueQuery: req.multiValueQuery }) }) api.put('/test/:test/query', function(req,res) { - // console.log(req) res.status(200).json({ method: 'put', status: 'ok', param: req.params.test, query: req.query, multiValueQuery: req.multiValueQuery }) }) @@ -164,16 +157,16 @@ api.METHOD('TEST','/test_options2/:param1/test', function(req,res) { res.status(200).json({ method: 'test', status: 'ok', body: req.body }) }) -api.options('/*', function(req,res) { - res.status(200).json({ method: 'options', status: 'ok', path: '/*'}) +api.options('/test_options2/:param1/*', function(req,res) { + res.status(200).json({ method: 'options', status: 'ok', path: '/test_options2/:param1/*', params:req.params}) }) api.options('/test_options2/*', function(req,res) { res.status(200).json({ method: 'options', status: 'ok', path: '/test_options2/*'}) }) -api.options('/test_options2/:param1/*', function(req,res) { - res.status(200).json({ method: 'options', status: 'ok', path: '/test_options2/:param1/*', params:req.params}) +api.options('/*', function(req,res) { + res.status(200).json({ method: 'options', status: 'ok', path: '/*'}) }) api.get('/override/head/request', (req,res) => { @@ -188,11 +181,11 @@ api.any('/any', (req,res) => { res.status(200).json({ method: req.method, path: '/any', anyRoute: true }) }) -api.any('/any2', (req,res) => { +api.any('/any2', function any2(req,res) { res.status(200).json({ method: req.method, path: '/any2', anyRoute: true }) }) -api.post('/any2', (req,res) => { +api.post('/any2', function any2post(req,res) { res.status(200).json({ method: req.method, path: '/any2', anyRoute: false }) }) @@ -224,11 +217,15 @@ api3.METHOD([1,'DELETE'],'/multimethod/badtype', (req,res) => { }) +api4.get('/test/*', (req,res) => { + res.status(200).header('wildcard',true).json({ method: req.method, path: req.path, nested: "true" }) +}) + api4.get('/*', (req,res) => { res.status(200).header('wildcard',true).json({ method: req.method, path: req.path }) }) -api4.get('/test/*', (req,res) => { +api4.options('/test/test/*', (req,res) => { res.status(200).header('wildcard',true).json({ method: req.method, path: req.path, nested: "true" }) }) @@ -236,7 +233,7 @@ api4.options('/test/*', (req,res) => { res.status(200).header('wildcard',true).json({ method: req.method, path: req.path, nested: "true" }) }) -api4.options('/test/test/*', (req,res) => { +api4.post('/test/*', (req,res) => { res.status(200).header('wildcard',true).json({ method: req.method, path: req.path, nested: "true" }) }) @@ -244,11 +241,6 @@ api4.post('/*', (req,res) => { res.status(200).header('wildcard',true).json({ method: req.method, path: req.path }) }) -api4.post('/test/*', (req,res) => { - res.status(200).header('wildcard',true).json({ method: req.method, path: req.path, nested: "true" }) -}) - - /******************************************************************************/ /*** BEGIN TESTS ***/ /******************************************************************************/ @@ -306,7 +298,7 @@ describe('Route Tests:', function() { it('Path with parameter: /test/123', async function() { let _event = Object.assign({},event,{ path: '/test/123' }) let result = await new Promise(r => api.run(_event,{},(e,res) => { r(res) })) - expect(result).to.deep.equal({ multiValueHeaders: { 'content-type': ['application/json'] }, statusCode: 200, body: '{"method":"get","status":"ok","param":"123"}', isBase64Encoded: false }) + expect(result).to.deep.equal({ multiValueHeaders: { 'content-type': ['application/json'] }, statusCode: 200, body: '{"method":"get","status":"ok","param":"123","param2":"123"}', isBase64Encoded: false }) }) // end it it('Path with parameter and querystring: /test/123/query/?test=321', async function() { @@ -356,6 +348,7 @@ describe('Route Tests:', function() { it('Wildcard: /*', async function() { let _event = Object.assign({},event,{ path: '/foo/bar' }) let result = await new Promise(r => api4.run(_event,{},(e,res) => { r(res) })) + // console.log(JSON.stringify(api4._routes,null,2)); expect(result).to.deep.equal({ multiValueHeaders: { 'content-type': ['application/json'], 'wildcard': [true] }, statusCode: 200, @@ -964,11 +957,11 @@ describe('Route Tests:', function() { it('Expected routes', function() { expect(api3.routes()).to.deep.equal([ - [ 'POST', '/multimethod/test' ], [ 'GET', '/multimethod/test' ], - [ 'DELETE', '/multimethod/:var' ], - [ 'PUT', '/multimethod/:var' ], + [ 'POST', '/multimethod/test' ], [ 'GET', '/multimethod/:var' ], + [ 'PUT', '/multimethod/:var' ], + [ 'DELETE', '/multimethod/:var' ], [ 'DELETE', '/multimethod/badtype' ] ]) }) // end it @@ -988,7 +981,7 @@ describe('Route Tests:', function() { error = e } expect(error.name).to.equal('ConfigurationError') - expect(error.message).to.equal('No route handler specified for GET method on /test-missing-handler route.') + expect(error.message).to.equal('No handler or middleware specified for GET method on /test-missing-handler route.') }) // end it // TODO: ??? @@ -1018,6 +1011,19 @@ describe('Route Tests:', function() { expect(error.message).to.equal('Middleware must have 3 or 4 parameters') }) // end it + it('Invalid route-based middleware', async function() { + let error + try { + const api_error2 = require('../index')({ version: 'v1.0' }) + api_error2.get('/',() => {},(res,req) => {},(res,req) => {}) + } catch(e) { + // console.log(e); + error = e + } + expect(error.name).to.equal('ConfigurationError') + expect(error.message).to.equal('Route-based middleware must have 3 parameters') + }) // end it + }) // end Configuration errors describe('routes() (debug method)', function() { diff --git a/test/unit.js b/test/unit.js index 52173a9..83a9413 100644 --- a/test/unit.js +++ b/test/unit.js @@ -25,25 +25,40 @@ let event = { describe('Unit Tests:', function() { - it('setRoute - string path', async function() { + it('setRoute', async function() { let routes = {} - api.setRoute(routes,{['_GET']: { route: '/testPath' } },['testPath']) - api.setRoute(routes,{['_GET']: { route: '/testPath/testx' } },'testPath.testx') - expect(routes).to.deep.equal({ testPath: { _GET: { route: '/testPath' }, testx: { _GET: { route: '/testPath/testx' } } } }) + api.setRoute(routes,'GET', { route: '/testPath' }, ['testPath']) + api.setRoute(routes,'GET', { route: '/testPath/testx' }, ['testPath','testx']) + expect(routes).to.deep.equal({ + ROUTES: { + testPath: { + METHODS: { + GET: { route: '/testPath' } + }, + ROUTES: { + testx: { + METHODS: { + GET: { route: '/testPath/testx' } + } + } + } + } + } + }) }) // end it - it('setRoute - null path', async function() { - let routes = { testPath: null } - api.setRoute(routes,{['_GET']: { route: '/testPath/testx' } },'testPath.testx') - expect(routes).to.deep.equal({ testPath: { testx: { _GET: { route: '/testPath/testx' } } } }) - }) // end it - - it('setRoute - null single path', async function() { - let routes = { testPath: null } - api.setRoute(routes,{['_GET']: { route: '/testPath' } },['testPath']) - expect(routes).to.deep.equal({ testPath: { _GET: { route: '/testPath' } } }) - }) // end it + // it('setRoute - null path', async function() { + // let routes = { testPath: null } + // api.setRoute(routes,{['_GET']: { route: '/testPath/testx' } },'testPath.testx') + // expect(routes).to.deep.equal({ testPath: { testx: { _GET: { route: '/testPath/testx' } } } }) + // }) // end it + // + // it('setRoute - null single path', async function() { + // let routes = { testPath: null } + // api.setRoute(routes,{['_GET']: { route: '/testPath' } },['testPath']) + // expect(routes).to.deep.equal({ testPath: { _GET: { route: '/testPath' } } }) + // }) // end it }) // end UNIT tests