diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptClientCodegen.java index f58c7711630f..5adfe735e6e3 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptClientCodegen.java @@ -407,13 +407,16 @@ public ModelsMap postProcessModels(ModelsMap objs) { } if (!cm.oneOf.isEmpty()) { // For oneOfs only import $refs within the oneOf - TreeSet oneOfRefs = new TreeSet<>(); - for (String im : cm.imports) { - if (cm.oneOf.contains(im)) { - oneOfRefs.add(im); - } + TreeSet oneOfRefs = new TreeSet<>(cm.oneOf); + List oneOfProperties = Optional.ofNullable(cm.getComposedSchemas()) + .map(CodegenComposedSchemas::getOneOf) + .orElse(Collections.emptyList()); + for (CodegenProperty oneOfProperty : oneOfProperties) { + // resolve the types referenced by each branch so that inner types of + // container types (e.g. Foo in Array) keep their model import + oneOfRefs.addAll(oneOfProperty.getImports(false, true, generatorMetadata.getFeatureSet())); } - cm.imports = oneOfRefs; + cm.imports = new TreeSet<>(Sets.intersection(cm.imports, oneOfRefs)); } } for (ModelMap mo : models) { diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptReduxQueryClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptReduxQueryClientCodegen.java index 2f3595880e0c..80a068e76d13 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptReduxQueryClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptReduxQueryClientCodegen.java @@ -29,8 +29,10 @@ import org.openapitools.codegen.utils.ModelUtils; import java.io.File; +import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.TreeSet; /** @@ -148,13 +150,14 @@ public ModelsMap postProcessModels(ModelsMap objs) { } if (!cm.oneOf.isEmpty()) { // For oneOfs only import $refs within the oneOf - TreeSet oneOfRefs = new TreeSet<>(); - for (String im : cm.imports) { - if (cm.oneOf.contains(im)) { - oneOfRefs.add(im); - } + TreeSet oneOfRefs = new TreeSet<>(cm.oneOf); + List oneOfProperties = Optional.ofNullable(cm.getComposedSchemas()) + .map(CodegenComposedSchemas::getOneOf) + .orElse(Collections.emptyList()); + for (CodegenProperty oneOfProperty : oneOfProperties) { + oneOfRefs.addAll(oneOfProperty.getImports(false, true, generatorMetadata.getFeatureSet())); } - cm.imports = oneOfRefs; + cm.imports.retainAll(oneOfRefs); } } diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/typescript/TypeScriptClientCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/typescript/TypeScriptClientCodegenTest.java index 505a073d4137..3f2d9e746c6d 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/typescript/TypeScriptClientCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/typescript/TypeScriptClientCodegenTest.java @@ -293,6 +293,34 @@ public void testDeprecatedAttribute() throws Exception { assertEquals(1, TestUtils.countOccurrences(content, "@deprecated")); } + @Test + public void testCircularOneOfGeneratesImports() throws Exception { + final File output = Files.createTempDirectory("typescriptclient_").toFile(); + output.deleteOnExit(); + + final CodegenConfigurator configurator = new CodegenConfigurator() + .setGeneratorName("typescript") + .setInputSpec("src/test/resources/bugs/issue_23286.yaml") + .setOutputDir(output.getAbsolutePath().replace("\\", "/")); + + final ClientOptInput clientOptInput = configurator.toClientOptInput(); + final DefaultGenerator generator = new DefaultGenerator(); + final List files = generator.opts(clientOptInput).generate(); + files.forEach(File::deleteOnExit); + + // model with a circular oneOf branch (Array) must import Repro + TestUtils.assertFileContains( + Paths.get(output + "/models/ReproContent.ts"), + "import { Repro } from '../models/Repro';", + "export type ReproContent = Array | string;" + ); + + configurator.setGeneratorName("typescript-redux-query"); + generator.opts(configurator.toClientOptInput()).generate().forEach(File::deleteOnExit); + TestUtils.assertFileContains(Paths.get(output + "/src/models/ReproContent.ts"), + "Repro,", "export type ReproContent = Array | string;"); + } + @Test(description = "Verify useErasableSyntax config parameter generates erasable code") public void testUseErasableSyntaxConfig() throws IOException { boolean[] options = {true, false}; diff --git a/modules/openapi-generator/src/test/resources/bugs/issue_23286.yaml b/modules/openapi-generator/src/test/resources/bugs/issue_23286.yaml new file mode 100644 index 000000000000..7cb118bd6de8 --- /dev/null +++ b/modules/openapi-generator/src/test/resources/bugs/issue_23286.yaml @@ -0,0 +1,16 @@ +openapi: 3.0.3 +info: + title: Repro + version: 1.0.0 +paths: {} +components: + schemas: + Repro: + type: object + properties: + content: + oneOf: + - type: string + - type: array + items: + $ref: '#/components/schemas/Repro'