From 21bbc1912afc681af61fbd43e2a1903cb0403849 Mon Sep 17 00:00:00 2001 From: Manuel Spigolon Date: Mon, 30 Aug 2021 15:15:05 +0200 Subject: [PATCH 1/2] add local reference builder --- README.md | 43 +++++++++++++++++++++++++++++++------ lib/mode/dynamic.js | 14 +++++++++--- lib/util/common.js | 9 +++++--- package.json | 2 +- test/spec/openapi/schema.js | 22 ++++++++++++++++++- 5 files changed, 75 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 3af31062..ac082bb8 100644 --- a/README.md +++ b/README.md @@ -211,13 +211,14 @@ An example of using `fastify-swagger` with `static` mode enabled can be found [h | hideUntagged | false | If `true` remove routes without tags from resulting Swagger/OpenAPI schema file. | | initOAuth | {} | Configuration options for [Swagger UI initOAuth](https://swagger.io/docs/open-source-tools/swagger-ui/usage/oauth2/). | | openapi | {} | [OpenAPI configuration](https://swagger.io/specification/#oasObject). | - | routePrefix | '/documentation' | Overwrite the default Swagger UI route prefix. | + | routePrefix | '/documentation' | Overwrite the default Swagger UI route prefix. | | staticCSP | false | Enable CSP header for static resources. | | stripBasePath | true | Strips base path from routes in docs. | | swagger | {} | [Swagger configuration](https://swagger.io/specification/v2/#swaggerObject). | | transform | null | Transform method for schema. | - | transformStaticCSP | undefined | Synchronous function to transform CSP header for static resources if the header has been previously set. | - | uiConfig | {} | Configuration options for [Swagger UI](https://github.com/swagger-api/swagger-ui/blob/master/docs/usage/configuration.md). Must be literal values, see [#5710](https://github.com/swagger-api/swagger-ui/issues/5710).| + | transformStaticCSP | undefined | Synchronous function to transform CSP header for static resources if the header has been previously set. | + | uiConfig | {} | Configuration options for [Swagger UI](https://github.com/swagger-api/swagger-ui/blob/master/docs/usage/configuration.md). Must be literal values, see [#5710](https://github.com/swagger-api/swagger-ui/issues/5710).| + | refResolver | {} | Option to manage the `$ref`s of your application's schemas. Read the [`$ref` documentation](#register.options.refResolver) | If you set `exposeRoute` to `true` the plugin will expose the documentation with the following APIs: @@ -255,6 +256,32 @@ fastify.register(require('fastify-swagger'), { } ``` + +#### Managing your `$ref`s + +When this plugin is configured as `dynamic` mode, it will resolve all `$ref`s in your application's schemas. +This process will create an new in-line schema that is going to reference itself. + +This logic stap it is done to make sure that the generated documentation is valid, otherwise the Swagger UI will try to fetch the schemas from the server or the network and fail. + +By default, this option will resolve all `$ref`s renaming them to `def-${counter}`, but your view models keep the original `$id` naming thanks to the [`title` parameter](https://swagger.io/docs/specification/2-0/basic-structure/#metadata). + +To customize this logic you can pass a `refResolver` option to the plugin: + +```js +fastify.register(require('fastify-swagger'), { + swagger: { ... }, + ... + refResolver: { + buildLocalReference (json, baseUri, fragment, i) { + return `my-fragment-${i}` + } + } +} +``` + +To deep down the `buildLocalReference` arguments, you may read the [documentation](https://github.com/Eomm/json-schema-resolver#usage-resolve-one-schema-against-external-schemas). + ### Route options @@ -637,8 +664,7 @@ You can integration this plugin with ```fastify-helmet``` with some little work. }) ``` - -### Development +## Development In order to start development run: ``` npm i @@ -647,11 +673,14 @@ npm run prepare So that [swagger-ui](https://github.com/swagger-api/swagger-ui) static folder will be generated for you. -#### How it works under the hood +### How it works under the hood `fastify-static` serves `swagger-ui` static files, then calls `/docs/json` to get the Swagger file and render it. - +#### How to work with $refs + +The `/docs/json` endpoint in dynamic mode produces a single `swagger.json` file resolving all your + ## Acknowledgements This project is kindly sponsored by: diff --git a/lib/mode/dynamic.js b/lib/mode/dynamic.js index 6a1b31b4..ebee051e 100644 --- a/lib/mode/dynamic.js +++ b/lib/mode/dynamic.js @@ -3,8 +3,6 @@ const { addHook, resolveSwaggerFunction } = require('../util/common') module.exports = function (fastify, opts, done) { - const { routes, Ref } = addHook(fastify) - opts = Object.assign({}, { exposeRoute: false, hiddenTag: 'X-HIDDEN', @@ -12,9 +10,19 @@ module.exports = function (fastify, opts, done) { stripBasePath: true, openapi: null, swagger: {}, - transform: null + transform: null, + refResolver: { + buildLocalReference (json, baseUri, fragment, i) { + if (!json.title && json.$id) { + json.title = json.$id + } + return `def-${i}` + } + } }, opts) + const { routes, Ref } = addHook(fastify, opts) + if (opts.exposeRoute === true) { const prefix = opts.routePrefix || '/documentation' const uiConfig = opts.uiConfig || {} diff --git a/lib/util/common.js b/lib/util/common.js index a727db25..fc416628 100644 --- a/lib/util/common.js +++ b/lib/util/common.js @@ -6,7 +6,7 @@ const Ref = require('json-schema-resolver') const { rawRequired } = require('../symbols') const { xConsume } = require('../constants') -function addHook (fastify) { +function addHook (fastify, pluginOptions) { const routes = [] const sharedSchemasMap = new Map() @@ -35,8 +35,11 @@ function addHook (fastify) { routes, Ref () { const externalSchemas = Array.from(sharedSchemasMap.values()) - // TODO: hardcoded applicationUri is not a ideal solution - return Ref({ clone: true, applicationUri: 'todo.com', externalSchemas }) + return Ref(Object.assign( + { applicationUri: 'todo.com' }, + pluginOptions.refResolver, + { clone: true, externalSchemas }) + ) } } } diff --git a/package.json b/package.json index 14c47b46..367919aa 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ "fastify-plugin": "^3.0.0", "fastify-static": "^4.0.0", "js-yaml": "^4.0.0", - "json-schema-resolver": "^1.2.0", + "json-schema-resolver": "github:Eomm/json-schema-resolver#ref-to-def", "openapi-types": "^9.1.0" }, "standard": { diff --git a/test/spec/openapi/schema.js b/test/spec/openapi/schema.js index 719f27bb..a34beb25 100644 --- a/test/spec/openapi/schema.js +++ b/test/spec/openapi/schema.js @@ -308,5 +308,25 @@ test('support global schema reference', async t => { const swaggerObject = fastify.swagger() const api = await Swagger.validate(swaggerObject) - t.same(api.components.schemas['def-0'], schema) + t.match(api.components.schemas['def-0'], schema) +}) + +test('support global schema reference with title', async t => { + const schema = { + title: 'schema view title', + type: 'object', + properties: { + hello: { type: 'string' } + }, + required: ['hello'] + } + const fastify = Fastify() + fastify.register(fastifySwagger, { openapi: true, routePrefix: '/docs', exposeRoute: true }) + fastify.addSchema({ ...schema, $id: 'requiredUniqueSchema' }) + fastify.get('/', { schema: { query: { $ref: 'requiredUniqueSchema' } } }, () => {}) + await fastify.ready() + + const swaggerObject = fastify.swagger() + const api = await Swagger.validate(swaggerObject) + t.match(api.components.schemas['def-0'], schema) }) From a2c210d6a4ae8c251a52e2c75a2a644caa435f94 Mon Sep 17 00:00:00 2001 From: Manuel Spigolon Date: Mon, 30 Aug 2021 17:29:09 +0200 Subject: [PATCH 2/2] bump dep --- README.md | 8 ++++-- examples/dynamic-swagger.js | 53 ++++++++++++++++--------------------- package.json | 2 +- 3 files changed, 30 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index ac082bb8..8ae4cb2d 100644 --- a/README.md +++ b/README.md @@ -262,7 +262,7 @@ fastify.register(require('fastify-swagger'), { When this plugin is configured as `dynamic` mode, it will resolve all `$ref`s in your application's schemas. This process will create an new in-line schema that is going to reference itself. -This logic stap it is done to make sure that the generated documentation is valid, otherwise the Swagger UI will try to fetch the schemas from the server or the network and fail. +This logic step is done to make sure that the generated documentation is valid, otherwise the Swagger UI will try to fetch the schemas from the server or the network and fail. By default, this option will resolve all `$ref`s renaming them to `def-${counter}`, but your view models keep the original `$id` naming thanks to the [`title` parameter](https://swagger.io/docs/specification/2-0/basic-structure/#metadata). @@ -274,7 +274,7 @@ fastify.register(require('fastify-swagger'), { ... refResolver: { buildLocalReference (json, baseUri, fragment, i) { - return `my-fragment-${i}` + return json.$id || `my-fragment-${i}` } } } @@ -664,6 +664,10 @@ You can integration this plugin with ```fastify-helmet``` with some little work. }) ``` + +## `$id` and `$ref` usage + + ## Development In order to start development run: ``` diff --git a/examples/dynamic-swagger.js b/examples/dynamic-swagger.js index f9bc9e53..83f6682a 100644 --- a/examples/dynamic-swagger.js +++ b/examples/dynamic-swagger.js @@ -25,31 +25,37 @@ fastify.register(require('../index'), { exposeRoute: true }) +fastify.addSchema({ + $id: 'user', + type: 'object', + properties: { + id: { + type: 'string', + description: 'user id' + } + } +}) + +fastify.addSchema({ + $id: 'some', + type: 'object', + properties: { + some: { type: 'string' } + } +}) + fastify.put('/some-route/:id', { schema: { description: 'post some data', tags: ['user', 'code'], summary: 'qwerty', security: [{ apiKey: [] }], - params: { - type: 'object', - properties: { - id: { - type: 'string', - description: 'user id' - } - } - }, + params: { $ref: 'user#' }, body: { type: 'object', properties: { hello: { type: 'string' }, - obj: { - type: 'object', - properties: { - some: { type: 'string' } - } - } + obj: { $ref: 'some#' } } }, response: { @@ -69,25 +75,12 @@ fastify.post('/some-route/:id', { description: 'post some data', summary: 'qwerty', security: [{ apiKey: [] }], - params: { - type: 'object', - properties: { - id: { - type: 'string', - description: 'user id' - } - } - }, + params: { $ref: 'user#' }, body: { type: 'object', properties: { hello: { type: 'string' }, - obj: { - type: 'object', - properties: { - some: { type: 'string' } - } - } + obj: { $ref: 'some#' } } }, response: { diff --git a/package.json b/package.json index 367919aa..ac6511a1 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ "fastify-plugin": "^3.0.0", "fastify-static": "^4.0.0", "js-yaml": "^4.0.0", - "json-schema-resolver": "github:Eomm/json-schema-resolver#ref-to-def", + "json-schema-resolver": "^1.3.0", "openapi-types": "^9.1.0" }, "standard": {