Skip to content

Commit 1eaa1d9

Browse files
Fix angular typescript imports from same file or package being overwritten
1 parent 7a14a43 commit 1eaa1d9

3 files changed

Lines changed: 92 additions & 19 deletions

File tree

modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import org.apache.commons.io.IOCase;
3232
import org.apache.commons.lang3.ObjectUtils;
3333
import org.apache.commons.lang3.StringUtils;
34+
import org.apache.commons.lang3.tuple.Pair;
3435
import org.openapitools.codegen.api.*;
3536
import org.openapitools.codegen.config.GlobalSettings;
3637
import org.openapitools.codegen.ignore.CodegenIgnoreProcessor;
@@ -1656,7 +1657,7 @@ private OperationsMap processOperations(CodegenConfig config, String tag, List<C
16561657
allImports.addAll(op.imports);
16571658
}
16581659

1659-
Map<String, String> mappings = getAllImportsMappings(allImports);
1660+
Set<Pair<String, String>> mappings = getAllImportMappings(allImports);
16601661
Set<Map<String, String>> imports = toImportsObjects(mappings);
16611662

16621663
//Some codegen implementations rely on a list interface for the imports
@@ -1734,6 +1735,16 @@ private Map<String, String> getAllImportsMappings(Set<String> allImports) {
17341735
return result;
17351736
}
17361737

