From fe779d2e4ee2116882db7d1691eec78860f43bef Mon Sep 17 00:00:00 2001 From: "Fawzi E. Abdulfattah" Date: Mon, 17 Oct 2022 10:19:32 +0200 Subject: [PATCH] feat: rendering the syntax of different content types responses (#665) * Supporting different content types responses Signed-off-by: iifawzi * updating the docs Signed-off-by: iifawzi * Refactoring for-loop and update the tests Signed-off-by: iifawzi * update package.json to use repo Signed-off-by: iifawzi * update the rendering logic according to fastify changes Signed-off-by: iifawzi * update the ducktyping Signed-off-by: iifawzi * Apply suggestions from code review Co-authored-by: Uzlopak * revert fastify deleted by mistake Signed-off-by: iifawzi * Update test/spec/openapi/schema.js Co-authored-by: Uzlopak * Using strictSame and temporary updat packagejson to check ci Signed-off-by: iifawzi * update deps Signed-off-by: iifawzi Signed-off-by: iifawzi Co-authored-by: Uzlopak --- README.md | 29 ++++++++++++ lib/spec/openapi/utils.js | 25 ++++++----- test/spec/openapi/schema.js | 90 +++++++++++++++++++++++++++++++++++++ 3 files changed, 134 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 5a0732f6..3ce2895f 100644 --- a/README.md +++ b/README.md @@ -430,6 +430,35 @@ You can decorate your own response headers by following the below example: Note: You need to specify `type` property when you decorate the response headers, otherwise the schema will be modified by Fastify. +##### Different content types responses +**Note:** not supported by Swagger (OpenAPI v2), [only OpenAPI v3](https://swagger.io/docs/specification/describing-responses/) +Different content types responses are supported by `@fastify/swagger` and `@fastify`. +Please use `content` for the response otherwise Fastify itself will fail to compile the schema: + +```js +{ + response: { + 200: { + description: 'Description and all status-code based properties are working', + content: { + 'application/json': { + schema: { + name: { type: 'string' }, + image: { type: 'string' }, + address: { type: 'string' } + } + }, + 'application/vnd.v1+json': { + schema: { + fullName: { type: 'string' }, + phone: { type: 'string' } + } + } + } + } + } +} +``` ##### Empty Body Responses Empty body responses are supported by `@fastify/swagger`. Please specify `type: 'null'` for the response otherwise Fastify itself will fail to compile the schema: diff --git a/lib/spec/openapi/utils.js b/lib/spec/openapi/utils.js index 5583022d..8242d402 100644 --- a/lib/spec/openapi/utils.js +++ b/lib/spec/openapi/utils.js @@ -323,20 +323,25 @@ function resolveResponse (fastifyResponseJson, produces, ref) { // add schema when type is not 'null' if (rawJsonSchema.type !== 'null') { - const content = {} + if (resolved.content && resolved.content[Object.keys(resolved.content)[0]].schema) { + response.content = resolved.content + } else { + const content = {} - if ((Array.isArray(produces) && produces.length === 0) || typeof produces === 'undefined') { - produces = ['application/json'] - } + if ((Array.isArray(produces) && produces.length === 0) || typeof produces === 'undefined') { + produces = ['application/json'] + } - delete resolved[xResponseDescription] + delete resolved[xResponseDescription] - const media = schemaToMedia(resolved) - produces.forEach((produce) => { - content[produce] = media - }) + const media = schemaToMedia(resolved) - response.content = content + for (const produce of produces) { + content[produce] = media + } + + response.content = content + } } responsesContainer[statusCode] = response diff --git a/test/spec/openapi/schema.js b/test/spec/openapi/schema.js index 0912071e..fae7b288 100644 --- a/test/spec/openapi/schema.js +++ b/test/spec/openapi/schema.js @@ -108,6 +108,96 @@ test('support 2xx response', async t => { t.same(definedPath.responses['3XX'].description, 'Default Response') }) +test('support multiple content types as response', async t => { + const fastify = Fastify() + await fastify.register(fastifySwagger, { + openapi: true, + routePrefix: '/docs', + exposeRoute: true + }) + + const opt = { + schema: { + response: { + 200: { + description: 'Description and all status-code based properties are working', + content: { + 'application/json': { + schema: { + name: { type: 'string' }, + image: { type: 'string' }, + address: { type: 'string' } + } + }, + 'application/vnd.v1+json': { + schema: { + fullName: { type: 'string' }, + phone: { type: 'string' } + } + } + } + }, + '4xx': { + type: 'object', + properties: { + name: { type: 'string' } + } + }, + 300: { + age: { type: 'number' } + } + } + } + } + fastify.get('/', opt, () => {}) + + await fastify.ready() + + const swaggerObject = fastify.swagger() + const api = await Swagger.validate(swaggerObject) + const definedPath = api.paths['/'].get + t.same(definedPath.responses['200'].description, 'Description and all status-code based properties are working') + t.strictSame(definedPath.responses['200'].content, { + 'application/json': { + schema: { + type: 'object', + properties: { + name: { type: 'string' }, image: { type: 'string' }, address: { type: 'string' } + } + } + }, + 'application/vnd.v1+json': { + schema: { + type: 'object', + properties: { + fullName: { type: 'string' }, phone: { type: 'string' } + } + } + } + }) + t.same(definedPath.responses['4XX'].description, 'Default Response') + t.strictSame(definedPath.responses['4XX'].content, { + 'application/json': { + schema: { + type: 'object', + properties: { + name: { type: 'string' } + } + } + } + }) + t.strictSame(definedPath.responses[300].content, { + 'application/json': { + schema: { + type: 'object', + properties: { + age: { type: 'number' } + } + } + } + }) +}) + test('support status code 204', async t => { const opt = { schema: {