@fastify/swagger
Advanced tools
Comparing version 8.11.0 to 8.12.0
@@ -52,3 +52,3 @@ 'use strict' | ||
const openapiMethod = prepareOpenapiMethod(schema, ref, openapiObject) | ||
const openapiMethod = prepareOpenapiMethod(schema, ref, openapiObject, url) | ||
@@ -55,0 +55,0 @@ if (route.links) { |
@@ -8,2 +8,4 @@ 'use strict' | ||
const { rawRequired } = require('../../symbols') | ||
const { generateParamsSchema } = require('../../util/generate-params-schema') | ||
const { hasParams } = require('../../util/match-params') | ||
@@ -363,3 +365,3 @@ function prepareDefaultOptions (opts) { | ||
function prepareOpenapiMethod (schema, ref, openapiObject) { | ||
function prepareOpenapiMethod (schema, ref, openapiObject, url) { | ||
const openapiMethod = {} | ||
@@ -412,2 +414,9 @@ const parameters = [] | ||
// If there is no schema or schema.params, we need to generate them | ||
if ((!schema || !schema.params) && hasParams(url)) { | ||
const schemaGenerated = generateParamsSchema(url) | ||
resolveCommonParams('path', parameters, schemaGenerated.params, ref, openapiObject.definitions) | ||
openapiMethod.parameters = parameters | ||
} | ||
openapiMethod.responses = resolveResponse(schema ? schema.response : null, schema ? schema.produces : null, ref) | ||
@@ -414,0 +423,0 @@ |
@@ -50,3 +50,3 @@ 'use strict' | ||
const swaggerMethod = prepareSwaggerMethod(schema, ref, swaggerObject) | ||
const swaggerMethod = prepareSwaggerMethod(schema, ref, swaggerObject, url) | ||
@@ -53,0 +53,0 @@ if (route.links) { |
@@ -7,2 +7,4 @@ 'use strict' | ||
const { xResponseDescription, xConsume } = require('../../constants') | ||
const { generateParamsSchema } = require('../../util/generate-params-schema') | ||
const { hasParams } = require('../../util/match-params') | ||
@@ -258,3 +260,3 @@ function prepareDefaultOptions (opts) { | ||
function prepareSwaggerMethod (schema, ref, swaggerObject) { | ||
function prepareSwaggerMethod (schema, ref, swaggerObject, url) { | ||
const swaggerMethod = {} | ||
@@ -307,2 +309,9 @@ const parameters = [] | ||
// If there is no schema or schema.params, we need to generate them | ||
if ((!schema || !schema.params) && hasParams(url)) { | ||
const schemaGenerated = generateParamsSchema(url) | ||
resolveCommonParams('path', parameters, schemaGenerated.params, ref, swaggerObject.definitions) | ||
swaggerMethod.parameters = parameters | ||
} | ||
swaggerMethod.responses = resolveResponse(schema ? schema.response : null, ref) | ||
@@ -309,0 +318,0 @@ |
{ | ||
"name": "@fastify/swagger", | ||
"version": "8.11.0", | ||
"version": "8.12.0", | ||
"description": "Serve Swagger/OpenAPI documentation for Fastify, supporting dynamic generation", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
141
README.md
@@ -564,3 +564,3 @@ # @fastify/swagger | ||
**Note:** OpenAPI's terminology differs from Fastify's. OpenAPI uses "parameter" to refer to parts of a request that in [Fastify's validation documentation](https://www.fastify.io/docs/latest/Validation-and-Serialization/#validation) are called "querystring", "params", and "headers". | ||
**Note:** OpenAPI's terminology differs from Fastify's. OpenAPI uses "parameter" to refer to parts of a request that in [Fastify's validation documentation](https://fastify.dev/docs/latest/Reference/Validation-and-Serialization/) are called "querystring", "params", and "headers". | ||
@@ -702,2 +702,141 @@ OpenAPI provides some options beyond those provided by the [JSON schema specification](https://json-schema.org/specification.html) for specifying the shape of parameters. A prime example of this is the `collectionFormat` option for specifying how to encode parameters that should be handled as arrays of values. | ||
##### Route parameters | ||
Route parameters in Fastify are called params, these are values included in the URL of the requests, for example: | ||
```js | ||
fastify.route({ | ||
method: 'GET', | ||
url: '/:id', | ||
schema: { | ||
params: { | ||
type: 'object', | ||
properties: { | ||
id: { | ||
type: 'string', | ||
description: 'user id' | ||
} | ||
} | ||
} | ||
}, | ||
handler (request, reply) { | ||
reply.send(request.params.id) | ||
} | ||
}) | ||
``` | ||
Will generate this in the Swagger (OpenAPI v2) schema's `paths`: | ||
```json | ||
{ | ||
"/{id}": { | ||
"get": { | ||
"parameters": [ | ||
{ | ||
"type": "string", | ||
"description": "user id", | ||
"required": true, | ||
"in": "path", | ||
"name": "id" | ||
} | ||
], | ||
"responses": { | ||
"200": { | ||
"description": "Default Response" | ||
} | ||
} | ||
} | ||
} | ||
} | ||
``` | ||
Will generate this in the OpenAPI v3 schema's `paths`: | ||
```json | ||
{ | ||
"/{id}": { | ||
"get": { | ||
"parameters": [ | ||
{ | ||
"schema": { | ||
"type": "string" | ||
}, | ||
"in": "path", | ||
"name": "id", | ||
"required": true, | ||
"description": "user id" | ||
} | ||
], | ||
"responses": { | ||
"200": { | ||
"description": "Default Response" | ||
} | ||
} | ||
} | ||
} | ||
} | ||
``` | ||
Whether `params` is not present in the schema, or a schema is not provided, parameters are automatically generated, for example: | ||
```js | ||
fastify.route({ | ||
method: 'POST', | ||
url: '/:id', | ||
handler (request, reply) { | ||
reply.send(request.params.id) | ||
} | ||
}) | ||
``` | ||
Will generate this in the Swagger (OpenAPI v2) schema's `paths`: | ||
```json | ||
{ | ||
"/{id}": { | ||
"get": { | ||
"parameters": [ | ||
{ | ||
"type": "string", | ||
"required": true, | ||
"in": "path", | ||
"name": "id" | ||
} | ||
], | ||
"responses": { | ||
"200": { | ||
"description": "Default Response" | ||
} | ||
} | ||
} | ||
} | ||
} | ||
``` | ||
Will generate this in the OpenAPI v3 schema's `paths`: | ||
```json | ||
{ | ||
"/{id}": { | ||
"get": { | ||
"parameters": [ | ||
{ | ||
"schema": { | ||
"type": "string" | ||
}, | ||
"in": "path", | ||
"name": "id", | ||
"required": true | ||
} | ||
], | ||
"responses": { | ||
"200": { | ||
"description": "Default Response" | ||
} | ||
} | ||
} | ||
} | ||
} | ||
``` | ||
<a name="route.links"></a> | ||
@@ -704,0 +843,0 @@ #### Links |
@@ -865,1 +865,31 @@ 'use strict' | ||
}) | ||
test('verify generated path param definition with route prefixing', async (t) => { | ||
const opts = { | ||
schema: {} | ||
} | ||
const fastify = Fastify() | ||
await fastify.register(fastifySwagger, openapiRelativeOptions) | ||
await fastify.register(function (app, _, done) { | ||
app.get('/:userId', opts, () => {}) | ||
done() | ||
}, { prefix: '/v1' }) | ||
await fastify.ready() | ||
const swaggerObject = fastify.swagger() | ||
const api = await Swagger.validate(swaggerObject) | ||
const definedPath = api.paths['/v1/{userId}'].get | ||
t.same(definedPath.parameters, [{ | ||
schema: { | ||
type: 'string' | ||
}, | ||
in: 'path', | ||
name: 'userId', | ||
required: true | ||
}]) | ||
}) |
@@ -914,1 +914,115 @@ 'use strict' | ||
}) | ||
test('add default properties for url params when missing schema', async t => { | ||
const opt = {} | ||
const fastify = Fastify() | ||
await fastify.register(fastifySwagger, { | ||
openapi: true | ||
}) | ||
fastify.get('/:userId', opt, () => { }) | ||
await fastify.ready() | ||
const swaggerObject = fastify.swagger() | ||
const api = await Swagger.validate(swaggerObject) | ||
const definedPath = api.paths['/{userId}'].get | ||
t.strictSame(definedPath.parameters[0], { | ||
in: 'path', | ||
name: 'userId', | ||
required: true, | ||
schema: { | ||
type: 'string' | ||
} | ||
}) | ||
}) | ||
test('add default properties for url params when missing schema.params', async t => { | ||
const opt = { | ||
schema: { | ||
body: { | ||
type: 'object', | ||
properties: { | ||
bio: { | ||
type: 'string' | ||
} | ||
} | ||
} | ||
} | ||
} | ||
const fastify = Fastify() | ||
await fastify.register(fastifySwagger, { | ||
openapi: true | ||
}) | ||
fastify.post('/:userId', opt, () => { }) | ||
await fastify.ready() | ||
const swaggerObject = fastify.swagger() | ||
const api = await Swagger.validate(swaggerObject) | ||
const definedPath = api.paths['/{userId}'].post | ||
t.strictSame(definedPath.parameters[0], { | ||
in: 'path', | ||
name: 'userId', | ||
required: true, | ||
schema: { | ||
type: 'string' | ||
} | ||
}) | ||
t.strictSame(definedPath.requestBody.content['application/json'].schema.properties, { | ||
bio: { | ||
type: 'string' | ||
} | ||
}) | ||
}) | ||
test('avoid overwriting params when schema.params is provided', async t => { | ||
const opt = { | ||
schema: { | ||
params: { | ||
type: 'object', | ||
properties: { | ||
id: { | ||
type: 'string' | ||
} | ||
} | ||
}, | ||
body: { | ||
type: 'object', | ||
properties: { | ||
bio: { | ||
type: 'string' | ||
} | ||
} | ||
} | ||
} | ||
} | ||
const fastify = Fastify() | ||
await fastify.register(fastifySwagger, { | ||
openapi: true | ||
}) | ||
fastify.post('/:userId', opt, () => { }) | ||
await fastify.ready() | ||
const swaggerObject = fastify.swagger() | ||
const definedPath = swaggerObject.paths['/{userId}'].post | ||
t.strictSame(definedPath.parameters[0], { | ||
in: 'path', | ||
name: 'id', | ||
required: true, | ||
schema: { | ||
type: 'string' | ||
} | ||
}) | ||
t.strictSame(definedPath.requestBody.content['application/json'].schema.properties, { | ||
bio: { | ||
type: 'string' | ||
} | ||
}) | ||
}) |
@@ -558,1 +558,29 @@ 'use strict' | ||
}) | ||
test('verify generated path param definition with route prefixing', async (t) => { | ||
const opts = { | ||
schema: {} | ||
} | ||
const fastify = Fastify() | ||
await fastify.register(fastifySwagger, swaggerOption) | ||
await fastify.register(function (app, _, done) { | ||
app.get('/:userId', opts, () => {}) | ||
done() | ||
}, { prefix: '/v1' }) | ||
await fastify.ready() | ||
const swaggerObject = fastify.swagger() | ||
const api = await Swagger.validate(swaggerObject) | ||
const definedPath = api.paths['/v1/{userId}'].get | ||
t.same(definedPath.parameters, [{ | ||
in: 'path', | ||
name: 'userId', | ||
type: 'string', | ||
required: true | ||
}]) | ||
}) |
@@ -653,1 +653,109 @@ 'use strict' | ||
}) | ||
test('add default properties for url params when missing schema', async t => { | ||
const opt = {} | ||
const fastify = Fastify() | ||
await fastify.register(fastifySwagger) | ||
fastify.get('/:userId', opt, () => { }) | ||
await fastify.ready() | ||
const swaggerObject = fastify.swagger() | ||
const api = await Swagger.validate(swaggerObject) | ||
const definedPath = api.paths['/{userId}'].get | ||
t.strictSame(definedPath.parameters[0], { | ||
type: 'string', | ||
required: true, | ||
in: 'path', | ||
name: 'userId' | ||
}) | ||
}) | ||
test('add default properties for url params when missing schema.params', async t => { | ||
const opt = { | ||
schema: { | ||
body: { | ||
type: 'object', | ||
properties: { | ||
bio: { | ||
type: 'string' | ||
} | ||
} | ||
} | ||
} | ||
} | ||
const fastify = Fastify() | ||
await fastify.register(fastifySwagger) | ||
fastify.post('/:userId', opt, () => { }) | ||
await fastify.ready() | ||
const swaggerObject = fastify.swagger() | ||
const api = await Swagger.validate(swaggerObject) | ||
const definedPath = api.paths['/{userId}'].post | ||
t.strictSame(definedPath.parameters[0].schema, { | ||
type: 'object', | ||
properties: { | ||
bio: { | ||
type: 'string' | ||
} | ||
} | ||
}) | ||
t.strictSame(definedPath.parameters[1], { | ||
in: 'path', | ||
name: 'userId', | ||
type: 'string', | ||
required: true | ||
}) | ||
}) | ||
test('avoid overwriting params when schema.params is provided', async t => { | ||
const opt = { | ||
schema: { | ||
params: { | ||
type: 'object', | ||
properties: { | ||
id: { | ||
type: 'string' | ||
} | ||
} | ||
}, | ||
body: { | ||
type: 'object', | ||
properties: { | ||
bio: { | ||
type: 'string' | ||
} | ||
} | ||
} | ||
} | ||
} | ||
const fastify = Fastify() | ||
await fastify.register(fastifySwagger) | ||
fastify.post('/:userId', opt, () => { }) | ||
await fastify.ready() | ||
const swaggerObject = fastify.swagger() | ||
const definedPath = swaggerObject.paths['/{userId}'].post | ||
t.strictSame(definedPath.parameters[0].schema, { | ||
type: 'object', | ||
properties: { | ||
bio: { | ||
type: 'string' | ||
} | ||
} | ||
}) | ||
t.strictSame(definedPath.parameters[1], { | ||
in: 'path', | ||
name: 'id', | ||
type: 'string', | ||
required: true | ||
}) | ||
}) |
131
test/util.js
@@ -5,2 +5,4 @@ 'use strict' | ||
const { formatParamUrl } = require('../lib/util/format-param-url') | ||
const { hasParams, matchParams } = require('../lib/util/match-params') | ||
const { generateParamsSchema, paramName } = require('../lib/util/generate-params-schema') | ||
@@ -29,1 +31,130 @@ const cases = [ | ||
}) | ||
test('hasParams function', (t) => { | ||
t.test('should return false for empty url', (t) => { | ||
const url = '' | ||
const result = hasParams(url) | ||
t.equal(result, false) | ||
t.end() | ||
}) | ||
t.test('should return true for url with parameters', (t) => { | ||
const url = '/example/{userId}' | ||
const result = hasParams(url) | ||
t.equal(result, true) | ||
t.end() | ||
}) | ||
t.test('should return true for url with multiple parameters', (t) => { | ||
const url = '/example/{userId}/{secretToken}' | ||
const result = hasParams(url) | ||
t.equal(result, true) | ||
t.end() | ||
}) | ||
t.test('should return false for url without parameters', (t) => { | ||
const url = '/example/path' | ||
const result = hasParams(url) | ||
t.equal(result, false) | ||
t.end() | ||
}) | ||
t.end() | ||
}) | ||
test('matchParams function', (t) => { | ||
t.test('should return an empty array for empty url', (t) => { | ||
const url = '' | ||
const result = matchParams(url) | ||
t.same(result, []) | ||
t.end() | ||
}) | ||
t.test('should return an array of matched parameters', (t) => { | ||
const url = '/example/{userId}/{secretToken}' | ||
const result = matchParams(url) | ||
t.same(result, ['{userId}', '{secretToken}']) | ||
t.end() | ||
}) | ||
t.test('should return an empty array for url without parameters', (t) => { | ||
const url = '/example/path' | ||
const result = matchParams(url) | ||
t.same(result, []) | ||
t.end() | ||
}) | ||
t.end() | ||
}) | ||
const urlsToShemas = [ | ||
[ | ||
'/example/{userId}', { | ||
params: { | ||
type: 'object', | ||
properties: { | ||
userId: { | ||
type: 'string' | ||
} | ||
} | ||
} | ||
} | ||
], | ||
[ | ||
'/example/{userId}/{secretToken}', { | ||
params: { | ||
type: 'object', | ||
properties: { | ||
userId: { | ||
type: 'string' | ||
}, | ||
secretToken: { | ||
type: 'string' | ||
} | ||
} | ||
} | ||
} | ||
], | ||
[ | ||
'/example/near/{lat}-{lng}', { | ||
params: { | ||
type: 'object', | ||
properties: { | ||
lat: { | ||
type: 'string' | ||
}, | ||
lng: { | ||
type: 'string' | ||
} | ||
} | ||
} | ||
} | ||
] | ||
] | ||
test('generateParamsSchema function', (t) => { | ||
t.plan(urlsToShemas.length) | ||
for (const [url, expectedSchema] of urlsToShemas) { | ||
const result = generateParamsSchema(url) | ||
t.same(result, expectedSchema) | ||
} | ||
}) | ||
test('paramName function', (t) => { | ||
t.test('should return the captured value from the param', (t) => { | ||
const param = '{userId}' | ||
const result = paramName(param) | ||
t.equal(result, 'userId') | ||
t.end() | ||
}) | ||
t.test('should return the same value if there are no captures', (t) => { | ||
const param = 'userId' | ||
const result = paramName(param) | ||
t.equal(result, 'userId') | ||
t.end() | ||
}) | ||
t.end() | ||
}) |
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
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
277006
60
8035
1108