1738+
private Set<Pair<String, String>> getAllImportMappings(Set<String> allImports) {
1739+
return allImports.stream().map(nextImport -> {
1740+
String mapping = config.importMapping().get(nextImport);
1741+
if (mapping != null) {
1742+
return Pair.of(mapping, nextImport);
1743+
}
1744+
return Pair.of(config.toModelImport(nextImport), nextImport);
1745+
}).collect(Collectors.toSet());
1746+
}
1747+
17371748
/**
17381749
* Using an import map created via {@link #getAllImportsMappings(Set)} to build a list import objects.
17391750
* 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<Map<String, String>> toImportsObjects(Map<String, String> mappedImpo
17551766
return result;
17561767
}
17571768

1769+
private Set<Map<String, String>> toImportsObjects(Set<Pair<String, String>> importPairs) {
1770+
Set<Map<String, String>> result = new TreeSet<>(
1771+
Comparator.comparing(o -> o.get("classname"))
1772+
);
1773+
1774+
importPairs.forEach((pair) -> {
1775+
Map<String, String> im = new LinkedHashMap<>();
1776+
im.put("import", pair.getLeft());
1777+
im.put("classname", pair.getRight());
1778+
result.add(im);
1779+
});
1780+
return result;
1781+
}
1782+
17581783
private ModelsMap processModels(CodegenConfig config, Map<String, Schema> definitions) {
17591784
ModelsMap objs = new ModelsMap();
17601785
objs.put("package", config.modelPackage());

modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/TypeScriptAngularClientCodegen.java

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import lombok.Data;
2323
import lombok.Getter;
2424
import lombok.Setter;
25+
import org.apache.commons.lang3.tuple.Pair;
2526
import org.openapitools.codegen.*;
2627
import org.openapitools.codegen.meta.features.DocumentationFeature;
2728
import org.openapitools.codegen.meta.features.GlobalFeature;
@@ -476,13 +477,13 @@ public OperationsMap postProcessOperationsWithModels(OperationsMap operations, L
476477
operations.put("hasSomeEncodableParams", hasSomeEncodableParams);
477478

478479
// Add additional filename information for model imports in the services
479-
List<Map<String, String>> imports = operations.getImports();
480-
for (Map<String, String> im : imports) {
480+
List<Map<String, String>> mergedImports = mergeImports(operations.getImports());
481+
for (Map<String, String> im : mergedImports) {
481482
// This property is not used in the templates any more, subject for removal
482483
im.put("filename", im.get("import"));
483484
im.put("classname", im.get("classname"));
484485
}
485-
486+
operations.setImports(mergedImports);
486487
return operations;
487488
}
488489

@@ -580,6 +581,28 @@ private List<Map<String, String>> toTsImports(CodegenModel cm, Set<String> impor
580581
return tsImports;
581582
}
582583

584+
/**
585+
* Merge imports that belong to the same file
586+
*/
587+
private List<Map<String, String>> mergeImports(List<Map<String, String>> imports) {
588+
Map<String, String> importLookup = new HashMap<>();
589+
imports.forEach(importMap -> {
590+
String importPackage = importMap.get("import");
591+
String importType = importMap.get("classname");
592+
String existingImportType = importLookup.get(importPackage);
593+
if (existingImportType != null && !existingImportType.equals(importType)) {
594+
String newImportType = String.join(", ", existingImportType, importType);
595+
importLookup.put(importPackage, newImportType);
596+
} else {
597+
importLookup.put(importPackage, importType);
598+
}
599+
});
600+
601+
return importLookup.entrySet().stream()
602+
.map(entry -> new HashMap<>(Map.of("import", entry.getKey(), "classname", entry.getValue())))
603+
.collect(Collectors.toList());
604+
}
605+
583606
@Override
584607
public String toApiName(String name) {
585608
if (name.length() == 0) {

modules/openapi-generator/src/test/java/org/openapitools/codegen/typescript/typescriptangular/TypeScriptAngularClientCodegenTest.java

Lines changed: 40 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
import org.openapitools.codegen.*;
1212
import org.openapitools.codegen.config.CodegenConfigurator;
1313
import org.openapitools.codegen.languages.TypeScriptAngularClientCodegen;
14+
import org.openapitools.codegen.model.OperationMap;
15+
import org.openapitools.codegen.model.OperationsMap;
1416
import org.openapitools.codegen.typescript.TypeScriptGroups;
1517
import org.testng.Assert;
1618
import org.testng.annotations.Test;
@@ -19,12 +21,13 @@
1921
import java.io.IOException;
2022
import java.nio.file.Files;
2123
import java.nio.file.Paths;
24+
import java.util.ArrayList;
2225
import java.util.HashMap;
26+
import java.util.List;
2327
import java.util.Map;
2428

2529
import static org.assertj.core.api.Assertions.assertThat;
2630

27-
2831
@Test(groups = {TypeScriptGroups.TYPESCRIPT, TypeScriptGroups.TYPESCRIPT_ANGULAR})
2932
public class TypeScriptAngularClientCodegenTest {
3033
@Test
@@ -117,14 +120,14 @@ public void testModelFileSuffix() {
117120
@Test
118121
public void testOperationIdParser() {
119122
OpenAPI openAPI = TestUtils.createOpenAPI();
120-
Operation operation1 = new Operation().operationId("123_test_@#$%_special_tags").responses(new ApiResponses().addApiResponse("201", new ApiResponse().description("OK")));
123+
Operation operation1 = new Operation().operationId("123_test_@#$%_special_tags").responses(new ApiResponses().addApiResponse("201"
124+
, new ApiResponse().description("OK")));
121125
openAPI.path("another-fake/dummy/", new PathItem().get(operation1));
122126
final TypeScriptAngularClientCodegen codegen = new TypeScriptAngularClientCodegen();
123127
codegen.setOpenAPI(openAPI);
124128

125129
CodegenOperation co1 = codegen.fromOperation("/another-fake/dummy/", "get", operation1, null);
126130
org.testng.Assert.assertEquals(co1.operationId, "_123testSpecialTags");
127-
128131
}
129132

130133
@Test
@@ -148,7 +151,6 @@ public void testSnapshotVersion() {
148151
codegen.preprocessOpenAPI(openAPI);
149152

150153
Assert.assertTrue(codegen.getNpmVersion().matches("^3.0.0-M1-SNAPSHOT.[0-9]{12}$"));
151-
152154
}
153155

154156
@Test
@@ -172,7 +174,6 @@ public void testWithoutSnapshotVersion() {
172174
codegen.preprocessOpenAPI(openAPI);
173175

174176
Assert.assertTrue(codegen.getNpmVersion().matches("^3.0.0-M1$"));
175-
176177
}
177178

178179
@Test
@@ -426,9 +427,9 @@ public void testBasePath() throws IOException {
426427

427428
// WHEN
428429
final CodegenConfigurator configurator = new CodegenConfigurator()
429-
.setGeneratorName("typescript-angular")
430-
.setInputSpec(specPath)
431-
.setOutputDir(output.getAbsolutePath().replace("\\", "/"));
430+
.setGeneratorName("typescript-angular")
431+
.setInputSpec(specPath)
432+
.setOutputDir(output.getAbsolutePath().replace("\\", "/"));
432433

433434
final ClientOptInput clientOptInput = configurator.toClientOptInput();
434435

@@ -450,9 +451,9 @@ public void testEnumAsConst() throws IOException {
450451

451452
// WHEN
452453
final CodegenConfigurator configurator = new CodegenConfigurator()
453-
.setGeneratorName("typescript-angular")
454-
.setInputSpec(specPath)
455-
.setOutputDir(output.getAbsolutePath().replace("\\", "/"));
454+
.setGeneratorName("typescript-angular")
455+
.setInputSpec(specPath)
456+
.setOutputDir(output.getAbsolutePath().replace("\\", "/"));
456457

457458
final ClientOptInput clientOptInput = configurator.toClientOptInput();
458459

@@ -475,9 +476,9 @@ public void testDeepObject() throws IOException {
475476

476477
// WHEN
477478
final CodegenConfigurator configurator = new CodegenConfigurator()
478-
.setGeneratorName("typescript-angular")
479-
.setInputSpec(specPath)
480-
.setOutputDir(output.getAbsolutePath().replace("\\", "/"));
479+
.setGeneratorName("typescript-angular")
480+
.setInputSpec(specPath)
481+
.setOutputDir(output.getAbsolutePath().replace("\\", "/"));
481482

482483
final ClientOptInput clientOptInput = configurator.toClientOptInput();
483484

@@ -511,7 +512,31 @@ public void testOpenIdCredentialsAreSet() throws IOException {
511512

512513
//THEN
513514
final String fileContents = Files.readString(Paths.get(output + "/api/default.service.ts"));
514-
String credentialsSet = "localVarHeaders = this.configuration.addCredentialToHeaders('oidc', 'Authorization', localVarHeaders, 'Bearer ');";
515+
String credentialsSet = "localVarHeaders = this.configuration.addCredentialToHeaders('oidc', 'Authorization', localVarHeaders, " +
516+
"'Bearer ');";
515517
assertThat(fileContents).contains(credentialsSet);
516518
}
519+
520+
@Test
521+
public void testMergingImports() {
522+
TypeScriptAngularClientCodegen codegen = new TypeScriptAngularClientCodegen();
523+
524+
List<Map<String, String>> imports = new ArrayList<>();
525+
imports.add(Map.of("classname", "type1", "import", "npmPackage"));
526+
imports.add(Map.of("classname", "type2", "import", "npmPackage"));
527+
imports.add(Map.of("classname", "type3", "import", "npmPackage2"));
528+
OperationMap operation = new OperationMap();
529+
operation.setClassname("classname");
530+
operation.setOperation(List.of());
531+
OperationsMap operationsMap = new OperationsMap();
532+
operationsMap.setImports(imports);
533+
operationsMap.setOperation(operation);
534+
535+
OperationsMap result = codegen.postProcessOperationsWithModels(operationsMap, List.of());
536+
537+
assertThat(result.getImports()).containsExactlyInAnyOrder(
538+
Map.of("classname", "type1, type2", "filename", "npmPackage", "import", "npmPackage"),
539+
Map.of("classname", "type3", "filename", "npmPackage2", "import", "npmPackage2")
540+
);
541+
}
517542
}

0 commit comments

Comments
 (0)