From 04dd1b549118c8b8e5a3b86f6dbed741f44770c8 Mon Sep 17 00:00:00 2001 From: Matthew Jacobs Date: Sat, 24 Feb 2024 21:18:37 -0500 Subject: [PATCH 01/15] fix: bug with exportAllNamedSchemas where schemas will reuse wrong name --- .changeset/smooth-impalas-behave.md | 5 +++++ lib/src/CodeMeta.ts | 2 +- .../getZodiosEndpointDefinitionList.test.ts | 4 +++- lib/src/getZodiosEndpointDefinitionList.ts | 14 ++++++++++---- lib/tests/export-all-named-schemas.test.ts | 18 ++++++++++++++++++ 5 files changed, 37 insertions(+), 6 deletions(-) create mode 100644 .changeset/smooth-impalas-behave.md diff --git a/.changeset/smooth-impalas-behave.md b/.changeset/smooth-impalas-behave.md new file mode 100644 index 00000000..ea695909 --- /dev/null +++ b/.changeset/smooth-impalas-behave.md @@ -0,0 +1,5 @@ +--- +"openapi-zod-client": patch +--- + +Fix bug with `exportAllNamedSchemas` option where schemas will reuse last schema name with matching schema rather than it's own name that has already been used before. diff --git a/lib/src/CodeMeta.ts b/lib/src/CodeMeta.ts index ed17305a..9f6c722a 100644 --- a/lib/src/CodeMeta.ts +++ b/lib/src/CodeMeta.ts @@ -7,7 +7,7 @@ import { getSchemaComplexity } from "./schema-complexity"; export type ConversionTypeContext = { resolver: DocumentResolver; zodSchemaByName: Record; - schemaByName: Record; + schemaByName: Record; }; export type CodeMetaData = { diff --git a/lib/src/getZodiosEndpointDefinitionList.test.ts b/lib/src/getZodiosEndpointDefinitionList.test.ts index 3e12d2a0..565868e9 100644 --- a/lib/src/getZodiosEndpointDefinitionList.test.ts +++ b/lib/src/getZodiosEndpointDefinitionList.test.ts @@ -439,7 +439,9 @@ test("getZodiosEndpointDefinitionList /pet without schema ref", () => { "resolveSchemaName": [Function], }, "schemaByName": { - "Pet.and(Reason)": "updatePet_Body", + "Pet.and(Reason)": [ + "updatePet_Body", + ], }, "zodSchemaByName": { "Category": "z.object({ id: z.number().int(), name: z.string() }).partial().passthrough()", diff --git a/lib/src/getZodiosEndpointDefinitionList.ts b/lib/src/getZodiosEndpointDefinitionList.ts index 5785851d..6509f1d6 100644 --- a/lib/src/getZodiosEndpointDefinitionList.ts +++ b/lib/src/getZodiosEndpointDefinitionList.ts @@ -84,7 +84,7 @@ export const getZodiosEndpointDefinitionList = (doc: OpenAPIObject, options?: Te // if schema is already assigned to a variable, re-use that variable name if (!options?.exportAllNamedSchemas && ctx.schemaByName[result]) { - return ctx.schemaByName[result]!; + return ctx.schemaByName[result]![0]!; } // result is complex and would benefit from being re-used @@ -95,8 +95,8 @@ export const getZodiosEndpointDefinitionList = (doc: OpenAPIObject, options?: Te let isVarNameAlreadyUsed = false; while ((isVarNameAlreadyUsed = Boolean(ctx.zodSchemaByName[formatedName]))) { if (isVarNameAlreadyUsed) { - if (options?.exportAllNamedSchemas && ctx.schemaByName[result]) { - return ctx.schemaByName[result]!; + if (options?.exportAllNamedSchemas && ctx.schemaByName[result]?.includes(formatedName)) { + return formatedName; } else if (ctx.zodSchemaByName[formatedName] === safeName) { return formatedName; } else { @@ -107,7 +107,13 @@ export const getZodiosEndpointDefinitionList = (doc: OpenAPIObject, options?: Te } ctx.zodSchemaByName[formatedName] = result; - ctx.schemaByName[result] = formatedName; + + if (options?.exportAllNamedSchemas) { + ctx.schemaByName[result] = (ctx.schemaByName[result] ?? []).concat(formatedName); + } else { + ctx.schemaByName[result] = [formatedName]; + } + return formatedName; } diff --git a/lib/tests/export-all-named-schemas.test.ts b/lib/tests/export-all-named-schemas.test.ts index 079df9f9..833869fc 100644 --- a/lib/tests/export-all-named-schemas.test.ts +++ b/lib/tests/export-all-named-schemas.test.ts @@ -58,6 +58,11 @@ test("export-all-named-schemas", async () => { }, }, parameters: [ + { + name: "sameSchemaDifferentName", + in: "query", + schema: { type: "string", enum: ["xxx", "yyy", "zzz"] }, + }, { name: "sameSchemaSameName", in: "query", @@ -128,6 +133,11 @@ test("export-all-named-schemas", async () => { "errors": [], "method": "delete", "parameters": [ + { + "name": "sameSchemaDifferentName", + "schema": "sameSchemaDifferentName", + "type": "Query", + }, { "name": "sameSchemaSameName", "schema": "sameSchemaSameName", @@ -160,6 +170,7 @@ test("export-all-named-schemas", async () => { "withAlias": false, }, "schemas": { + "sameSchemaDifferentName": "z.enum(["xxx", "yyy", "zzz"]).optional()", "sameSchemaSameName": "z.enum(["xxx", "yyy", "zzz"]).optional()", "schemaNameAlreadyUsed": "z.enum(["aaa", "bbb", "ccc"]).optional()", "schemaNameAlreadyUsed__2": "z.enum(["ggg", "hhh", "iii"]).optional()", @@ -180,11 +191,13 @@ test("export-all-named-schemas", async () => { const sameSchemaSameName = z.enum(["xxx", "yyy", "zzz"]).optional(); const schemaNameAlreadyUsed = z.enum(["aaa", "bbb", "ccc"]).optional(); + const sameSchemaDifferentName = z.enum(["xxx", "yyy", "zzz"]).optional(); const schemaNameAlreadyUsed__2 = z.enum(["ggg", "hhh", "iii"]).optional(); export const schemas = { sameSchemaSameName, schemaNameAlreadyUsed, + sameSchemaDifferentName, schemaNameAlreadyUsed__2, }; @@ -220,6 +233,11 @@ test("export-all-named-schemas", async () => { path: "/export-all-named-schemas", requestFormat: "json", parameters: [ + { + name: "sameSchemaDifferentName", + type: "Query", + schema: sameSchemaDifferentName, + }, { name: "sameSchemaSameName", type: "Query", From 989320eccd90e9f777832c531fc03340f8b3e7a9 Mon Sep 17 00:00:00 2001 From: Matthew Jacobs Date: Sat, 24 Feb 2024 21:27:01 -0500 Subject: [PATCH 02/15] fix: use optional property rather than change existing property --- lib/src/CodeMeta.ts | 3 ++- .../getZodiosEndpointDefinitionList.test.ts | 4 +--- lib/src/getZodiosEndpointDefinitionList.ts | 21 ++++++++++++------- 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/lib/src/CodeMeta.ts b/lib/src/CodeMeta.ts index 9f6c722a..6c489655 100644 --- a/lib/src/CodeMeta.ts +++ b/lib/src/CodeMeta.ts @@ -7,7 +7,8 @@ import { getSchemaComplexity } from "./schema-complexity"; export type ConversionTypeContext = { resolver: DocumentResolver; zodSchemaByName: Record; - schemaByName: Record; + schemaByName: Record; + schemasByName?: Record; }; export type CodeMetaData = { diff --git a/lib/src/getZodiosEndpointDefinitionList.test.ts b/lib/src/getZodiosEndpointDefinitionList.test.ts index 565868e9..3e12d2a0 100644 --- a/lib/src/getZodiosEndpointDefinitionList.test.ts +++ b/lib/src/getZodiosEndpointDefinitionList.test.ts @@ -439,9 +439,7 @@ test("getZodiosEndpointDefinitionList /pet without schema ref", () => { "resolveSchemaName": [Function], }, "schemaByName": { - "Pet.and(Reason)": [ - "updatePet_Body", - ], + "Pet.and(Reason)": "updatePet_Body", }, "zodSchemaByName": { "Category": "z.object({ id: z.number().int(), name: z.string() }).partial().passthrough()", diff --git a/lib/src/getZodiosEndpointDefinitionList.ts b/lib/src/getZodiosEndpointDefinitionList.ts index 6509f1d6..40dbbe75 100644 --- a/lib/src/getZodiosEndpointDefinitionList.ts +++ b/lib/src/getZodiosEndpointDefinitionList.ts @@ -65,6 +65,10 @@ export const getZodiosEndpointDefinitionList = (doc: OpenAPIObject, options?: Te .otherwise((fn) => fn); const ctx: ConversionTypeContext = { resolver, zodSchemaByName: {}, schemaByName: {} }; + if (options?.exportAllNamedSchemas) { + ctx.schemasByName = {}; + } + const complexityThreshold = options?.complexityThreshold ?? 4; const getZodVarName = (input: CodeMeta, fallbackName?: string) => { const result = input.toString(); @@ -84,7 +88,7 @@ export const getZodiosEndpointDefinitionList = (doc: OpenAPIObject, options?: Te // if schema is already assigned to a variable, re-use that variable name if (!options?.exportAllNamedSchemas && ctx.schemaByName[result]) { - return ctx.schemaByName[result]![0]!; + return ctx.schemaByName[result]!; } // result is complex and would benefit from being re-used @@ -95,7 +99,7 @@ export const getZodiosEndpointDefinitionList = (doc: OpenAPIObject, options?: Te let isVarNameAlreadyUsed = false; while ((isVarNameAlreadyUsed = Boolean(ctx.zodSchemaByName[formatedName]))) { if (isVarNameAlreadyUsed) { - if (options?.exportAllNamedSchemas && ctx.schemaByName[result]?.includes(formatedName)) { + if (options?.exportAllNamedSchemas && ctx.schemasByName?.[result]?.includes(formatedName)) { return formatedName; } else if (ctx.zodSchemaByName[formatedName] === safeName) { return formatedName; @@ -107,11 +111,10 @@ export const getZodiosEndpointDefinitionList = (doc: OpenAPIObject, options?: Te } ctx.zodSchemaByName[formatedName] = result; + ctx.schemaByName[result] = formatedName; - if (options?.exportAllNamedSchemas) { - ctx.schemaByName[result] = (ctx.schemaByName[result] ?? []).concat(formatedName); - } else { - ctx.schemaByName[result] = [formatedName]; + if (options?.exportAllNamedSchemas && ctx.schemasByName) { + ctx.schemasByName[result] = (ctx.schemasByName[result] ?? []).concat(formatedName); } return formatedName; @@ -315,7 +318,11 @@ export const getZodiosEndpointDefinitionList = (doc: OpenAPIObject, options?: Te } if (endpointDefinition.responses !== undefined) { - endpointDefinition.responses.push({ statusCode, schema: schemaString ?? voidSchema, description: responseItem.description }); + endpointDefinition.responses.push({ + statusCode, + schema: schemaString ?? voidSchema, + description: responseItem.description, + }); } if (schemaString) { From def55df8ba5e1c6de0a91207e7d031af9050519e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 25 Feb 2024 08:08:12 +0000 Subject: [PATCH 03/15] Version Packages --- .changeset/smooth-impalas-behave.md | 5 ----- lib/CHANGELOG.md | 6 ++++++ lib/package.json | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) delete mode 100644 .changeset/smooth-impalas-behave.md diff --git a/.changeset/smooth-impalas-behave.md b/.changeset/smooth-impalas-behave.md deleted file mode 100644 index ea695909..00000000 --- a/.changeset/smooth-impalas-behave.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"openapi-zod-client": patch ---- - -Fix bug with `exportAllNamedSchemas` option where schemas will reuse last schema name with matching schema rather than it's own name that has already been used before. diff --git a/lib/CHANGELOG.md b/lib/CHANGELOG.md index 28c9aff0..757bc731 100644 --- a/lib/CHANGELOG.md +++ b/lib/CHANGELOG.md @@ -1,5 +1,11 @@ # openapi-zod-client +## 1.16.1 + +### Patch Changes + +- [#270](https://github.com/astahmer/openapi-zod-client/pull/270) [`04dd1b5`](https://github.com/astahmer/openapi-zod-client/commit/04dd1b549118c8b8e5a3b86f6dbed741f44770c8) Thanks [@codingmatty](https://github.com/codingmatty)! - Fix bug with `exportAllNamedSchemas` option where schemas will reuse last schema name with matching schema rather than it's own name that has already been used before. + ## 1.16.0 ### Minor Changes diff --git a/lib/package.json b/lib/package.json index 80a40b8e..ba2b8778 100644 --- a/lib/package.json +++ b/lib/package.json @@ -1,6 +1,6 @@ { "name": "openapi-zod-client", - "version": "1.16.0", + "version": "1.16.1", "repository": { "type": "git", "url": "https://github.com/astahmer/openapi-zod-client.git" From 197316b50b0b84cea977984ae82441f2ce108ea0 Mon Sep 17 00:00:00 2001 From: Matthew Jacobs Date: Sat, 24 Feb 2024 21:50:39 -0500 Subject: [PATCH 04/15] fix: invalid dependency when using chaining with tag group strategy --- .changeset/two-pears-tap.md | 5 + lib/src/template-context.ts | 6 +- ...ody-with-chains-tag-group-strategy.test.ts | 92 +++++++++++++++++++ 3 files changed, 101 insertions(+), 2 deletions(-) create mode 100644 .changeset/two-pears-tap.md create mode 100644 lib/tests/array-body-with-chains-tag-group-strategy.test.ts diff --git a/.changeset/two-pears-tap.md b/.changeset/two-pears-tap.md new file mode 100644 index 00000000..89a51476 --- /dev/null +++ b/.changeset/two-pears-tap.md @@ -0,0 +1,5 @@ +--- +"openapi-zod-client": patch +--- + +Fix invalid output when using array types as the endpoint body with minItems or maxItems and using the tag-file group-strategy. diff --git a/lib/src/template-context.ts b/lib/src/template-context.ts index 3fc3585e..03f34d14 100644 --- a/lib/src/template-context.ts +++ b/lib/src/template-context.ts @@ -138,7 +138,9 @@ export const getZodClientTemplateContext = ( const addDependencyIfNeeded = (schemaName: string) => { if (!schemaName) return; if (schemaName.startsWith("z.")) return; - dependencies.add(schemaName); + // Sometimes the schema includes a chain that should be removed from the dependency + const [normalizedSchemaName] = schemaName.split("."); + dependencies.add(normalizedSchemaName!); }; addDependencyIfNeeded(endpoint.response); @@ -394,7 +396,7 @@ export type TemplateContextOptions = { * When true, returns a "responses" array with all responses (both success and errors) */ withAllResponses?: boolean; - + /** * When true, prevents using the exact same name for the same type * For example, if 2 schemas have the same type, but different names, export each as separate schemas diff --git a/lib/tests/array-body-with-chains-tag-group-strategy.test.ts b/lib/tests/array-body-with-chains-tag-group-strategy.test.ts new file mode 100644 index 00000000..aa0d3676 --- /dev/null +++ b/lib/tests/array-body-with-chains-tag-group-strategy.test.ts @@ -0,0 +1,92 @@ +import type { OpenAPIObject } from "openapi3-ts"; +import { expect, test } from "vitest"; +import { generateZodClientFromOpenAPI } from "../src"; + +test("array-body-with-chains-tag-group-strategy", async () => { + const openApiDoc: OpenAPIObject = { + openapi: "3.0.0", + info: { title: "Test", version: "1.0.1" }, + paths: { + "/test": { + put: { + summary: "Test", + description: "Test", + tags: ["Test"], + requestBody: { + content: { + "application/json": { + schema: { + type: "array", + items: { + type: "object", + properties: { + testItem: { + type: "string", + }, + }, + additionalProperties: false, + }, + minItems: 1, + maxItems: 10, + }, + }, + }, + }, + parameters: [], + responses: { + "200": { + description: "Success", + content: { "application/json": {} }, + }, + }, + }, + }, + }, + components: {}, + tags: [], + }; + + const output = await generateZodClientFromOpenAPI({ + disableWriteToFile: true, + openApiDoc, + options: { groupStrategy: "tag-file" }, + }); + expect(output).toMatchInlineSnapshot(` + { + "Test": "import { makeApi, Zodios, type ZodiosOptions } from "@zodios/core"; + import { z } from "zod"; + + const putTest_Body = z.array(z.object({ testItem: z.string() }).partial()); + + export const schemas = { + putTest_Body, + }; + + const endpoints = makeApi([ + { + method: "put", + path: "/test", + description: \`Test\`, + requestFormat: "json", + parameters: [ + { + name: "body", + type: "Body", + schema: putTest_Body.min(1).max(10), + }, + ], + response: z.void(), + }, + ]); + + export const TestApi = new Zodios(endpoints); + + export function createApiClient(baseUrl: string, options?: ZodiosOptions) { + return new Zodios(baseUrl, endpoints, options); + } + ", + "__index": "export { TestApi } from "./Test"; + ", + } + `); +}); From f0672b593e2b0fca28e1ac39d26c5e4d0b0f3b74 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 25 Feb 2024 17:45:34 +0000 Subject: [PATCH 05/15] Version Packages --- .changeset/two-pears-tap.md | 5 ----- lib/CHANGELOG.md | 6 ++++++ lib/package.json | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) delete mode 100644 .changeset/two-pears-tap.md diff --git a/.changeset/two-pears-tap.md b/.changeset/two-pears-tap.md deleted file mode 100644 index 89a51476..00000000 --- a/.changeset/two-pears-tap.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"openapi-zod-client": patch ---- - -Fix invalid output when using array types as the endpoint body with minItems or maxItems and using the tag-file group-strategy. diff --git a/lib/CHANGELOG.md b/lib/CHANGELOG.md index 757bc731..dc098d92 100644 --- a/lib/CHANGELOG.md +++ b/lib/CHANGELOG.md @@ -1,5 +1,11 @@ # openapi-zod-client +## 1.16.2 + +### Patch Changes + +- [#271](https://github.com/astahmer/openapi-zod-client/pull/271) [`197316b`](https://github.com/astahmer/openapi-zod-client/commit/197316b50b0b84cea977984ae82441f2ce108ea0) Thanks [@codingmatty](https://github.com/codingmatty)! - Fix invalid output when using array types as the endpoint body with minItems or maxItems and using the tag-file group-strategy. + ## 1.16.1 ### Patch Changes diff --git a/lib/package.json b/lib/package.json index ba2b8778..cdcc376c 100644 --- a/lib/package.json +++ b/lib/package.json @@ -1,6 +1,6 @@ { "name": "openapi-zod-client", - "version": "1.16.1", + "version": "1.16.2", "repository": { "type": "git", "url": "https://github.com/astahmer/openapi-zod-client.git" From 8b710ff4cc37a957fcc8769aebdd76162e3f39b7 Mon Sep 17 00:00:00 2001 From: tankers746 Date: Mon, 18 Mar 2024 19:13:43 +0000 Subject: [PATCH 06/15] fix: explicit undefined check to not exclude falsy default values --- lib/src/openApiToZod.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/openApiToZod.ts b/lib/src/openApiToZod.ts index 7ff69db7..d2772f0f 100644 --- a/lib/src/openApiToZod.ts +++ b/lib/src/openApiToZod.ts @@ -341,7 +341,7 @@ const unwrapQuotesIfNeeded = (value: string | number) => { }; const getZodChainableDefault = (schema: SchemaObject) => { - if (schema.default) { + if (schema.default !== undefined) { const value = match(schema.type) .with("number", "integer", () => unwrapQuotesIfNeeded(schema.default)) .otherwise(() => JSON.stringify(schema.default)); From aa4c7a3668c6d96492bcd319ccd940f0b735b029 Mon Sep 17 00:00:00 2001 From: Thomas Ankers Date: Mon, 18 Mar 2024 19:20:26 +0000 Subject: [PATCH 07/15] Create rude-ways-shake.md --- .changeset/rude-ways-shake.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/rude-ways-shake.md diff --git a/.changeset/rude-ways-shake.md b/.changeset/rude-ways-shake.md new file mode 100644 index 00000000..44bafd97 --- /dev/null +++ b/.changeset/rude-ways-shake.md @@ -0,0 +1,5 @@ +--- +"openapi-zod-client": patch +--- + +Fixed bug which was excluding falsy default values From 9bc51647a7b1f362a2167fe5995678e381a13c09 Mon Sep 17 00:00:00 2001 From: Thomas Ankers Date: Mon, 18 Mar 2024 19:29:02 +0000 Subject: [PATCH 08/15] Update samples.test.ts --- lib/tests/samples.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/tests/samples.test.ts b/lib/tests/samples.test.ts index 8c71c5b0..fb1f207a 100644 --- a/lib/tests/samples.test.ts +++ b/lib/tests/samples.test.ts @@ -398,7 +398,7 @@ describe("samples-generator", async () => { const perform_search_Body = z .object({ criteria: z.string().default("*:*"), - start: z.number().int().optional(), + start: z.number().int().optional().default(0), rows: z.number().int().optional().default(100), }) .passthrough(); From 8d84b028ca561403cb869727c7ec0f6a186f5ec7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 26 Mar 2024 14:07:27 +0000 Subject: [PATCH 09/15] Version Packages --- .changeset/rude-ways-shake.md | 5 ----- lib/CHANGELOG.md | 6 ++++++ lib/package.json | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) delete mode 100644 .changeset/rude-ways-shake.md diff --git a/.changeset/rude-ways-shake.md b/.changeset/rude-ways-shake.md deleted file mode 100644 index 44bafd97..00000000 --- a/.changeset/rude-ways-shake.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"openapi-zod-client": patch ---- - -Fixed bug which was excluding falsy default values diff --git a/lib/CHANGELOG.md b/lib/CHANGELOG.md index dc098d92..2337ef45 100644 --- a/lib/CHANGELOG.md +++ b/lib/CHANGELOG.md @@ -1,5 +1,11 @@ # openapi-zod-client +## 1.16.3 + +### Patch Changes + +- [#276](https://github.com/astahmer/openapi-zod-client/pull/276) [`aa4c7a3`](https://github.com/astahmer/openapi-zod-client/commit/aa4c7a3668c6d96492bcd319ccd940f0b735b029) Thanks [@tankers746](https://github.com/tankers746)! - Fixed bug which was excluding falsy default values + ## 1.16.2 ### Patch Changes diff --git a/lib/package.json b/lib/package.json index cdcc376c..6e32da6b 100644 --- a/lib/package.json +++ b/lib/package.json @@ -1,6 +1,6 @@ { "name": "openapi-zod-client", - "version": "1.16.2", + "version": "1.16.3", "repository": { "type": "git", "url": "https://github.com/astahmer/openapi-zod-client.git" From 7224486d79dbaef688a9e7a6bf37df9173eb7d8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Douglas=20Gad=C3=AAlha?= Date: Tue, 2 Apr 2024 15:21:57 -0300 Subject: [PATCH 10/15] fix: multiline description --- lib/src/getZodiosEndpointDefinitionList.ts | 2 +- lib/src/openApiToZod.ts | 6 +++- lib/tests/description-in-zod.test.ts | 32 ++++++++++++++++++++++ 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/lib/src/getZodiosEndpointDefinitionList.ts b/lib/src/getZodiosEndpointDefinitionList.ts index 40dbbe75..c572068f 100644 --- a/lib/src/getZodiosEndpointDefinitionList.ts +++ b/lib/src/getZodiosEndpointDefinitionList.ts @@ -249,7 +249,7 @@ export const getZodiosEndpointDefinitionList = (doc: OpenAPIObject, options?: Te } if (options?.withDescription && paramSchema) { - (paramSchema as SchemaObject).description = (paramItem.description ?? "")?.replace("\n", ""); + (paramSchema as SchemaObject).description = (paramItem.description ?? "").trim(); } // resolve ref if needed, and fallback to default (unknown) value if needed diff --git a/lib/src/openApiToZod.ts b/lib/src/openApiToZod.ts index d2772f0f..88d505b6 100644 --- a/lib/src/openApiToZod.ts +++ b/lib/src/openApiToZod.ts @@ -302,7 +302,11 @@ export const getZodChain = ({ schema, meta, options }: ZodChainArgs) => { .otherwise(() => void 0); if (typeof schema.description === "string" && schema.description !== "" && options?.withDescription) { - chains.push(`describe("${schema.description}")`); + if (["\n", "\r", "\r\n"].some((c) => String.prototype.includes.call(schema.description, c))) { + chains.push(`describe(\`${schema.description}\`)`); + } else { + chains.push(`describe("${schema.description}")`); + } } const output = chains diff --git a/lib/tests/description-in-zod.test.ts b/lib/tests/description-in-zod.test.ts index 56344d03..31f3b400 100644 --- a/lib/tests/description-in-zod.test.ts +++ b/lib/tests/description-in-zod.test.ts @@ -31,6 +31,23 @@ test("description-in-zod", async () => { }, description: "bar description", }, + { + in: "query", + name: "baz", + schema: { + type: "number", + enum: [1.3, 34.1, -57.89], + }, + description: "baz\nmultiline\ndescription", + }, + { + in: "query", + name: "qux", + schema: { + type: "string", + }, + description: " ", // spaces only description + }, ], responses: { "200": { @@ -73,6 +90,21 @@ test("description-in-zod", async () => { .describe("bar description") .optional(), }, + { + name: "baz", + type: "Query", + schema: z + .union([z.literal(1.3), z.literal(34.1), z.literal(-57.89)]) + .describe( + \`baz\nmultiline\ndescription\` + ) + .optional(), + }, + { + name: "qux", + type: "Query", + schema: z.string().optional(), + }, ], response: z.void(), }, From f3ee25efc191d0be97231498924fe50fd977fb88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Douglas=20Gad=C3=AAlha?= Date: Wed, 3 Apr 2024 11:56:24 -0300 Subject: [PATCH 11/15] feat: add changeset --- .changeset/chilly-needles-pump.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/chilly-needles-pump.md diff --git a/.changeset/chilly-needles-pump.md b/.changeset/chilly-needles-pump.md new file mode 100644 index 00000000..16660521 --- /dev/null +++ b/.changeset/chilly-needles-pump.md @@ -0,0 +1,5 @@ +--- +"openapi-zod-client": patch +--- + +Fix multiline descriptions when `describe` is enabled From c574b50c91e0135916e44f0368e564f204952898 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 3 Apr 2024 15:44:08 +0000 Subject: [PATCH 12/15] Version Packages --- .changeset/chilly-needles-pump.md | 5 ----- lib/CHANGELOG.md | 6 ++++++ lib/package.json | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) delete mode 100644 .changeset/chilly-needles-pump.md diff --git a/.changeset/chilly-needles-pump.md b/.changeset/chilly-needles-pump.md deleted file mode 100644 index 16660521..00000000 --- a/.changeset/chilly-needles-pump.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"openapi-zod-client": patch ---- - -Fix multiline descriptions when `describe` is enabled diff --git a/lib/CHANGELOG.md b/lib/CHANGELOG.md index 2337ef45..82ca7d97 100644 --- a/lib/CHANGELOG.md +++ b/lib/CHANGELOG.md @@ -1,5 +1,11 @@ # openapi-zod-client +## 1.16.4 + +### Patch Changes + +- [#279](https://github.com/astahmer/openapi-zod-client/pull/279) [`f3ee25e`](https://github.com/astahmer/openapi-zod-client/commit/f3ee25efc191d0be97231498924fe50fd977fb88) Thanks [@dgadelha](https://github.com/dgadelha)! - Fix multiline descriptions when `describe` is enabled + ## 1.16.3 ### Patch Changes diff --git a/lib/package.json b/lib/package.json index 6e32da6b..984b4557 100644 --- a/lib/package.json +++ b/lib/package.json @@ -1,6 +1,6 @@ { "name": "openapi-zod-client", - "version": "1.16.3", + "version": "1.16.4", "repository": { "type": "git", "url": "https://github.com/astahmer/openapi-zod-client.git" From e812354e578af2dde19b96b2f169e928be129b90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Douglas=20Gad=C3=AAlha?= Date: Wed, 10 Apr 2024 15:48:58 -0300 Subject: [PATCH 13/15] feat: schema refiner --- lib/src/openApiToZod.ts | 6 +++-- lib/src/template-context.ts | 8 ++++++- lib/tests/schema-refiner.test.ts | 41 ++++++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+), 3 deletions(-) create mode 100644 lib/tests/schema-refiner.test.ts diff --git a/lib/src/openApiToZod.ts b/lib/src/openApiToZod.ts index 88d505b6..2bd35d25 100644 --- a/lib/src/openApiToZod.ts +++ b/lib/src/openApiToZod.ts @@ -20,10 +20,12 @@ type ConversionArgs = { * @see https://github.com/colinhacks/zod */ // eslint-disable-next-line sonarjs/cognitive-complexity -export function getZodSchema({ schema, ctx, meta: inheritedMeta, options }: ConversionArgs): CodeMeta { - if (!schema) { +export function getZodSchema({ schema: $schema, ctx, meta: inheritedMeta, options }: ConversionArgs): CodeMeta { + if (!$schema) { throw new Error("Schema is required"); } + + const schema = options?.schemaRefiner?.($schema, inheritedMeta) ?? $schema; const code = new CodeMeta(schema, ctx, inheritedMeta); const meta = { parent: code.inherit(inheritedMeta?.parent), diff --git a/lib/src/template-context.ts b/lib/src/template-context.ts index 03f34d14..aced2cbd 100644 --- a/lib/src/template-context.ts +++ b/lib/src/template-context.ts @@ -1,4 +1,4 @@ -import type { OpenAPIObject, OperationObject, PathItemObject, SchemaObject } from "openapi3-ts"; +import type { OpenAPIObject, OperationObject, PathItemObject, ReferenceObject, SchemaObject } from "openapi3-ts"; import { sortBy, sortListFromRefArray, sortObjKeysFromArray } from "pastable/server"; import { ts } from "tanu"; import { match } from "ts-pattern"; @@ -11,6 +11,7 @@ import { getTypescriptFromOpenApi } from "./openApiToTypescript"; import { getZodSchema } from "./openApiToZod"; import { topologicalSort } from "./topologicalSort"; import { asComponentSchema, normalizeString } from "./utils"; +import type { CodeMetaData } from "./CodeMeta"; const file = ts.createSourceFile("", "", ts.ScriptTarget.ESNext, true); const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed }); @@ -403,4 +404,9 @@ export type TemplateContextOptions = { * If 2 schemas have the same name but different types, export subsequent names with numbers appended */ exportAllNamedSchemas?: boolean; + + /** + * A function that runs in the schema conversion process to refine the schema before it's converted to a Zod schema. + */ + schemaRefiner?: (schema: T, parentMeta?: CodeMetaData) => T; }; diff --git a/lib/tests/schema-refiner.test.ts b/lib/tests/schema-refiner.test.ts new file mode 100644 index 00000000..79b89b42 --- /dev/null +++ b/lib/tests/schema-refiner.test.ts @@ -0,0 +1,41 @@ +import { isReferenceObject } from "openapi3-ts"; +import { getZodSchema } from "../src/openApiToZod"; +import { test, expect } from "vitest"; + +test("schema-refiner", () => { + expect( + getZodSchema({ + schema: { + properties: { + name: { + type: "string", + }, + email: { + type: "string", + }, + }, + }, + options: { + schemaRefiner(schema) { + if (isReferenceObject(schema) || !schema.properties) { + return schema; + } + + if (!schema.required && schema.properties) { + for (const key in schema.properties) { + const prop = schema.properties[key]; + + if (!isReferenceObject(prop)) { + prop.nullable = true; + } + } + } + + return schema; + }, + }, + }) + ).toMatchInlineSnapshot( + '"z.object({ name: z.string().nullable(), email: z.string().nullable() }).partial().passthrough()"' + ); +}); From 3ec491572e56fc40e3b49cefb58cb6f08600190f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Douglas=20Gad=C3=AAlha?= Date: Wed, 10 Apr 2024 15:53:46 -0300 Subject: [PATCH 14/15] feat: add changeset according to SEMVER spec, this must be a minor update: > Minor version Y (x.Y.z | x > 0) MUST be incremented if new, backward compatible functionality is introduced to the public API. --- .changeset/ten-poems-appear.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/ten-poems-appear.md diff --git a/.changeset/ten-poems-appear.md b/.changeset/ten-poems-appear.md new file mode 100644 index 00000000..6e08c318 --- /dev/null +++ b/.changeset/ten-poems-appear.md @@ -0,0 +1,5 @@ +--- +"openapi-zod-client": minor +--- + +Add `schemaRefiner` option to allow refining the OpenAPI schema before its converted to a Zod schema From 494583f94ac5ed4464053ea7ccdb14c86a19544a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 11 Apr 2024 07:35:48 +0000 Subject: [PATCH 15/15] Version Packages --- .changeset/ten-poems-appear.md | 5 ----- lib/CHANGELOG.md | 6 ++++++ lib/package.json | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) delete mode 100644 .changeset/ten-poems-appear.md diff --git a/.changeset/ten-poems-appear.md b/.changeset/ten-poems-appear.md deleted file mode 100644 index 6e08c318..00000000 --- a/.changeset/ten-poems-appear.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"openapi-zod-client": minor ---- - -Add `schemaRefiner` option to allow refining the OpenAPI schema before its converted to a Zod schema diff --git a/lib/CHANGELOG.md b/lib/CHANGELOG.md index 82ca7d97..0a6cf0be 100644 --- a/lib/CHANGELOG.md +++ b/lib/CHANGELOG.md @@ -1,5 +1,11 @@ # openapi-zod-client +## 1.17.0 + +### Minor Changes + +- [#283](https://github.com/astahmer/openapi-zod-client/pull/283) [`3ec4915`](https://github.com/astahmer/openapi-zod-client/commit/3ec491572e56fc40e3b49cefb58cb6f08600190f) Thanks [@dgadelha](https://github.com/dgadelha)! - Add `schemaRefiner` option to allow refining the OpenAPI schema before its converted to a Zod schema + ## 1.16.4 ### Patch Changes diff --git a/lib/package.json b/lib/package.json index 984b4557..d75a1e31 100644 --- a/lib/package.json +++ b/lib/package.json @@ -1,6 +1,6 @@ { "name": "openapi-zod-client", - "version": "1.16.4", + "version": "1.17.0", "repository": { "type": "git", "url": "https://github.com/astahmer/openapi-zod-client.git"