diff --git a/packages/openapi-generator/src/openapi.ts b/packages/openapi-generator/src/openapi.ts index 6aa92361..87f236e9 100644 --- a/packages/openapi-generator/src/openapi.ts +++ b/packages/openapi-generator/src/openapi.ts @@ -75,7 +75,14 @@ function schemaToOpenAPI( if (oneOf.length === 0) { return undefined; } else if (oneOf.length === 1) { - return { ...(nullable ? { nullable } : {}), ...oneOf[0] }; + if ( + Object.keys( + oneOf[0] as OpenAPIV3.SchemaObject | OpenAPIV3.ReferenceObject, + )[0] === '$ref' + ) + // OpenAPI spec doesn't allow $ref properties to have siblings, so they're wrapped in an 'allOf' array + return { ...(nullable ? { nullable } : {}), allOf: oneOf }; + else return { ...(nullable ? { nullable } : {}), ...oneOf[0] }; } else { return { ...(nullable ? { nullable } : {}), oneOf }; } diff --git a/packages/openapi-generator/test/openapi.test.ts b/packages/openapi-generator/test/openapi.test.ts index bd12b7b5..150d4afc 100644 --- a/packages/openapi-generator/test/openapi.test.ts +++ b/packages/openapi-generator/test/openapi.test.ts @@ -735,6 +735,80 @@ testCase('request body double ref', SCHEMA_DOUBLE_REF, { }, }); +const SCHEMA_NULLABLE_REF = ` +import * as t from 'io-ts'; +import * as h from '@api-ts/io-ts-http'; + +export const route = h.httpRoute({ + path: '/foo', + method: 'GET', + request: t.type({ + body: t.union([Foo, t.null]), + }), + response: { + /** foo response */ + 200: t.string + }, +}); + +const Foo = t.type({ foo: t.string }); +`; + +testCase('request body nullable ref', SCHEMA_NULLABLE_REF, { + openapi: '3.0.3', + info: { + title: 'Test', + version: '1.0.0', + }, + paths: { + '/foo': { + get: { + parameters: [], + requestBody: { + content: { + 'application/json': { + schema: { + nullable: true, + allOf: [ + { + $ref: '#/components/schemas/Foo', + }, + ], + }, + }, + }, + }, + responses: { + 200: { + description: 'foo response', + content: { + 'application/json': { + schema: { + type: 'string', + }, + }, + }, + }, + }, + }, + }, + }, + components: { + schemas: { + Foo: { + title: 'Foo', + type: 'object', + properties: { + foo: { + type: 'string', + }, + }, + required: ['foo'], + }, + }, + }, +}); + const TITLE_TAG = ` import * as t from 'io-ts'; import * as h from '@api-ts/io-ts-http';