From 0566561b73b94360279306f76f455eed5e760aa5 Mon Sep 17 00:00:00 2001 From: Patrick McLaughlin Date: Tue, 5 Sep 2023 10:25:25 -0400 Subject: [PATCH] feat: output OpenAPI v3 instead of v3.1 --- packages/openapi-generator/src/openapi.ts | 34 +++++++++++++------ .../openapi-generator/test/openapi.test.ts | 6 ++-- 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/packages/openapi-generator/src/openapi.ts b/packages/openapi-generator/src/openapi.ts index a934e39f..1f840134 100644 --- a/packages/openapi-generator/src/openapi.ts +++ b/packages/openapi-generator/src/openapi.ts @@ -1,4 +1,4 @@ -import { OpenAPIV3_1 } from 'openapi-types'; +import { OpenAPIV3 } from 'openapi-types'; import { STATUS_CODES } from 'http'; import { parseCommentBlock } from './jsdoc'; @@ -8,12 +8,24 @@ import type { Schema } from './ir'; function schemaToOpenAPI( schema: Schema, -): OpenAPIV3_1.SchemaObject | OpenAPIV3_1.ReferenceObject | undefined { +): OpenAPIV3.SchemaObject | OpenAPIV3.ReferenceObject | undefined { switch (schema.type) { case 'primitive': - return { type: schema.value }; + if (schema.value === 'integer') { + return { type: 'number' }; + } else if (schema.value === 'null') { + // TODO: OpenAPI v3 does not have an explicit null type, is there a better way to represent this? + // Or should we just conflate explicit null and undefined properties? + return { nullable: true, enum: [] }; + } else { + return { type: schema.value }; + } case 'literal': - return { type: schema.kind, enum: [schema.value] }; + if (schema.kind === 'null') { + return { nullable: true, enum: [] }; + } else { + return { type: schema.kind, enum: [schema.value] }; + } case 'ref': return { $ref: `#/components/schemas/${schema.name}` }; case 'array': @@ -34,7 +46,7 @@ function schemaToOpenAPI( return { ...acc, [name]: innerSchema }; }, - {} as Record, + {} as Record, ), required: schema.required, }; @@ -76,7 +88,7 @@ function schemaToOpenAPI( } } -function routeToOpenAPI(route: Route): [string, string, OpenAPIV3_1.OperationObject] { +function routeToOpenAPI(route: Route): [string, string, OpenAPIV3.OperationObject] { const jsdoc = route.comment !== undefined ? parseCommentBlock(route.comment) : {}; const operationId = jsdoc.tags?.operationId; const tag = jsdoc.tags?.tag ?? ''; @@ -136,10 +148,10 @@ function routeToOpenAPI(route: Route): [string, string, OpenAPIV3_1.OperationObj } export function convertRoutesToOpenAPI( - info: OpenAPIV3_1.InfoObject, + info: OpenAPIV3.InfoObject, routes: Route[], schemas: Components, -): OpenAPIV3_1.Document { +): OpenAPIV3.Document { const paths = routes.reduce( (acc, route) => { const [path, method, pathItem] = routeToOpenAPI(route); @@ -147,7 +159,7 @@ export function convertRoutesToOpenAPI( pathObject[method] = pathItem; return { ...acc, [path]: pathObject }; }, - {} as Record>, + {} as Record>, ); const openapiSchemas = Object.entries(schemas).reduce( @@ -159,11 +171,11 @@ export function convertRoutesToOpenAPI( return { ...acc, [name]: openapiSchema }; } }, - {} as Record, + {} as Record, ); return { - openapi: '3.1.0', + openapi: '3.0.0', info, paths, components: { diff --git a/packages/openapi-generator/test/openapi.test.ts b/packages/openapi-generator/test/openapi.test.ts index bd54293b..43b93625 100644 --- a/packages/openapi-generator/test/openapi.test.ts +++ b/packages/openapi-generator/test/openapi.test.ts @@ -107,7 +107,7 @@ export const internalRoute = h.httpRoute({ `; testCase('simple route', SIMPLE, { - openapi: '3.1.0', + openapi: '3.0.0', info: { title: 'Test', version: '1.0.0', @@ -205,7 +205,7 @@ export const route = h.httpRoute({ `; testCase('request body route', REQUEST_BODY, { - openapi: '3.1.0', + openapi: '3.0.0', info: { title: 'Test', version: '1.0.0', @@ -276,7 +276,7 @@ export const route = h.httpRoute({ `; testCase('request union route', UNION, { - openapi: '3.1.0', + openapi: '3.0.0', info: { title: 'Test', version: '1.0.0',