diff --git a/README.md b/README.md index 28d80bd..f9b2e62 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ Doing so you will get a complete overview of your application and you can: - optimize your code - optimize your application structure -- find out the application structure (expecially if you have joined a new team) +- find out the application structure (especially if you have joined a new team) - automate some documentation tasks This plugin is intended to be run only for _development_ purposes. @@ -176,6 +176,11 @@ app.register(require('fastify-overview'), { exposeRouteOptions: { method: 'POST', // default: 'GET' url: '/customUrl', // default: '/json-overview' + }, + onRouteDefinition: (opts) => { + return { + schema: opts.schema + } } }) @@ -294,6 +299,25 @@ By default the route is exposed at `GET /json-overview`. You can customize the route's options when `exposeRoute` is set to `true`. You can provide all the [fastify route's options](https://www.fastify.io/docs/latest/Reference/Routes/#routes-options) except the `handler`. +### onRouteDefinition + +This option can be used to determine which properties of the route options are additional included in the overview. +The function receives the [RouteOptions](https://github.com/fastify/fastify/blob/62f564d965949bc123184a27a610f214f23e9a49/types/hooks.d.ts#L695) +object as the only parameter and must return an object with the desired properties. You can also overwrite the properties +that are included in the route overview by default (namely `url`, `method`, `prefix` and `hooks`). You cannot +override the `source` property. +```js + onRouteDefinition: (routeOptions) => { + return { + method: routeOptions.method, + url: routeOptions.url.length, + prefix: routeOptions.prefix, + schema: routeOptions.schema + } + } +``` +In this example, the `url` property is overridden and the `url` length is returned instead of the `url`. + ## License Copyright [Manuel Spigolon](https://github.com/Eomm), Licensed under [MIT](./LICENSE). diff --git a/index.d.ts b/index.d.ts index 30d9f41..c85cb1d 100644 --- a/index.d.ts +++ b/index.d.ts @@ -50,18 +50,18 @@ interface RouteItem { source?: OverviewStructureSource, } -export interface OverviewStructure { +export interface OverviewStructure { id: Number, name: string, source?: OverviewStructureSource, - children?: OverviewStructure[], + children?: OverviewStructure[], decorators?: { decorate: OverviewStructureDecorator[], decorateRequest: OverviewStructureDecorator[], decorateReply: OverviewStructureDecorator[] }, hooks?: OverviewStructureHooks, - routes?: RouteItem[] + routes?: (Omit & T)[] } export interface FastifyOverviewOptions { @@ -82,6 +82,11 @@ export interface FastifyOverviewOptions { * Customize the route's options when `exposeRoute` is set to `true` */ exposeRouteOptions?: Partial, + + /** + * Customise which properties of the route options will be included in the overview + */ + onRouteDefinition?: (routeOptions: RouteOptions & { routePath: string; path: string; prefix: string }) => Record } export interface FastifyOverviewDecoratorOptions { @@ -98,7 +103,7 @@ export interface FastifyOverviewDecoratorOptions { declare module 'fastify' { export interface FastifyInstance { - overview: (opts?: FastifyOverviewDecoratorOptions) => OverviewStructure; + overview: (opts?: FastifyOverviewDecoratorOptions) => OverviewStructure; } } diff --git a/index.js b/index.js index 06f344f..536aab1 100644 --- a/index.js +++ b/index.js @@ -31,7 +31,7 @@ function fastifyOverview (fastify, options, next) { }) fastify.addHook('onRoute', function markRoute (routeOpts) { - const routeNode = transformRoute(routeOpts) + const routeNode = Object.assign(transformRoute(routeOpts), opts.onRouteDefinition?.(routeOpts)) if (opts.addSource) { routeNode.source = routeOpts.handler[kSourceRoute] diff --git a/lib/utils.js b/lib/utils.js index ca14121..b42473b 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -23,7 +23,6 @@ function getFunctionName (func) { function transformRoute (routeOpts) { const hooks = getEmptyHookRoute() - for (const hook of Object.keys(hooks)) { if (routeOpts[hook]) { if (Array.isArray(routeOpts[hook])) { diff --git a/test/fixture/routes.04.json b/test/fixture/routes.04.json new file mode 100644 index 0000000..f5b0e09 --- /dev/null +++ b/test/fixture/routes.04.json @@ -0,0 +1,42 @@ +[ + { + "method": "GET", + "url":"/get", + "prefix":"", + "bodySchema": null, + "hooks":{ + "onRequest":[ ], + "preParsing":[ ], + "preValidation":[ ], + "preHandler":[ ], + "preSerialization":[ ], + "onError":[ ], + "onSend":[ ], + "onResponse":[ ], + "onTimeout":[ ], + "onRequestAbort":[ ] + } + }, + { + "method": "POST", + "url":"/post", + "prefix":"", + "bodySchema": { + "test": { + "type": "string" + } + }, + "hooks":{ + "onRequest":[ ], + "preParsing":[ ], + "preValidation":[ ], + "preHandler":[ ], + "preSerialization":[ ], + "onError":[ ], + "onSend":[ ], + "onResponse":[ ], + "onTimeout":[ ], + "onRequestAbort":[ ] + } + } +] diff --git a/test/fixture/routes.05.json b/test/fixture/routes.05.json new file mode 100644 index 0000000..bb3e4af --- /dev/null +++ b/test/fixture/routes.05.json @@ -0,0 +1,20 @@ +[ + { + "method": "PUT", + "url":"/api/plugin", + "prefix":"/api", + "bodySchema": null, + "hooks":{ + "onRequest":[ ], + "preParsing":[ ], + "preValidation":[ ], + "preHandler":[ ], + "preSerialization":[ ], + "onError":[ ], + "onSend":[ ], + "onResponse":[ ], + "onTimeout":[ ], + "onRequestAbort":[ ] + } + } +] diff --git a/test/fixture/routes.06.json b/test/fixture/routes.06.json new file mode 100644 index 0000000..ecb234c --- /dev/null +++ b/test/fixture/routes.06.json @@ -0,0 +1,24 @@ +[ + { + "method": "PATCH", + "url":"/api/patch/:param", + "prefix":"/api", + "bodySchema": { + "text": { + "type": "boolean" + } + }, + "hooks":{ + "onRequest":[ ], + "preParsing":[ ], + "preValidation":[ ], + "preHandler":[ ], + "preSerialization":[ ], + "onError":[ ], + "onSend":[ ], + "onResponse":[ ], + "onTimeout":[ ], + "onRequestAbort":[ ] + } + } +] diff --git a/test/fixture/routes.07.json b/test/fixture/routes.07.json new file mode 100644 index 0000000..8f67a3c --- /dev/null +++ b/test/fixture/routes.07.json @@ -0,0 +1,36 @@ +[ + { + "method": "GETGET", + "url":"static url", + "prefix":"", + "hooks":{ + "onRequest":[ ], + "preParsing":[ ], + "preValidation":[ ], + "preHandler":[ ], + "preSerialization":[ ], + "onError":[ ], + "onSend":[ ], + "onResponse":[ ], + "onTimeout":[ ], + "onRequestAbort":[ ] + } + }, + { + "method": "POSTPOST", + "url":"static url", + "prefix":"", + "hooks":{ + "onRequest":[ ], + "preParsing":[ ], + "preValidation":[ ], + "preHandler":[ ], + "preSerialization":[ ], + "onError":[ ], + "onSend":[ ], + "onResponse":[ ], + "onTimeout":[ ], + "onRequestAbort":[ ] + } + } +] diff --git a/test/routes.test.js b/test/routes.test.js index 325d44c..3d7b65c 100644 --- a/test/routes.test.js +++ b/test/routes.test.js @@ -93,3 +93,79 @@ test('routes', async t => { t.same(reg3.routes.length, 9) t.same(reg3.routes, require('./fixture/routes.03.json')) }) + +test('custom onRouteDefinition', async t => { + const app = fastify({ exposeHeadRoutes: false }) + + await app.register(plugin, { + onRouteDefinition: (opts) => { + return { + bodySchema: opts.schema ? opts.schema.body : undefined + } + } + }) + + app.get('/get', noop) + app.post('/post', { schema: { body: { test: { type: 'string' } } } }, noop) + + app.register(async (instance) => { + instance.put('/plugin', { schema: { querystring: { size: { type: 'integer' } } } }, noop) + + instance.register(async function (instance2) { + instance2.patch('/patch/:param', { + schema: { + params: { + param: { + type: 'string' + } + }, + body: { + text: { + type: 'boolean' + } + } + } + }, noop) + }) + }, { prefix: 'api' }) + + await app.ready() + + const root = app.overview() + + t.equal(root.children.length, 1) + t.equal(root.routes.length, 2) + t.same(root.routes, require('./fixture/routes.04.json')) + + t.equal(root.children[0].routes.length, 1) + t.equal(root.children[0].children.length, 1) + t.same(root.children[0].routes, require('./fixture/routes.05.json')) + + t.equal(root.children[0].children[0].routes.length, 1) + t.equal(root.children[0].children[0].children.length, 0) + t.same(root.children[0].children[0].routes, require('./fixture/routes.06.json')) +}) + +test('custom onRouteDefinition with overriding', async t => { + const app = fastify({ exposeHeadRoutes: false }) + + await app.register(plugin, { + onRouteDefinition: (opts) => { + return { + url: 'static url', + method: opts.method + opts.method + } + } + }) + + app.get('/get', noop) + app.post('/post', { schema: { body: { test: { type: 'string' } } } }, noop) + + await app.ready() + + const root = app.overview() + + t.equal(root.children.length, 0) + t.equal(root.routes.length, 2) + t.same(root.routes, require('./fixture/routes.07.json')) +}) diff --git a/test/types/index.test-d.ts b/test/types/index.test-d.ts index ad1afc9..cb46042 100644 --- a/test/types/index.test-d.ts +++ b/test/types/index.test-d.ts @@ -18,3 +18,33 @@ app expectType(data) }) .ready() + +app + .register(fastifyOverview, { + onRouteDefinition: (routeOptions) => { + return { + bodySchema: routeOptions.schema?.body, + headerNames: Object.keys(routeOptions.schema?.headers ?? {}) + } + } + }) + .after((_) => { + const data = app.overview<{ bodySchema: {}, headerNames: string[] }>() + expectType>(data) + }) + .ready() + +app + .register(fastifyOverview, { + onRouteDefinition: (routeOptions) => { + return { + url: routeOptions.url.length + } + } + }) + .after((_) => { + const data = app.overview<{ url: number }>() + expectType>(data) + expectType(data.routes![0].url) + }) + .ready()