diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java index 60b17e8e47d5..f73aadec2f8c 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java @@ -31,6 +31,7 @@ import org.apache.commons.io.IOCase; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.Pair; import org.openapitools.codegen.api.*; import org.openapitools.codegen.config.GlobalSettings; import org.openapitools.codegen.ignore.CodegenIgnoreProcessor; @@ -1656,7 +1657,7 @@ private OperationsMap processOperations(CodegenConfig config, String tag, List mappings = getAllImportsMappings(allImports); + Set> mappings = getAllImportMappings(allImports); Set> imports = toImportsObjects(mappings); //Some codegen implementations rely on a list interface for the imports @@ -1734,6 +1735,16 @@ private Map getAllImportsMappings(Set allImports) { return result; } + private Set> getAllImportMappings(Set allImports) { + return allImports.stream().map(nextImport -> { + String mapping = config.importMapping().get(nextImport); + if (mapping != null) { + return Pair.of(mapping, nextImport); + } + return Pair.of(config.toModelImport(nextImport), nextImport); + }).collect(Collectors.toSet()); + } + /** * Using an import map created via {@link #getAllImportsMappings(Set)} to build a list import objects. * The import objects have two keys: import and classname which hold the key and value of the initial map entry. @@ -1755,6 +1766,20 @@ private Set> toImportsObjects(Map mappedImpo return result; } + private Set> toImportsObjects(Set> importPairs) { + Set> result = new TreeSet<>( + Comparator.comparing(o -> o.get("classname")) + ); + + importPairs.forEach((pair) -> { + Map im = new LinkedHashMap<>(); + im.put("import", pair.getLeft()); + im.put("classname", pair.getRight()); + result.add(im); + }); + return result; + } + private ModelsMap processModels(CodegenConfig config, Map definitions) { ModelsMap objs = new ModelsMap(); objs.put("package", config.modelPackage()); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptAngularClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptAngularClientCodegen.java index a7c1b962a2b7..f540b1b962d8 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptAngularClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptAngularClientCodegen.java @@ -22,6 +22,7 @@ import lombok.Data; import lombok.Getter; import lombok.Setter; +import org.apache.commons.lang3.tuple.Pair; import org.openapitools.codegen.*; import org.openapitools.codegen.meta.features.DocumentationFeature; import org.openapitools.codegen.meta.features.GlobalFeature; @@ -476,13 +477,13 @@ public OperationsMap postProcessOperationsWithModels(OperationsMap operations, L operations.put("hasSomeEncodableParams", hasSomeEncodableParams); // Add additional filename information for model imports in the services - List> imports = operations.getImports(); - for (Map im : imports) { + List> mergedImports = mergeImports(operations.getImports()); + for (Map im : mergedImports) { // This property is not used in the templates any more, subject for removal im.put("filename", im.get("import")); im.put("classname", im.get("classname")); } - + operations.setImports(mergedImports); return operations; } @@ -580,6 +581,28 @@ private List> toTsImports(CodegenModel cm, Set impor return tsImports; } + /** + * Merge imports that belong to the same file + */ + private List> mergeImports(List> imports) { + Map importLookup = new HashMap<>(); + imports.forEach(importMap -> { + String importPackage = importMap.get("import"); + String importType = importMap.get("classname"); + String existingImportType = importLookup.get(importPackage); + if (existingImportType != null && !existingImportType.equals(importType)) { + String newImportType = String.join(", ", existingImportType, importType); + importLookup.put(importPackage, newImportType); + } else { + importLookup.put(importPackage, importType); + } + }); + + return importLookup.entrySet().stream() + .map(entry -> new HashMap<>(Map.of("import", entry.getKey(), "classname", entry.getValue()))) + .collect(Collectors.toList()); + } + @Override public String toApiName(String name) { if (name.length() == 0) { diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/typescript/typescriptangular/TypeScriptAngularClientCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/typescript/typescriptangular/TypeScriptAngularClientCodegenTest.java index e3acfc556211..e8bbc3635867 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/typescript/typescriptangular/TypeScriptAngularClientCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/typescript/typescriptangular/TypeScriptAngularClientCodegenTest.java @@ -11,6 +11,8 @@ import org.openapitools.codegen.*; import org.openapitools.codegen.config.CodegenConfigurator; import org.openapitools.codegen.languages.TypeScriptAngularClientCodegen; +import org.openapitools.codegen.model.OperationMap; +import org.openapitools.codegen.model.OperationsMap; import org.openapitools.codegen.typescript.TypeScriptGroups; import org.testng.Assert; import org.testng.annotations.Test; @@ -19,12 +21,13 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import static org.assertj.core.api.Assertions.assertThat; - @Test(groups = {TypeScriptGroups.TYPESCRIPT, TypeScriptGroups.TYPESCRIPT_ANGULAR}) public class TypeScriptAngularClientCodegenTest { @Test @@ -124,7 +127,6 @@ public void testOperationIdParser() { CodegenOperation co1 = codegen.fromOperation("/another-fake/dummy/", "get", operation1, null); org.testng.Assert.assertEquals(co1.operationId, "_123testSpecialTags"); - } @Test @@ -148,7 +150,6 @@ public void testSnapshotVersion() { codegen.preprocessOpenAPI(openAPI); Assert.assertTrue(codegen.getNpmVersion().matches("^3.0.0-M1-SNAPSHOT.[0-9]{12}$")); - } @Test @@ -172,7 +173,6 @@ public void testWithoutSnapshotVersion() { codegen.preprocessOpenAPI(openAPI); Assert.assertTrue(codegen.getNpmVersion().matches("^3.0.0-M1$")); - } @Test @@ -500,9 +500,9 @@ public void testOpenIdCredentialsAreSet() throws IOException { // WHEN final CodegenConfigurator configurator = new CodegenConfigurator() - .setGeneratorName("typescript-angular") - .setInputSpec(specPath) - .setOutputDir(output.getAbsolutePath().replace("\\", "/")); + .setGeneratorName("typescript-angular") + .setInputSpec(specPath) + .setOutputDir(output.getAbsolutePath().replace("\\", "/")); final ClientOptInput clientOptInput = configurator.toClientOptInput(); @@ -514,4 +514,27 @@ public void testOpenIdCredentialsAreSet() throws IOException { String credentialsSet = "localVarHeaders = this.configuration.addCredentialToHeaders('oidc', 'Authorization', localVarHeaders, 'Bearer ');"; assertThat(fileContents).contains(credentialsSet); } + + @Test + public void testMergingImports() { + TypeScriptAngularClientCodegen codegen = new TypeScriptAngularClientCodegen(); + + List> imports = new ArrayList<>(); + imports.add(Map.of("classname", "type1", "import", "npmPackage")); + imports.add(Map.of("classname", "type2", "import", "npmPackage")); + imports.add(Map.of("classname", "type3", "import", "npmPackage2")); + OperationMap operation = new OperationMap(); + operation.setClassname("classname"); + operation.setOperation(List.of()); + OperationsMap operationsMap = new OperationsMap(); + operationsMap.setImports(imports); + operationsMap.setOperation(operation); + + OperationsMap result = codegen.postProcessOperationsWithModels(operationsMap, List.of()); + + assertThat(result.getImports()).containsExactlyInAnyOrder( + Map.of("classname", "type1, type2", "filename", "npmPackage", "import", "npmPackage"), + Map.of("classname", "type3", "filename", "npmPackage2", "import", "npmPackage2") + ); + } }