[Bug][typescript-fetch] Typescript fetch one of addtl props imports (#21656)

* Add unit tests for bug in imports for oneOf schemas with additional properties set to true #21587

* Fix bug in filtering primitive, built-in types from model imports in TypeScriptFetchClientCodegen

* Add YAML OAS file for #21587

* Revert change to issue_21259.yaml

* Remove comment from issue_21259.yaml

* Filter out arrays from oneOfModels along with primitive types

* Update issue_21587 unit test to catch the Array<Model> case

---------

Co-authored-by: Chris Gual <cgual@omnidian.com>
This commit is contained in:
Christopher Gual
2025-07-30 00:02:40 -07:00
committed by GitHub
parent 657f5fb004
commit d29a4bea25
3 changed files with 129 additions and 2 deletions
@@ -787,21 +787,26 @@ public class TypeScriptFetchClientCodegen extends AbstractTypeScriptClientCodege
.map(CodegenComposedSchemas::getOneOf)
.orElse(Collections.emptyList());
// 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(CodegenProperty::getIsModel)
.filter(cp -> !cp.getIsPrimitiveType() && !cp.getIsArray())
.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>, 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 TypeScriptFetchClientCodegen extends AbstractTypeScriptClientCodege
@Getter @Setter
public Set<String> modelImports = new TreeSet<String>();
// 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<String> oneOfModels = new TreeSet<>();
@Getter @Setter
@@ -7,6 +7,8 @@ import io.swagger.v3.oas.models.media.MapSchema;
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,44 @@ public class TypeScriptFetchClientCodegenTest {
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'");
TestUtils.assertFileContains(testResponse, "import type { OptionThree } from './OptionThree'");
}
private static File generate(
Map<String, Object> properties
) throws IOException {
@@ -0,0 +1,79 @@
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: array
items:
$ref: '#/components/schemas/OptionThree'
- 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
OptionThree:
type: object
title: OptionThree
properties:
propThree:
type: boolean
additionalProperties: true