From 3cf4d531caa587b143e35acafe15009194e4daad Mon Sep 17 00:00:00 2001 From: scott-wyatt Date: Wed, 18 Jul 2018 12:11:58 -0400 Subject: [PATCH] [feat] Route Refine --- README.md | 33 ++++++++++++-- lib/utils.ts | 71 ++++++++++++++++++++++++++++--- package-lock.json | 2 +- package.json | 2 +- test/integration/lib/util.test.js | 25 ++++++++++- 5 files changed, 121 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index ac295ad..a5268b8 100755 --- a/README.md +++ b/README.md @@ -64,15 +64,42 @@ that takes the following form: { // ... '/example/test': { - 'GET': 'ExampleController.test', - config: { - pre: [ 'ExamplePolicy.test' ] + 'GET': { + handler: 'ExampleController.test', + config: { + pre: [ ] + } + } + } + // ... +} +``` + +You can also refine this by explicitly defining the handler and config: + +```js +{ + // ... + '/example/test': { + 'GET': { + handler: 'ExampleController.get', + config: { + pre: [ 'ExamplePolicy.get' ] + } + }, + 'POST': { + handler: 'ExampleController.post', + config: { + pre: [ 'ExamplePolicy.post' ] + } } } // ... } ``` +Which is useful for refining controller over different http methods on a route. +##### Prefixes ```js { // ... diff --git a/lib/utils.ts b/lib/utils.ts index 372e23b..5e3eed7 100755 --- a/lib/utils.ts +++ b/lib/utils.ts @@ -28,19 +28,52 @@ export const Utils = { Utils.getHandlerFromString(app, route) route.config.pre = route.config.pre - .map(pre => this.getHandlerFromPrerequisite(app, pre)) + .map(pre => Utils.getHandlerFromPrerequisite(app, pre)) .filter(handler => !!handler) const routeHandlers = Object.keys(route).filter(value => -1 !== Utils.methods.indexOf(value)) - if (!routeHandlers.some(v => Utils.methods.indexOf(v) >= 0)) { + if (!routeHandlers.some(v => Utils.methods.indexOf(v) >= 0 || !!route[v])) { app.log.error('spool-router: route ', path, ' handler [', routeHandlers.join(', '), ']', 'does not correspond to any defined Controller handler') return {} } + + routeHandlers.forEach(method => { + if (route[method]) { + route[method].config = route[method].config || route.config + route[method].config.pre = route[method].config.pre || route.config.pre + route[method].config.pre = route[method].config.pre + .map(pre => Utils.getHandlerFromPrerequisite(app, pre)) + .filter(handler => !!handler) + } + }) + return { path, route } }, + /** + * Expands the search for the prefix to the route or config.* level + */ + getRouteLevelPrefix(app: FabrixApp, route) { + const configuredPrefix = app.config.get(route.config.prefix) + const routePrefix = route.config.prefix + if (typeof configuredPrefix !== 'undefined') { + if (configuredPrefix) { + return (configuredPrefix).replace(/$\//, '') + } + else { + return + } + } + else { + return (routePrefix || '').replace(/$\//, '') + } + }, + + /** + * Get's the prefix for a path + */ getPrefix (app: FabrixApp, route): string { if (!route || !(route instanceof Object)) { throw new RangeError('Expected a route object') @@ -48,11 +81,12 @@ export const Utils = { const hasPrefix = route.config && route.config.hasOwnProperty('prefix') && route.config.prefix !== false const ignorePrefix = route.config && route.config.hasOwnProperty('prefix') && route.config.prefix === false - const routeLevelPrefix = hasPrefix ? app.config.get(route.config.prefix) || route.config.prefix.replace('$/', '') : null + const routeLevelPrefix = hasPrefix ? Utils.getRouteLevelPrefix(app, route) : null const prefix = (app.config.get('router.prefix') || '').replace(/$\//, '') return `${ ignorePrefix ? '' : routeLevelPrefix || prefix}` }, + /** * Build the Path from the Route config */ @@ -67,16 +101,23 @@ export const Utils = { return `${ prefix }/${ path }` }, + getPolicyFromString(app: FabrixApp, handler) { + return get(app.policies, handler) + }, + /** * Get handler method from a "hapi/hapi-like" prerequisite object/string */ getHandlerFromPrerequisite (app: FabrixApp, pre) { let handler if (pre && typeof pre === 'string') { - handler = get(app.policies, pre) + handler = Utils.getPolicyFromString(app, pre) } else if (pre && typeof pre.method === 'string') { - handler = get(app.policies, pre.method) + handler = Utils.getPolicyFromString(app, pre.method) + } + else if (pre && typeof pre === 'function') { + handler = pre } if (!handler) { @@ -88,6 +129,10 @@ export const Utils = { return handler }, + getControllerFromString(app: FabrixApp, handler) { + return get(app.controllers, handler) + }, + /** * Get handler method from a controller.method string path */ @@ -103,7 +148,15 @@ export const Utils = { Utils.methods.forEach(method => { if (route[method]) { if (typeof route[method] === 'string') { - return route[method] = get(app.controllers, route[method]) + return route[method] = { handler: Utils.getControllerFromString(app, route[method]) } + } + else if (route[method] instanceof Object && route[method].hasOwnProperty('handler')) { + if (typeof route[method].handler === 'string') { + return route[method].handler = Utils.getControllerFromString(app, route[method].handler) + } + else { + return route[method].handler + } } else { return route[method] @@ -112,6 +165,9 @@ export const Utils = { }) }, + /** + * Build a route collection + */ buildRoutes(app: FabrixApp, routes, toReturn = {}) { Object.keys(routes).forEach(r => { const { path, route } = Utils.buildRoute(app, r, routes[r]) @@ -120,6 +176,9 @@ export const Utils = { return Utils.sortRoutes(toReturn, app.config.get('router.sortOrder')) }, + /** + * Sort a route collection by object key + */ sortRoutes(routes, order) { const toReturn = {} const sorted = Object.keys(routes).sort(Utils.createSpecificityComparator({ order: order })) diff --git a/package-lock.json b/package-lock.json index 03c6962..ec88711 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@fabrix/spool-router", - "version": "1.1.0", + "version": "1.1.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index d067bf3..e87abd8 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@fabrix/spool-router", - "version": "1.1.0", + "version": "1.1.1", "description": "Spool - Router for Fabrix", "scripts": { "build": "tsc -p ./lib/tsconfig.release.json", diff --git a/test/integration/lib/util.test.js b/test/integration/lib/util.test.js index 922fec8..bc919b2 100755 --- a/test/integration/lib/util.test.js +++ b/test/integration/lib/util.test.js @@ -16,7 +16,15 @@ describe('lib.Util', () => { '*': 'FooController.bar' }) - assert.equal(route.GET, global.app.controllers.FooController.bar) + assert.equal(route.GET.handler, global.app.controllers.FooController.bar) + }) + it('should resolve the route handler to the correct controller method', () => { + const {path, route} = lib.Utils.buildRoute(global.app, '/foo/bar', { + '*': { + handler: 'FooController.bar' + } + }) + assert.equal(route.GET.handler, global.app.controllers.FooController.bar) }) it('should resolve the prerequisite handler (string) to the correct policy method', () => { const {path, route} = lib.Utils.buildRoute(global.app, '/foo/bar', { @@ -43,6 +51,21 @@ describe('lib.Util', () => { }) assert.equal(route.config.pre[0], global.app.policies.FooPolicy.bar) + assert.equal(route.GET.config.pre[0], global.app.policies.FooPolicy.bar) + }) + it('should resolve the prerequisite handler (string) to the correct policy method', () => { + const {path, route} = lib.Utils.buildRoute(global.app, '/foo/bar', { + 'GET': { + method: 'FooController.bar', + config: { + pre: [ + 'FooPolicy.bar' + ] + } + } + }) + + assert.equal(route.GET.config.pre[0], global.app.policies.FooPolicy.bar) }) })