@fastify/swagger
Advanced tools
Comparing version 8.14.0 to 9.0.0-pre.fv5.1
@@ -28,3 +28,3 @@ 'use strict' | ||
module.exports = fp(fastifySwagger, { | ||
fastify: '4.x', | ||
fastify: '5.x', | ||
name: '@fastify/swagger' | ||
@@ -31,0 +31,0 @@ }) |
@@ -118,32 +118,2 @@ 'use strict' | ||
function transformDefsToComponents (jsonSchema) { | ||
if (typeof jsonSchema === 'object' && jsonSchema !== null) { | ||
// Handle patternProperties, that is not part of OpenAPI definitions | ||
if (jsonSchema.patternProperties) { | ||
jsonSchema.additionalProperties = Object.values(jsonSchema.patternProperties)[0] | ||
delete jsonSchema.patternProperties | ||
} else if (jsonSchema.const !== undefined) { | ||
// OAS 3.1 supports `const` but it is not supported by `swagger-ui` | ||
// https://swagger.io/docs/specification/data-models/keywords/ | ||
jsonSchema.enum = [jsonSchema.const] | ||
delete jsonSchema.const | ||
} | ||
Object.keys(jsonSchema).forEach(function (key) { | ||
if (key === 'properties') { | ||
Object.keys(jsonSchema[key]).forEach(function (prop) { | ||
jsonSchema[key][prop] = transformDefsToComponents(jsonSchema[key][prop]) | ||
}) | ||
} else if (key === '$ref') { | ||
jsonSchema[key] = jsonSchema[key].replace('definitions', 'components/schemas') | ||
} else if (key === '$id' || key === '$schema') { | ||
delete jsonSchema[key] | ||
} else { | ||
jsonSchema[key] = transformDefsToComponents(jsonSchema[key]) | ||
} | ||
}) | ||
} | ||
return jsonSchema | ||
} | ||
function convertExamplesArrayToObject (examples) { | ||
@@ -164,3 +134,3 @@ return examples.reduce((examplesObject, example, index) => { | ||
function plainJsonObjectToOpenapi3 (container, jsonSchema, externalSchemas, securityIgnores = []) { | ||
const obj = transformDefsToComponents(resolveLocalRef(jsonSchema, externalSchemas)) | ||
const obj = convertJsonSchemaToOpenapi3(resolveLocalRef(jsonSchema, externalSchemas)) | ||
let toOpenapiProp | ||
@@ -297,3 +267,3 @@ switch (container) { | ||
function resolveBodyParams (body, schema, consumes, ref) { | ||
const resolved = transformDefsToComponents(ref.resolve(schema)) | ||
const resolved = convertJsonSchemaToOpenapi3(ref.resolve(schema)) | ||
if ((Array.isArray(consumes) && consumes.length === 0) || consumes === undefined) { | ||
@@ -319,3 +289,3 @@ consumes = ['application/json'] | ||
const schemasPath = '#/components/schemas/' | ||
let resolved = transformDefsToComponents(ref.resolve(schema)) | ||
let resolved = convertJsonSchemaToOpenapi3(ref.resolve(schema)) | ||
@@ -346,3 +316,3 @@ // if the resolved definition is in global schema | ||
const rawJsonSchema = fastifyResponseJson[statusCode] | ||
const resolved = transformDefsToComponents(ref.resolve(rawJsonSchema)) | ||
const resolved = convertJsonSchemaToOpenapi3(ref.resolve(rawJsonSchema)) | ||
@@ -473,20 +443,78 @@ /** | ||
function prepareOpenapiSchemas (schemas, ref) { | ||
return Object.entries(schemas) | ||
.reduce((res, [name, schema]) => { | ||
const _ = { ...schema } | ||
const resolved = transformDefsToComponents(ref.resolve(_, { externalSchemas: [schemas] })) | ||
function convertJsonSchemaToOpenapi3 (jsonSchema) { | ||
if (typeof jsonSchema !== 'object' || jsonSchema === null) { | ||
return jsonSchema | ||
} | ||
// Swagger doesn't accept $id on /definitions schemas. | ||
// The $ids are needed by Ref() to check the URI so we need | ||
// to remove them at the end of the process | ||
// definitions are added by resolve but they are replace by components.schemas | ||
delete resolved.$id | ||
delete resolved.definitions | ||
if (Array.isArray(jsonSchema)) { | ||
return jsonSchema.map(convertJsonSchemaToOpenapi3) | ||
} | ||
res[name] = resolved | ||
return res | ||
}, {}) | ||
const openapiSchema = { ...jsonSchema } | ||
for (const key of Object.keys(openapiSchema)) { | ||
const value = openapiSchema[key] | ||
if (key === '$id' || key === '$schema' || key === 'definitions') { | ||
// TODO: this breaks references to the definition properties | ||
delete openapiSchema[key] | ||
continue | ||
} | ||
if (key === '$ref') { | ||
openapiSchema.$ref = value.replace('definitions', 'components/schemas') | ||
continue | ||
} | ||
if (key === 'const') { | ||
// OAS 3.1 supports `const` but it is not supported by `swagger-ui` | ||
// https://swagger.io/docs/specification/data-models/keywords/ | ||
// TODO: check if enum property already exists | ||
// TODO: this breaks references to the const property | ||
openapiSchema.enum = [openapiSchema.const] | ||
delete openapiSchema.const | ||
continue | ||
} | ||
if (key === 'patternProperties') { | ||
// TODO: check if additionalProperties property already exists | ||
// TODO: this breaks references to the additionalProperties properties | ||
// TODO: patternProperties actually allowed in the openapi schema, but should | ||
// always start with "x-" prefix | ||
openapiSchema.additionalProperties = Object.values(openapiSchema.patternProperties)[0] | ||
delete openapiSchema.patternProperties | ||
continue | ||
} | ||
if (key === 'properties') { | ||
openapiSchema[key] = {} | ||
for (const propertyName of Object.keys(value)) { | ||
const propertyJsonSchema = value[propertyName] | ||
const propertyOpenapiSchema = convertJsonSchemaToOpenapi3(propertyJsonSchema) | ||
openapiSchema[key][propertyName] = propertyOpenapiSchema | ||
} | ||
continue | ||
} | ||
openapiSchema[key] = convertJsonSchemaToOpenapi3(value) | ||
} | ||
return openapiSchema | ||
} | ||
function prepareOpenapiSchemas (jsonSchemas, ref) { | ||
const openapiSchemas = {} | ||
for (const schemaName of Object.keys(jsonSchemas)) { | ||
const jsonSchema = { ...jsonSchemas[schemaName] } | ||
const resolvedJsonSchema = ref.resolve(jsonSchema, { externalSchemas: [jsonSchemas] }) | ||
const openapiSchema = convertJsonSchemaToOpenapi3(resolvedJsonSchema) | ||
resolveSchemaExamplesRecursive(openapiSchema) | ||
openapiSchemas[schemaName] = openapiSchema | ||
} | ||
return openapiSchemas | ||
} | ||
module.exports = { | ||
@@ -493,0 +521,0 @@ prepareDefaultOptions, |
@@ -24,3 +24,3 @@ # Migration | ||
| 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).| | ||
| uiHooks | {} | Additional hooks for the documentation's routes. You can provide the `onRequest` and `preHandler` hooks with the same [route's options](https://fastify.dev/docs/latest/Routes/#options) interface.| | ||
| uiHooks | {} | Additional hooks for the documentation's routes. You can provide the `onRequest` and `preHandler` hooks with the same [route's options](https://fastify.dev/docs/latest/Reference/Routes/#options) interface.| | ||
@@ -27,0 +27,0 @@ The `baseDir` option is new and is only needed if external spec files should be |
{ | ||
"name": "@fastify/swagger", | ||
"version": "8.14.0", | ||
"version": "9.0.0-pre.fv5.1", | ||
"description": "Serve Swagger/OpenAPI documentation for Fastify, supporting dynamic generation", | ||
@@ -43,20 +43,20 @@ "main": "index.js", | ||
"@apidevtools/swagger-parser": "^10.1.0", | ||
"@fastify/cookie": "^9.0.4", | ||
"@fastify/pre-commit": "^2.0.2", | ||
"@types/node": "^20.1.0", | ||
"fastify": "^4.0.0", | ||
"fluent-json-schema": "^4.0.0", | ||
"joi": "^17.6.0", | ||
"joi-to-json": "^4.0.0", | ||
"qs": "^6.10.3", | ||
"standard": "^17.0.0", | ||
"tap": "^16.2.0", | ||
"tsd": "^0.30.1" | ||
"@fastify/cookie": "^10.0.0-pre.fv5.1", | ||
"@fastify/pre-commit": "^2.1.0", | ||
"@types/node": "^20.12.8", | ||
"fastify": "^5.0.0-alpha.3", | ||
"fluent-json-schema": "^5.0.0", | ||
"joi": "^17.13.1", | ||
"joi-to-json": "^4.2.1", | ||
"qs": "^6.12.1", | ||
"standard": "^17.1.0", | ||
"tap": "18.7.2", | ||
"tsd": "^0.31.0" | ||
}, | ||
"dependencies": { | ||
"fastify-plugin": "^4.0.0", | ||
"fastify-plugin": "^5.0.0-pre.fv5.1", | ||
"json-schema-resolver": "^2.0.0", | ||
"openapi-types": "^12.0.0", | ||
"rfdc": "^1.3.0", | ||
"yaml": "^2.2.2" | ||
"openapi-types": "^12.1.3", | ||
"rfdc": "^1.3.1", | ||
"yaml": "^2.4.2" | ||
}, | ||
@@ -63,0 +63,0 @@ "standard": { |
@@ -11,3 +11,3 @@ # @fastify/swagger | ||
Following plugins serve swagger/openapi front-ends based on the swagger definitions generated by this plugin: | ||
Following plugins serve Swagger/OpenAPI front-ends based on the swagger definitions generated by this plugin: | ||
@@ -21,2 +21,3 @@ - [@fastify/swagger-ui](https://github.com/fastify/fastify-swagger-ui) | ||
## Install | ||
``` | ||
@@ -44,4 +45,5 @@ npm i @fastify/swagger | ||
## Usage | ||
Add it to your project with `register`, pass it some options, call the `swagger` API, and you are done! | ||
Add it to your project with `register`, pass it some options, call the `swagger` API, and you are done! Below an example of how to configure the OpenAPI v3 specification with Fastify Swagger: | ||
```js | ||
@@ -51,3 +53,4 @@ const fastify = require('fastify')() | ||
await fastify.register(require('@fastify/swagger'), { | ||
swagger: { | ||
openapi: { | ||
openapi: '3.0.0', | ||
info: { | ||
@@ -58,10 +61,8 @@ title: 'Test swagger', | ||
}, | ||
externalDocs: { | ||
url: 'https://swagger.io', | ||
description: 'Find more info here' | ||
}, | ||
host: 'localhost', | ||
schemes: ['http'], | ||
consumes: ['application/json'], | ||
produces: ['application/json'], | ||
servers: [ | ||
{ | ||
url: 'http://localhost:3000', | ||
description: 'Development server' | ||
} | ||
], | ||
tags: [ | ||
@@ -71,20 +72,14 @@ { name: 'user', description: 'User related end-points' }, | ||
], | ||
definitions: { | ||
User: { | ||
type: 'object', | ||
required: ['id', 'email'], | ||
properties: { | ||
id: { type: 'string', format: 'uuid' }, | ||
firstName: { type: 'string' }, | ||
lastName: { type: 'string' }, | ||
email: {type: 'string', format: 'email' } | ||
components: { | ||
securitySchemes: { | ||
apiKey: { | ||
type: 'apiKey', | ||
name: 'apiKey', | ||
in: 'header' | ||
} | ||
} | ||
}, | ||
securityDefinitions: { | ||
apiKey: { | ||
type: 'apiKey', | ||
name: 'apiKey', | ||
in: 'header' | ||
} | ||
externalDocs: { | ||
url: 'https://swagger.io', | ||
description: 'Find more info here' | ||
} | ||
@@ -99,2 +94,3 @@ } | ||
summary: 'qwerty', | ||
security: [{ apiKey: [] }], | ||
params: { | ||
@@ -136,10 +132,5 @@ type: 'object', | ||
} | ||
}, | ||
security: [ | ||
{ | ||
"apiKey": [] | ||
} | ||
] | ||
} | ||
} | ||
}, (req, reply) => {}) | ||
}, (req, reply) => { }) | ||
@@ -149,2 +140,19 @@ await fastify.ready() | ||
``` | ||
<a name="usage.fastify.autoload"></a> | ||
### With `@fastify/autoload` | ||
You need to register `@fastify/swagger` before registering routes. | ||
```js | ||
const fastify = require('fastify')() | ||
const fastify = fastify() | ||
await fastify.register(require('@fastify/swagger')) | ||
fastify.register(require("@fastify/autoload"), { | ||
dir: path.join(__dirname, 'routes') | ||
}) | ||
await fastify.ready() | ||
fastify.swagger() | ||
``` | ||
<a name="api"></a> | ||
@@ -151,0 +159,0 @@ ## API |
@@ -188,3 +188,3 @@ 'use strict' | ||
t.rejects(fastify.register(fastifySwagger, config), new Error('specification.document is not an object')) | ||
t.rejects(async () => await fastify.register(fastifySwagger, config), new Error('specification.document is not an object')) | ||
}) | ||
@@ -191,0 +191,0 @@ |
@@ -110,3 +110,3 @@ 'use strict' | ||
const openapiObject = fastify.swagger() | ||
t.same(openapiObject.components.schemas, openapiOption.openapi.components.schemas) | ||
t.match(openapiObject.components.schemas, openapiOption.openapi.components.schemas) | ||
delete openapiOption.openapi.components.schemas // remove what we just added | ||
@@ -113,0 +113,0 @@ }) |
@@ -22,3 +22,3 @@ 'use strict' | ||
fastify.register(async (instance) => { | ||
instance.addSchema({ $id: 'Order', type: 'object', properties: { id: { type: 'integer' } } }) | ||
instance.addSchema({ $id: 'Order', type: 'object', properties: { id: { type: 'integer', examples: [25] } } }) | ||
instance.post('/', { schema: { body: { $ref: 'Order#' }, response: { 200: { $ref: 'Order#' } } } }, () => {}) | ||
@@ -32,2 +32,3 @@ }) | ||
t.match(Object.keys(openapiObject.components.schemas), ['Order']) | ||
t.equal(openapiObject.components.schemas.Order.properties.id.example, 25) | ||
@@ -72,3 +73,3 @@ await Swagger.validate(openapiObject) | ||
fastify.register(async (instance) => { | ||
instance.addSchema({ $id: 'OrderItem', type: 'object', properties: { id: { type: 'integer' } } }) | ||
instance.addSchema({ $id: 'OrderItem', type: 'object', properties: { id: { type: 'integer' } }, examples: [{ id: 1 }] }) | ||
instance.addSchema({ $id: 'ProductItem', type: 'object', properties: { id: { type: 'integer' } } }) | ||
@@ -90,2 +91,3 @@ instance.addSchema({ $id: 'Order', type: 'object', properties: { products: { type: 'array', items: { $ref: 'OrderItem' } } } }) | ||
t.equal(schemas.Order.properties.products.items.$ref, '#/components/schemas/OrderItem') | ||
t.match(schemas.OrderItem.example, { id: 1 }) | ||
@@ -100,3 +102,3 @@ await Swagger.validate(openapiObject) | ||
instance.addSchema({ $id: 'schemaA', type: 'object', properties: { id: { type: 'integer' } } }) | ||
instance.addSchema({ $id: 'schemaB', type: 'object', properties: { id: { type: 'string' } } }) | ||
instance.addSchema({ $id: 'schemaB', type: 'object', properties: { id: { type: 'string', examples: ['ABC'] } } }) | ||
instance.addSchema({ $id: 'schemaC', type: 'object', properties: { a: { type: 'array', items: { $ref: 'schemaA' } } } }) | ||
@@ -120,2 +122,3 @@ instance.addSchema({ $id: 'schemaD', type: 'object', properties: { b: { $ref: 'schemaB' }, c: { $ref: 'schemaC' } } }) | ||
t.equal(schemas.schemaD.properties.c.$ref, '#/components/schemas/schemaC') | ||
t.equal(schemas.schemaB.properties.id.example, 'ABC') | ||
@@ -122,0 +125,0 @@ await Swagger.validate(openapiObject) |
@@ -162,3 +162,3 @@ 'use strict' | ||
t.ok(definedPath) | ||
t.same(definedPath.responses[200].content, { | ||
t.match(definedPath.responses[200].content, { | ||
'*/*': { | ||
@@ -238,3 +238,3 @@ schema: { | ||
t.ok(definedPath) | ||
t.same(definedPath.requestBody.content, { | ||
t.match(definedPath.requestBody.content, { | ||
'application/x-www-form-urlencoded': { | ||
@@ -428,3 +428,3 @@ schema: { | ||
t.ok(cookiesPath) | ||
t.same(cookiesPath.parameters, [ | ||
t.match(cookiesPath.parameters, [ | ||
{ | ||
@@ -451,3 +451,3 @@ required: false, | ||
t.ok(querystringPath) | ||
t.same(querystringPath.parameters, [ | ||
t.match(querystringPath.parameters, [ | ||
{ | ||
@@ -454,0 +454,0 @@ required: false, |
@@ -159,3 +159,3 @@ 'use strict' | ||
t.same(definedPath.responses['200'].description, 'Description and all status-code based properties are working') | ||
t.strictSame(definedPath.responses['200'].content, { | ||
t.match(definedPath.responses['200'].content, { | ||
'application/json': { | ||
@@ -179,3 +179,3 @@ schema: { | ||
t.same(definedPath.responses['4XX'].description, 'Default Response') | ||
t.strictSame(definedPath.responses['4XX'].content, { | ||
t.match(definedPath.responses['4XX'].content, { | ||
'application/json': { | ||
@@ -190,3 +190,3 @@ schema: { | ||
}) | ||
t.strictSame(definedPath.responses[300].content, { | ||
t.match(definedPath.responses[300].content, { | ||
'application/json': { | ||
@@ -492,3 +492,3 @@ schema: { | ||
t.same(definedPath.responses.default, { | ||
t.match(definedPath.responses.default, { | ||
description: 'Default Response', | ||
@@ -571,3 +571,3 @@ content: { | ||
t.same(definedPath.responses[200], { | ||
t.match(definedPath.responses[200], { | ||
description: 'Expected Response', | ||
@@ -630,3 +630,3 @@ content: { | ||
t.same(definedPath.responses[200], { | ||
t.match(definedPath.responses[200], { | ||
description: 'Expected Response', | ||
@@ -687,3 +687,3 @@ content: { | ||
const definedPath = api.paths['/'].post | ||
t.same(definedPath.requestBody, { | ||
t.match(definedPath.requestBody, { | ||
content: { | ||
@@ -750,3 +750,3 @@ 'application/json': { | ||
const definedPath = api.paths['/'].post | ||
t.same(definedPath.requestBody, { | ||
t.match(definedPath.requestBody, { | ||
content: { | ||
@@ -810,3 +810,3 @@ 'application/json': { | ||
const definedPath = api.paths['/'].post | ||
t.same(definedPath.requestBody, { | ||
t.match(definedPath.requestBody, { | ||
content: { | ||
@@ -865,3 +865,3 @@ 'application/json': { | ||
const definedPath = api.paths['/'].post | ||
t.same(definedPath.requestBody, { | ||
t.match(definedPath.requestBody, { | ||
description: 'Body description', | ||
@@ -868,0 +868,0 @@ content: { |
@@ -98,3 +98,3 @@ 'use strict' | ||
const swaggerObject = fastify.swagger() | ||
t.same(swaggerObject.definitions, swaggerOption.swagger.definitions) | ||
t.match(swaggerObject.definitions, swaggerOption.swagger.definitions) | ||
delete swaggerOption.swagger.definitions // remove what we just added | ||
@@ -101,0 +101,0 @@ }) |
@@ -346,3 +346,3 @@ 'use strict' | ||
t.same(definedPath.responses.default, { | ||
t.match(definedPath.responses.default, { | ||
description: 'Default Response', | ||
@@ -425,3 +425,3 @@ schema: { | ||
t.same(definedPath.parameters[0].schema, { | ||
t.match(definedPath.parameters[0].schema, { | ||
type: 'object', | ||
@@ -431,3 +431,3 @@ additionalProperties: { type: 'string' } | ||
t.same(definedPath.responses[200], { | ||
t.match(definedPath.responses[200], { | ||
description: 'Expected Response', | ||
@@ -477,3 +477,3 @@ schema: { | ||
const definedPath = api.paths['/'].post | ||
t.same(definedPath.parameters[0].schema, { | ||
t.match(definedPath.parameters[0].schema, { | ||
type: 'object', | ||
@@ -530,3 +530,3 @@ properties: { | ||
t.same(definedPath.parameters[0].description, 'Body description') | ||
t.same(definedPath.parameters[0].schema, { | ||
t.match(definedPath.parameters[0].schema, { | ||
type: 'object', | ||
@@ -704,3 +704,3 @@ description: 'Body description', | ||
t.strictSame(definedPath.parameters[0].schema, { | ||
t.match(definedPath.parameters[0].schema, { | ||
type: 'object', | ||
@@ -752,3 +752,3 @@ properties: { | ||
t.strictSame(definedPath.parameters[0].schema, { | ||
t.match(definedPath.parameters[0].schema, { | ||
type: 'object', | ||
@@ -755,0 +755,0 @@ properties: { |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
303738
8815
1117
1
+ Addedfastify-plugin@5.0.0(transitive)
- Removedfastify-plugin@4.5.1(transitive)
Updatedopenapi-types@^12.1.3
Updatedrfdc@^1.3.1
Updatedyaml@^2.4.2