From a105eb6db021c4e2434307ee669f186890e246f1 Mon Sep 17 00:00:00 2001 From: Patrick McLaughlin Date: Fri, 23 Feb 2024 11:43:02 -0500 Subject: [PATCH 1/2] fix: use `allOf` for schemas that are refs --- packages/openapi-generator/src/openapi.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/openapi-generator/src/openapi.ts b/packages/openapi-generator/src/openapi.ts index 5a75d736..06bad4cf 100644 --- a/packages/openapi-generator/src/openapi.ts +++ b/packages/openapi-generator/src/openapi.ts @@ -173,6 +173,13 @@ export function convertRoutesToOpenAPI( const openapiSchema = schemaToOpenAPI(schema); if (openapiSchema === undefined) { return acc; + } else if ('$ref' in openapiSchema) { + return { + ...acc, + [name]: { + allOf: [{ title: name }, openapiSchema], + }, + }; } else { return { ...acc, From 0321fa93a061cd4343be576501218cc25ff1b18c Mon Sep 17 00:00:00 2001 From: Patrick McLaughlin Date: Fri, 23 Feb 2024 12:05:11 -0500 Subject: [PATCH 2/2] chore: add test for schema ref --- .../openapi-generator/test/openapi.test.ts | 149 +++++++++++++++++- 1 file changed, 147 insertions(+), 2 deletions(-) diff --git a/packages/openapi-generator/test/openapi.test.ts b/packages/openapi-generator/test/openapi.test.ts index 93db5a90..995f3425 100644 --- a/packages/openapi-generator/test/openapi.test.ts +++ b/packages/openapi-generator/test/openapi.test.ts @@ -10,6 +10,7 @@ import { parseRoute, Project, type Route, + type Schema, } from '../src'; async function testCase( @@ -23,6 +24,7 @@ async function testCase( const project = new Project(); const routes: Route[] = []; + const schemas: Record = {}; const errors: string[] = []; for (const symbol of sourceFile.symbols.declarations) { if (symbol.init !== undefined) { @@ -36,7 +38,7 @@ async function testCase( } const result = parseRoute(project, routeSchemaE.right); if (E.isLeft(result)) { - errors.push(result.left); + schemas[symbol.name] = routeSchemaE.right; } else { routes.push(result.right); } @@ -46,7 +48,7 @@ async function testCase( const actual = convertRoutesToOpenAPI( { title: 'Test', version: '1.0.0' }, routes, - {}, + schemas, ); assert.deepStrictEqual(errors, expectedErrors); @@ -538,3 +540,146 @@ testCase('object with no required properties', EMPTY_REQUIRED, { schemas: {}, }, }); + +const SCHEMA_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: Foo, + }), + response: { + /** foo response */ + 200: t.string + }, +}); + +const Foo = t.type({ foo: t.string }); +`; + +testCase('request body ref', SCHEMA_REF, { + openapi: '3.0.0', + info: { + title: 'Test', + version: '1.0.0', + }, + paths: { + '/foo': { + get: { + parameters: [], + requestBody: { + content: { + 'application/json': { + schema: { + $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 SCHEMA_DOUBLE_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: Bar, + }), + response: { + /** foo response */ + 200: t.string + }, +}); + +const Foo = t.type({ foo: t.string }); + +const Bar = Foo; +`; + +testCase('request body double ref', SCHEMA_DOUBLE_REF, { + openapi: '3.0.0', + info: { + title: 'Test', + version: '1.0.0', + }, + paths: { + '/foo': { + get: { + parameters: [], + requestBody: { + content: { + 'application/json': { + schema: { + $ref: '#/components/schemas/Bar', + }, + }, + }, + }, + responses: { + 200: { + description: 'foo response', + content: { + 'application/json': { + schema: { + type: 'string', + }, + }, + }, + }, + }, + }, + }, + }, + components: { + schemas: { + Foo: { + title: 'Foo', + type: 'object', + properties: { + foo: { + type: 'string', + }, + }, + required: ['foo'], + }, + Bar: { + allOf: [{ title: 'Bar' }, { $ref: '#/components/schemas/Foo' }], + }, + }, + }, +});