diff --git a/package.json b/package.json index 221100d..5551310 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "test": "NODE_ENV=test mocha --exit" }, "dependencies": { - "@asteasolutions/zod-to-openapi": "^4.8.0" + "@asteasolutions/zod-to-openapi": "^5.5.0" }, "peerDependencies": { "express": "^5.0.0-beta.1", diff --git a/src/openAPI.test.ts b/src/openAPI.test.ts index c0fdd04..094af6e 100644 --- a/src/openAPI.test.ts +++ b/src/openAPI.test.ts @@ -92,7 +92,7 @@ describe("buildOpenAPIDocument", () => { const document = buildOpenAPIDocument({ config, routers, schemaPaths, errors, openApiVersion }); expect(document.paths).to.have.property("/test"); - expect(document.paths["/test"]).to.have.property("get"); + expect(document.paths!["/test"]).to.have.property("get"); }); it("should include error responses if defined", () => { @@ -160,8 +160,8 @@ describe("buildOpenAPIDocument", () => { const errors = { 401: "Unauthorized", 403: "Forbidden" }; const document = buildOpenAPIDocument({ config, routers, schemaPaths, errors, openApiVersion }); - const method = document.paths["/test"].get; - const responseSchema = method!.responses["200"].content["application/json"].schema; + const method = document.paths!["/test"].get; + const responseSchema = method!.responses!["200"].content["application/json"].schema; expect(responseSchema.$ref.includes("ResponseSchema")).to.be.true; }); @@ -186,7 +186,7 @@ describe("buildOpenAPIDocument", () => { const errors = { 401: "Unauthorized", 403: "Forbidden" }; const document = buildOpenAPIDocument({ config, routers, schemaPaths, errors, openApiVersion }); - const method = document.paths["/test"].get; + const method = document.paths!["/test"].get; // @ts-ignore const requestBodySchema = method!.requestBody?.content["application/json"].schema; diff --git a/src/openAPI.ts b/src/openAPI.ts index 6481f02..bb42fa5 100644 --- a/src/openAPI.ts +++ b/src/openAPI.ts @@ -1,10 +1,12 @@ import { extendZodWithOpenApi, - OpenAPIGenerator, + OpenApiGeneratorV3, + OpenApiGeneratorV31, OpenAPIRegistry, ResponseConfig, RouteConfig, } from "@asteasolutions/zod-to-openapi"; +import { OpenApiVersion } from "@asteasolutions/zod-to-openapi/dist/openapi-generator"; import { RequestHandler, Router } from "express"; import type { ComponentsObject } from "openapi3-ts/oas30"; import { z, ZodArray, ZodEffects, ZodObject } from "zod"; @@ -13,19 +15,19 @@ import { ErrorResponse } from "./schemas"; extendZodWithOpenApi(z); -export type OpenAPIDocument = ReturnType; -export type OpenAPIComponents = ReturnType; -export type OpenAPIConfig = Parameters[0]; -export type OpenApiVersion = ConstructorParameters[1]; +export type OpenAPIDocument = ReturnType; +export type OpenAPIV31Document = ReturnType; +export type OpenAPIComponents = ReturnType; +export type OpenAPIConfig = Parameters[0]; export function buildOpenAPIDocument(args: { - config: OpenAPIConfig; + config: Omit; routers: Router[]; schemaPaths: string[]; errors: { 401?: string; 403?: string }; securitySchemes?: ComponentsObject["securitySchemes"]; openApiVersion: OpenApiVersion; -}): OpenAPIDocument { +}): OpenAPIDocument | OpenAPIV31Document { const { config, routers, schemaPaths, securitySchemes, errors, openApiVersion } = args; const registry = new OpenAPIRegistry(); // Attach all of the Zod schemas to the OpenAPI specification @@ -189,8 +191,11 @@ export function buildOpenAPIDocument(args: { registry.registerPath(openapiRouteConfig); }); - const generator = new OpenAPIGenerator(registry.definitions, openApiVersion); - const openapiJSON = generator.generateDocument(config); + const generator = + openApiVersion === "3.1.0" + ? new OpenApiGeneratorV31(registry.definitions) + : new OpenApiGeneratorV3(registry.definitions); + const openapiJSON = generator.generateDocument({ ...config, openapi: openApiVersion }); // Attach the security schemes provided if (securitySchemes) { @@ -200,17 +205,19 @@ export function buildOpenAPIDocument(args: { // Verify that none of the "parameters" are appearing as optional, which is invalid // in the official OpenAPI spec and unsupported by readme.io - for (const [route, impl] of Object.entries(openapiJSON.paths)) { - for (const key of Object.keys(impl)) { - const method = key as keyof typeof impl; - for (const param of impl[method].parameters || []) { - if (param.required === false && param.in === "path") { - param.required = true; - console.warn( - `OpenAPI Warning: The route ${route} has an optional parameter ${param.name} in the path. ` + - `Optional parameters in the route path are not supported by readme.io. Make the parameter required ` + - `or split the route definition into two separate ones, one with the param and one without.`, - ); + if (openapiJSON.paths) { + for (const [route, impl] of Object.entries(openapiJSON.paths)) { + for (const key of Object.keys(impl)) { + const method = key as keyof typeof impl; + for (const param of impl[method].parameters || []) { + if (param.required === false && param.in === "path") { + param.required = true; + console.warn( + `OpenAPI Warning: The route ${route} has an optional parameter ${param.name} in the path. ` + + `Optional parameters in the route path are not supported by readme.io. Make the parameter required ` + + `or split the route definition into two separate ones, one with the param and one without.`, + ); + } } } } diff --git a/yarn.lock b/yarn.lock index fa3c167..f71025e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,10 +2,10 @@ # yarn lockfile v1 -"@asteasolutions/zod-to-openapi@^4.8.0": - version "4.8.0" - resolved "https://registry.yarnpkg.com/@asteasolutions/zod-to-openapi/-/zod-to-openapi-4.8.0.tgz#34c29228c5cd0098a534b3536c9f486fafca2cc1" - integrity sha512-FQlBeHIhSoEhjddAWD1v2zbeBESfTdaAVQuUr0RY9t0rN8ogoEaGirv2Ne8kQQTAVGydJzUMAOIlPULEOQDoeA== +"@asteasolutions/zod-to-openapi@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@asteasolutions/zod-to-openapi/-/zod-to-openapi-5.5.0.tgz#f3be8f1e97ece0fcef2e6ff5e9230da8138d20a6" + integrity sha512-d5HwrvM6dOKr3XdeF+DmashGvfEc+1oiEfbscugsiwSTrFtuMa7ETpW9sTNnVgn+hJaz+PRxPQUYD7q9/5dUig== dependencies: openapi3-ts "^4.1.2"