From a6e4d9b59680e4dbcaab40e19d97f6eba5001a77 Mon Sep 17 00:00:00 2001 From: Chris Gual Date: Sun, 27 Jul 2025 11:55:38 -0700 Subject: [PATCH 1/7] Add unit tests for bug in imports for oneOf schemas with additional properties set to true #21587 --- .../TypeScriptFetchClientCodegenTest.java | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/typescript/fetch/TypeScriptFetchClientCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/typescript/fetch/TypeScriptFetchClientCodegenTest.java index fb592791521c..225730ef6af8 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/typescript/fetch/TypeScriptFetchClientCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/typescript/fetch/TypeScriptFetchClientCodegenTest.java @@ -7,6 +7,8 @@ import io.swagger.v3.oas.models.media.Schema; import io.swagger.v3.oas.models.media.StringSchema; import java.util.Collections; +import java.util.Locale; +import java.util.stream.Stream; import org.apache.commons.lang3.StringUtils; import org.openapitools.codegen.*; import org.openapitools.codegen.config.CodegenConfigurator; @@ -404,6 +406,43 @@ public void testOneOfModelsDoNotImportPrimitiveTypes() throws IOException { TestUtils.assertFileContains(testDiscriminatorResponse, "export type TestDiscriminatorResponse = { discriminatorField: 'optionOne' } & OptionOne | { discriminatorField: 'optionTwo' } & OptionTwo"); } + /** + * Issue #21587 + * When using oneOf, the Typescript Fetch generator should import modelled types except for + * types built-in primitive types, even those marked with additional properties. + */ + @Test() + public void testOneOfModelsImportNonPrimitiveTypes() throws IOException { + File output = generate( + Collections.emptyMap(), + "src/test/resources/3_0/typescript-fetch/issue_21587.yaml" + ); + + Path testResponse = Paths.get(output + "/models/OneOfResponse.ts"); + TestUtils.assertFileExists(testResponse); + + // Primitive built-in types should not be included. This list is based off the type mappings + // and language specific primitive keywords established in the AbstractTypeScriptClientCodegen + Stream.of( + "Set", + "Array", + "boolean", + "string", + "number", + "object", + "any", + "Date", + "Error" + ).forEach(primitiveType -> + TestUtils.assertFileNotContains( + testResponse, + String.format(Locale.ROOT, "import type { %s } from './%s'", primitiveType, primitiveType) + ) + ); + TestUtils.assertFileContains(testResponse, "import type { OptionOne } from './OptionOne'"); + TestUtils.assertFileContains(testResponse, "import type { OptionTwo } from './OptionTwo'"); + } + private static File generate( Map properties ) throws IOException { From d9c95ab19f1872a42a70996b3d90b6a05e1c4926 Mon Sep 17 00:00:00 2001 From: Chris Gual Date: Sun, 27 Jul 2025 11:56:36 -0700 Subject: [PATCH 2/7] Fix bug in filtering primitive, built-in types from model imports in TypeScriptFetchClientCodegen --- .../languages/TypeScriptFetchClientCodegen.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptFetchClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptFetchClientCodegen.java index adf43d4a35f0..27d5367e7b22 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptFetchClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptFetchClientCodegen.java @@ -787,21 +787,26 @@ private ExtendedCodegenModel processCodeGenModel(ExtendedCodegenModel cm) { .map(CodegenComposedSchemas::getOneOf) .orElse(Collections.emptyList()); + // create a set of any non-primitive types used in the oneOf schemas which will need to + // be imported. cm.oneOfModels = oneOfsList.stream() - .filter(CodegenProperty::getIsModel) + .filter(cp -> !cp.getIsPrimitiveType()) .map(CodegenProperty::getBaseType) .filter(Objects::nonNull) .collect(Collectors.toCollection(TreeSet::new)); + // create a set of any complex, inner types used by arrays in the oneOf schema (e.g. if + // the oneOf uses Array, Foo needs to be imported). cm.oneOfArrays = oneOfsList.stream() .filter(CodegenProperty::getIsArray) .map(CodegenProperty::getComplexType) .filter(Objects::nonNull) .collect(Collectors.toCollection(TreeSet::new)); + // create a set of primitive types used in the oneOf schemas for use in the to & from + // typed JSON methods. cm.oneOfPrimitives = oneOfsList.stream() .filter(CodegenProperty::getIsPrimitiveType) - .filter(Objects::nonNull) .collect(Collectors.toCollection(HashSet::new)); if (!cm.oneOf.isEmpty()) { @@ -1485,6 +1490,9 @@ public class ExtendedCodegenModel extends CodegenModel { @Getter @Setter public Set modelImports = new TreeSet(); + // oneOfModels, oneOfArrays & oneOfPrimitives contain a list of types used in schemas + // composed with oneOf and are used to define the import list and the to & from + // 'TypedJSON' conversion methods in the composed model classes. @Getter @Setter public Set oneOfModels = new TreeSet<>(); @Getter @Setter From 94b1235f4f6254966d06f4eec2924710b5442137 Mon Sep 17 00:00:00 2001 From: Chris Gual Date: Sun, 27 Jul 2025 11:57:57 -0700 Subject: [PATCH 3/7] Add YAML OAS file for #21587 --- .../3_0/typescript-fetch/issue_21587.yaml | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 modules/openapi-generator/src/test/resources/3_0/typescript-fetch/issue_21587.yaml diff --git a/modules/openapi-generator/src/test/resources/3_0/typescript-fetch/issue_21587.yaml b/modules/openapi-generator/src/test/resources/3_0/typescript-fetch/issue_21587.yaml new file mode 100644 index 000000000000..b2220d7d2ecd --- /dev/null +++ b/modules/openapi-generator/src/test/resources/3_0/typescript-fetch/issue_21587.yaml @@ -0,0 +1,69 @@ +openapi: 3.0.1 +info: + title: Example API + version: 1.0.0 +paths: + /api/endpoint: + get: + operationId: GetEndpoint + summary: Get endpoint + tags: + - Examples + responses: + '200': + description: Successful response + content: + application/json: + schema: + type: object + title: OneOfResponse + oneOf: + - $ref: '#/components/schemas/OptionOne' + - $ref: '#/components/schemas/OptionTwo' + - type: string + enum: + - "fixed-value-a" + - "fixed-value-b" + - "fixed-value-c" + - type: boolean + - type: number + - type: string + format: date + - type: string + format: date-time + - type: integer + format: int64 + enum: [10, 20, 30] + - type: array + items: + type: number + - type: array + items: + type: object + - type: array + items: + type: string + enum: + - "oneof-array-enum-a" + - "oneof-array-enum-b" + - "oneof-array-enum-c" + - type: array + items: + type: number + uniqueItems: true + +components: + schemas: + OptionOne: + type: object + title: OptionOne + properties: + propOne: + type: number + additionalProperties: true + OptionTwo: + type: object + title: OptionTwo + properties: + propTwo: + type: string \ No newline at end of file From 4f6a9640a6d81f4db61c2be39a49adbe242da66d Mon Sep 17 00:00:00 2001 From: Chris Gual Date: Sun, 27 Jul 2025 11:59:25 -0700 Subject: [PATCH 4/7] Revert change to issue_21259.yaml --- .../openapi-generator/src/test/resources/bugs/issue_21259.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/openapi-generator/src/test/resources/bugs/issue_21259.yaml b/modules/openapi-generator/src/test/resources/bugs/issue_21259.yaml index ca6adc219c42..7e5db78f8c5f 100644 --- a/modules/openapi-generator/src/test/resources/bugs/issue_21259.yaml +++ b/modules/openapi-generator/src/test/resources/bugs/issue_21259.yaml @@ -49,6 +49,7 @@ components: items: type: string enum: + # It seems enums within arrays don't work. Leaving this here, though - "oneof-array-enum-a" - "oneof-array-enum-b" - "oneof-array-enum-c" From f4ab8b6c28f3f855a4e5dd55a25be5d350eaaf42 Mon Sep 17 00:00:00 2001 From: Chris Gual Date: Tue, 29 Jul 2025 11:46:12 -0700 Subject: [PATCH 5/7] Remove comment from issue_21259.yaml --- .../openapi-generator/src/test/resources/bugs/issue_21259.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/openapi-generator/src/test/resources/bugs/issue_21259.yaml b/modules/openapi-generator/src/test/resources/bugs/issue_21259.yaml index 7e5db78f8c5f..ca6adc219c42 100644 --- a/modules/openapi-generator/src/test/resources/bugs/issue_21259.yaml +++ b/modules/openapi-generator/src/test/resources/bugs/issue_21259.yaml @@ -49,7 +49,6 @@ components: items: type: string enum: - # It seems enums within arrays don't work. Leaving this here, though - "oneof-array-enum-a" - "oneof-array-enum-b" - "oneof-array-enum-c" From b9cd2db2edb4f70287b78b1225e859643d8820aa Mon Sep 17 00:00:00 2001 From: Chris Gual Date: Tue, 29 Jul 2025 16:04:08 -0700 Subject: [PATCH 6/7] Filter out arrays from oneOfModels along with primitive types --- .../codegen/languages/TypeScriptFetchClientCodegen.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptFetchClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptFetchClientCodegen.java index 27d5367e7b22..0c1b1ee13251 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptFetchClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptFetchClientCodegen.java @@ -787,10 +787,10 @@ private ExtendedCodegenModel processCodeGenModel(ExtendedCodegenModel cm) { .map(CodegenComposedSchemas::getOneOf) .orElse(Collections.emptyList()); - // create a set of any non-primitive types used in the oneOf schemas which will need to - // be imported. + // create a set of any non-primitive, non-array types used in the oneOf schemas which will + // need to be imported. cm.oneOfModels = oneOfsList.stream() - .filter(cp -> !cp.getIsPrimitiveType()) + .filter(cp -> !cp.getIsPrimitiveType() && !cp.getIsArray()) .map(CodegenProperty::getBaseType) .filter(Objects::nonNull) .collect(Collectors.toCollection(TreeSet::new)); From 450a126654444e6dfdbc1a5c90c005479a461b52 Mon Sep 17 00:00:00 2001 From: Chris Gual Date: Tue, 29 Jul 2025 16:22:33 -0700 Subject: [PATCH 7/7] Update issue_21587 unit test to catch the Array case --- .../fetch/TypeScriptFetchClientCodegenTest.java | 1 + .../resources/3_0/typescript-fetch/issue_21587.yaml | 12 +++++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/typescript/fetch/TypeScriptFetchClientCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/typescript/fetch/TypeScriptFetchClientCodegenTest.java index 225730ef6af8..1608fe2750cf 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/typescript/fetch/TypeScriptFetchClientCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/typescript/fetch/TypeScriptFetchClientCodegenTest.java @@ -441,6 +441,7 @@ public void testOneOfModelsImportNonPrimitiveTypes() throws IOException { ); TestUtils.assertFileContains(testResponse, "import type { OptionOne } from './OptionOne'"); TestUtils.assertFileContains(testResponse, "import type { OptionTwo } from './OptionTwo'"); + TestUtils.assertFileContains(testResponse, "import type { OptionThree } from './OptionThree'"); } private static File generate( diff --git a/modules/openapi-generator/src/test/resources/3_0/typescript-fetch/issue_21587.yaml b/modules/openapi-generator/src/test/resources/3_0/typescript-fetch/issue_21587.yaml index b2220d7d2ecd..dd997cecbb9f 100644 --- a/modules/openapi-generator/src/test/resources/3_0/typescript-fetch/issue_21587.yaml +++ b/modules/openapi-generator/src/test/resources/3_0/typescript-fetch/issue_21587.yaml @@ -20,6 +20,9 @@ paths: oneOf: - $ref: '#/components/schemas/OptionOne' - $ref: '#/components/schemas/OptionTwo' + - type: array + items: + $ref: '#/components/schemas/OptionThree' - type: string enum: - "fixed-value-a" @@ -66,4 +69,11 @@ components: title: OptionTwo properties: propTwo: - type: string \ No newline at end of file + type: string + OptionThree: + type: object + title: OptionThree + properties: + propThree: + type: boolean + additionalProperties: true \ No newline at end of file