diff --git a/lib/spec/openapi/utils.js b/lib/spec/openapi/utils.js index 4d617e6d..542c0da0 100644 --- a/lib/spec/openapi/utils.js +++ b/lib/spec/openapi/utils.js @@ -549,6 +549,27 @@ function convertJsonSchemaToOpenapi3 (opts, jsonSchema) { continue } + if (key === 'type') { + if (value === 'null') { + openapiSchema.nullable = true + delete openapiSchema.type + continue + } + if (Array.isArray(value) && value.includes('null')) { + openapiSchema.nullable = true + const remainingTypes = value.filter(t => t !== 'null') + if (remainingTypes.length === 1) { + openapiSchema.type = remainingTypes[0] + } else if (remainingTypes.length > 1) { + openapiSchema.anyOf = remainingTypes.map(t => ({ type: t })) + delete openapiSchema.type + } else { + delete openapiSchema.type + } + continue + } + } + if (key === 'contentEncoding') { if (value === 'binary') { openapiSchema.format = 'binary' diff --git a/test/spec/openapi/schema.test.js b/test/spec/openapi/schema.test.js index 471d59eb..4bdbe8a4 100644 --- a/test/spec/openapi/schema.test.js +++ b/test/spec/openapi/schema.test.js @@ -2018,3 +2018,46 @@ test('support callbacks', async () => { t.assert.strictEqual(definedPath.responses['201'].content['application/json'].schema.headers, undefined) }) }) + +test('support type null and array types with null in response schema (OpenAPI 3.0 nullable conversion)', async t => { + const opt = { + schema: { + response: { + 200: { + type: 'object', + properties: { + dataNull: { + type: 'null' + }, + dataStringNull: { + type: ['string', 'null'] + }, + dataMultiNull: { + type: ['string', 'number', 'null'] + }, + dataOnlyNullArray: { + type: ['null'] + } + } + } + } + } + } + + const fastify = Fastify() + await fastify.register(fastifySwagger, { + openapi: true + }) + fastify.get('/', opt, () => {}) + await fastify.ready() + + const swaggerObject = fastify.swagger() + const api = await Swagger.validate(swaggerObject) + + const definedPath = api.paths['/'].get + const props = definedPath.responses['200'].content['application/json'].schema.properties + t.assert.deepStrictEqual(props.dataNull, { nullable: true }) + t.assert.deepStrictEqual(props.dataStringNull, { type: 'string', nullable: true }) + t.assert.deepStrictEqual(props.dataMultiNull, { anyOf: [{ type: 'string' }, { type: 'number' }], nullable: true }) + t.assert.deepStrictEqual(props.dataOnlyNullArray, { nullable: true }) +})