diff --git a/modules/openapi-generator/pom.xml b/modules/openapi-generator/pom.xml index 49d4b81d07aab..5e64320be74d9 100644 --- a/modules/openapi-generator/pom.xml +++ b/modules/openapi-generator/pom.xml @@ -459,6 +459,11 @@ lombok ${lombok.version} + + org.yaml + snakeyaml + ${snakeyaml.version} + 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 420225325f640..68a53f5aff884 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 @@ -19,6 +19,7 @@ import io.swagger.v3.oas.models.media.Schema; import lombok.AccessLevel; +import lombok.Data; import lombok.Getter; import lombok.Setter; import org.openapitools.codegen.*; @@ -31,6 +32,7 @@ import org.openapitools.codegen.model.OperationsMap; import org.openapitools.codegen.utils.ModelUtils; import org.openapitools.codegen.utils.SemVer; +import org.openapitools.codegen.utils.YamlConfigUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -76,6 +78,10 @@ public static enum PROVIDED_IN_LEVEL {none, root, any, platform} public static final String STRING_ENUMS_DESC = "Generate string enums instead of objects for enum values."; public static final String QUERY_PARAM_OBJECT_FORMAT = "queryParamObjectFormat"; public static final String USE_SQUARE_BRACKETS_IN_ARRAY_NAMES = "useSquareBracketsInArrayNames"; + public static final String TYPESCRIPT_VERSION = "typescriptVersion"; + public static final String RXJS_VERSION = "rxjsVersion"; + public static final String NGPACKAGR_VERSION = "ngPackagrVersion "; + public static final String ZONEJS_VERSION = "zoneJsVersion"; protected String ngVersion = "19.0.0"; @Getter @Setter @@ -144,6 +150,10 @@ public TypeScriptAngularClientCodegen() { this.cliOptions.add(new CliOption(STRING_ENUMS, STRING_ENUMS_DESC).defaultValue(String.valueOf(this.stringEnums))); this.cliOptions.add(new CliOption(QUERY_PARAM_OBJECT_FORMAT, "The format for query param objects: 'dot', 'json', 'key'.").defaultValue(this.queryParamObjectFormat.name())); this.cliOptions.add(CliOption.newBoolean(USE_SQUARE_BRACKETS_IN_ARRAY_NAMES, "Setting this property to true will add brackets to array attribute names, e.g. my_values[].", false)); + this.cliOptions.add(new CliOption(TYPESCRIPT_VERSION, "The version of typescript compatible with Angular (see ngVersion option).")); + this.cliOptions.add(new CliOption(RXJS_VERSION, "The version of RxJS compatible with Angular (see ngVersion option).")); + this.cliOptions.add(new CliOption(NGPACKAGR_VERSION, "The version of ng-packagr compatible with Angular (see ngVersion option).")); + this.cliOptions.add(new CliOption(ZONEJS_VERSION, "The version of zone.js compatible with Angular (see ngVersion option).")); } @Override @@ -286,135 +296,53 @@ public void processOpts() { } + @Data + static class AngularDependencies { + String tsVersion; + String rxjsVersion; + String ngPackagrVersion; + String zonejsVersion; + String tsickleVersion; + } + private void addNpmPackageGeneration(SemVer ngVersion) { if (additionalProperties.containsKey(NPM_REPOSITORY)) { this.setNpmRepository(additionalProperties.get(NPM_REPOSITORY).toString()); } - // Set the typescript version compatible to the Angular version - // based on https://angular.dev/reference/versions - if (ngVersion.atLeast("19.0.0")) { - additionalProperties.put("tsVersion", ">=5.5.0 <5.7.0"); - } else if (ngVersion.atLeast("18.1.0")) { - additionalProperties.put("tsVersion", ">=5.4.0 <5.6.0"); - } else if (ngVersion.atLeast("18.0.0")) { - additionalProperties.put("tsVersion", ">=5.4.0 <5.5.0"); - } else if (ngVersion.atLeast("17.0.0")) { - additionalProperties.put("tsVersion", ">=4.9.3 <5.3.0"); - } else if (ngVersion.atLeast("16.1.0")) { - additionalProperties.put("tsVersion", ">=4.9.3 <5.2.0"); - } else if (ngVersion.atLeast("16.0.0")) { - additionalProperties.put("tsVersion", ">=4.9.3 <5.1.0"); - } else if (ngVersion.atLeast("15.0.0")) { - additionalProperties.put("tsVersion", ">=4.8.2 <4.10.0"); - } else if (ngVersion.atLeast("14.0.0")) { - additionalProperties.put("tsVersion", ">=4.6.0 <=4.8.0"); - } else if (ngVersion.atLeast("13.0.0")) { - additionalProperties.put("tsVersion", ">=4.4.2 <4.5.0"); - } else if (ngVersion.atLeast("12.0.0")) { - additionalProperties.put("tsVersion", ">=4.3.0 <4.4.0"); - } else if (ngVersion.atLeast("11.0.0")) { - additionalProperties.put("tsVersion", ">=4.0.0 <4.1.0"); - } else if (ngVersion.atLeast("10.0.0")) { - additionalProperties.put("tsVersion", ">=3.9.2 <4.0.0"); - } else if (ngVersion.atLeast("9.0.0")) { - additionalProperties.put("tsVersion", ">=3.6.0 <3.8.0"); - } else { - throw new IllegalArgumentException("Invalid ngVersion. Only Angular v9+ is supported."); - } - - // Set the rxJS version compatible to the Angular version - if (ngVersion.atLeast("19.0.0")) { - additionalProperties.put("rxjsVersion", "7.4.0"); - } else if (ngVersion.atLeast("18.0.0")) { - additionalProperties.put("rxjsVersion", "7.4.0"); - } else if (ngVersion.atLeast("17.0.0")) { - additionalProperties.put("rxjsVersion", "7.4.0"); - } else if (ngVersion.atLeast("16.0.0")) { - additionalProperties.put("rxjsVersion", "7.4.0"); - } else if (ngVersion.atLeast("15.0.0")) { - additionalProperties.put("rxjsVersion", "7.5.5"); - } else if (ngVersion.atLeast("14.0.0")) { - additionalProperties.put("rxjsVersion", "7.5.5"); - } else if (ngVersion.atLeast("13.0.0")) { - additionalProperties.put("rxjsVersion", "7.4.0"); - } else if (ngVersion.atLeast("10.0.0")) { - additionalProperties.put("rxjsVersion", "6.6.0"); - } else if (ngVersion.atLeast("9.0.0")) { - additionalProperties.put("rxjsVersion", "6.5.3"); - } + Map angularDependenciesByVersion = YamlConfigUtils.loadAsMap("typescript-angular/angularDependenciesByVersion.yaml", AngularDependencies.class); - supportingFiles.add(new SupportingFile("ng-package.mustache", getIndexDirectory(), "ng-package.json")); + AngularDependencies angularDependencies = angularDependenciesByVersion.entrySet().stream() + // we filter only config version above or equal the current one + .filter(versionMatrix -> ngVersion.atLeast(versionMatrix.getKey())) + // get can the latest version configured that match the current one + .max(Comparator.comparing(s -> new SemVer(s.getKey()))) + .map(Map.Entry::getValue) + .orElseThrow(() -> new IllegalArgumentException("Invalid ngVersion. Only Angular v9+ is supported.")); - // Specific ng-packagr configuration - if (ngVersion.atLeast("19.0.0")) { - additionalProperties.put("ngPackagrVersion", "19.0.0"); - } else if (ngVersion.atLeast("18.1.0")) { - additionalProperties.put("ngPackagrVersion", "18.1.0"); - // tsTickle is not required and there is no available version compatible with - // versions of TypeScript compatible with Angular 18. - } else if (ngVersion.atLeast("18.0.0")) { - additionalProperties.put("ngPackagrVersion", "18.0.0"); - // tsTickle is not required and there is no available version compatible with - // versions of TypeScript compatible with Angular 18. - } else if (ngVersion.atLeast("17.0.0")) { - additionalProperties.put("ngPackagrVersion", "17.0.3"); - // tsTickle is not required and there is no available version compatible with - // versions of TypeScript compatible with Angular 17. - } else if (ngVersion.atLeast("16.0.0")) { - additionalProperties.put("ngPackagrVersion", "16.0.0"); - // tsTickle is not required and there is no available version compatible with - // versions of TypeScript compatible with Angular 16. - } else if (ngVersion.atLeast("15.0.0")) { - additionalProperties.put("ngPackagrVersion", "15.0.2"); - // tsTickle is not required and there is no available version compatible with - // versions of TypeScript compatible with Angular 15. - } else if (ngVersion.atLeast("14.0.0")) { - additionalProperties.put("ngPackagrVersion", "14.0.2"); - additionalProperties.put("tsickleVersion", "0.46.3"); - } else if (ngVersion.atLeast("13.0.0")) { - additionalProperties.put("ngPackagrVersion", "13.0.3"); - additionalProperties.put("tsickleVersion", "0.43.0"); - } else if (ngVersion.atLeast("12.0.0")) { - additionalProperties.put("ngPackagrVersion", "12.2.1"); - additionalProperties.put("tsickleVersion", "0.43.0"); - } else if (ngVersion.atLeast("11.0.0")) { - additionalProperties.put("ngPackagrVersion", "11.0.2"); - additionalProperties.put("tsickleVersion", "0.39.1"); - } else if (ngVersion.atLeast("10.0.0")) { - additionalProperties.put("ngPackagrVersion", "10.0.3"); - additionalProperties.put("tsickleVersion", "0.39.1"); - } else if (ngVersion.atLeast("9.0.0")) { - additionalProperties.put("ngPackagrVersion", "9.0.1"); - additionalProperties.put("tsickleVersion", "0.38.0"); - } - - // set zone.js version - // based on https://github.com/angular/angular/blob/main/packages/core/package.json - if (ngVersion.atLeast("19.0.0")) { - additionalProperties.put("zonejsVersion", "0.15.0"); - } else if (ngVersion.atLeast("18.0.0")) { - additionalProperties.put("zonejsVersion", "0.14.7"); - } else if (ngVersion.atLeast("17.0.0")) { - additionalProperties.put("zonejsVersion", "0.14.0"); - } else if (ngVersion.atLeast("16.0.0")) { - additionalProperties.put("zonejsVersion", "0.13.0"); - } else if (ngVersion.atLeast("15.0.0")) { - additionalProperties.put("zonejsVersion", "0.11.5"); - } else if (ngVersion.atLeast("14.0.0")) { - additionalProperties.put("zonejsVersion", "0.11.5"); - } else if (ngVersion.atLeast("12.0.0")) { - additionalProperties.put("zonejsVersion", "0.11.4"); - } else if (ngVersion.atLeast("11.0.0")) { - additionalProperties.put("zonejsVersion", "0.11.3"); - } else if (ngVersion.atLeast("9.0.0")) { - additionalProperties.put("zonejsVersion", "0.10.2"); - } else if (ngVersion.atLeast("8.0.0")) { - additionalProperties.put("zonejsVersion", "0.9.1"); + additionalProperties.put("tsVersion", additionalProperties.containsKey(TYPESCRIPT_VERSION) + ? additionalProperties.containsKey(TYPESCRIPT_VERSION) + : angularDependencies.getTsVersion()); + + additionalProperties.put("rxjsVersion", additionalProperties.containsKey(RXJS_VERSION) + ? additionalProperties.containsKey(RXJS_VERSION) + : angularDependencies.getRxjsVersion()); + + additionalProperties.put("ngPackagrVersion", additionalProperties.containsKey(NGPACKAGR_VERSION) + ? additionalProperties.containsKey(NGPACKAGR_VERSION) + : angularDependencies.getNgPackagrVersion()); + + additionalProperties.put("zonejsVersion", additionalProperties.containsKey(ZONEJS_VERSION) + ? additionalProperties.containsKey(ZONEJS_VERSION) + : angularDependencies.getZonejsVersion()); + + if (angularDependencies.getTsickleVersion() != null) { + additionalProperties.put("tsickleVersion", angularDependencies.getTsickleVersion()); } //Files for building our lib + supportingFiles.add(new SupportingFile("ng-package.mustache", getIndexDirectory(), "ng-package.json")); supportingFiles.add(new SupportingFile("package.mustache", getIndexDirectory(), "package.json")); supportingFiles.add(new SupportingFile("tsconfig.mustache", getIndexDirectory(), "tsconfig.json")); } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/YamlConfigUtils.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/YamlConfigUtils.java new file mode 100644 index 0000000000000..eaa41f357c22f --- /dev/null +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/YamlConfigUtils.java @@ -0,0 +1,69 @@ +package org.openapitools.codegen.utils; + +import org.yaml.snakeyaml.LoaderOptions; +import org.yaml.snakeyaml.TypeDescription; +import org.yaml.snakeyaml.Yaml; +import org.yaml.snakeyaml.constructor.Constructor; +import org.yaml.snakeyaml.introspector.BeanAccess; +import org.yaml.snakeyaml.nodes.MappingNode; +import org.yaml.snakeyaml.nodes.Node; +import org.yaml.snakeyaml.nodes.Tag; + +import java.io.InputStream; +import java.util.Map; +import java.util.stream.Collectors; + +public class YamlConfigUtils { + + /** + * Load yaml config file as map + * + * @param configFile yaml config file to load + * @param clazz class of object to map data + * @param type of config object to generate + * @return config object generated + */ + public static Map loadAsMap(String configFile, Class clazz) { + LoaderOptions loaderOptions = new LoaderOptions(); + loaderOptions.setAllowDuplicateKeys(false); + + Yaml yaml = new Yaml(new MapConstructor(loaderOptions, clazz)); + yaml.setBeanAccess(BeanAccess.FIELD); + InputStream inputStream = YamlConfigUtils.class + .getClassLoader() + .getResourceAsStream(configFile); + + return yaml.load(inputStream); + } + + private static class MapConstructor extends Constructor { + private final TypeDescription itemType; + + public MapConstructor(LoaderOptions loaderOptions, Class clazz) { + super(loaderOptions); + this.rootTag = new Tag("root"); + itemType = new TypeDescription(clazz); + this.addTypeDescription(itemType); + } + + @Override + protected Object constructObject(Node node) { + if ("root".equals(node.getTag().getValue()) && node instanceof MappingNode) { + MappingNode mNode = (MappingNode) node; + return mNode.getValue().stream().collect( + Collectors.toMap( + t -> super.constructObject(t.getKeyNode()), + t -> { + Node child = t.getValueNode(); + child.setType(itemType.getType()); + return super.constructObject(child); + } + ) + ); + + } else { + return super.constructObject(node); + } + } + } +} \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/typescript-angular/angularDependenciesByVersion.yaml b/modules/openapi-generator/src/main/resources/typescript-angular/angularDependenciesByVersion.yaml new file mode 100644 index 0000000000000..26c53f39f9f9c --- /dev/null +++ b/modules/openapi-generator/src/main/resources/typescript-angular/angularDependenciesByVersion.yaml @@ -0,0 +1,74 @@ +# For future versions... +# Get typescript and rxjs version here: https://angular.dev/reference/versions +# Get zone.js version here: https://github.com/angular/angular/blob/main/packages/core/package.json +19.0.0: + tsVersion: '>=5.5.0 <5.7.0' + rxjsVersion: 7.4.0 + ngPackagrVersion: 19.0.0 + zonejsVersion: 0.15.0 +18.1.0: + tsVersion: '>=5.4.0 <5.6.0' + rxjsVersion: 7.4.0 + ngPackagrVersion: 18.1.0 + zonejsVersion: 0.14.7 +18.0.0: + tsVersion: '>=5.4.0 <5.5.0' + rxjsVersion: 7.4.0 + ngPackagrVersion: 18.0.0 + zonejsVersion: 0.14.7 +17.0.0: + tsVersion: '>=4.9.3 <5.3.0' + rxjsVersion: 7.4.0 + ngPackagrVersion: 17.0.3 + zonejsVersion: 0.14.0 +16.1.0: + tsVersion: '>=4.9.3 <5.2.0' + rxjsVersion: 7.4.0 + ngPackagrVersion: 16.0.0 + zonejsVersion: 0.13.0 +16.0.0: + tsVersion: '>=4.9.3 <5.1.0' + rxjsVersion: 7.4.0 + ngPackagrVersion: 16.0.0 + zonejsVersion: 0.13.0 +15.0.0: + tsVersion: '>=4.8.2 <4.10.0' + rxjsVersion: 7.5.5 + ngPackagrVersion: 15.0.2 + zonejsVersion: 0.11.5 +14.0.0: + tsVersion: '>=4.6.0 <=4.8.0' + rxjsVersion: 7.5.5 + ngPackagrVersion: 14.0.2 + zonejsVersion: 0.11.5 + tsickleVersion: 0.46.3 +13.0.0: + tsVersion: '>=4.4.2 <4.5.0' + rxjsVersion: 7.4.0 + ngPackagrVersion: 13.0.3 + zonejsVersion: 0.11.4 + tsickleVersion: 0.43.0 +12.0.0: + tsVersion: '>=4.3.0 <4.4.0' + rxjsVersion: 6.6.0 + ngPackagrVersion: 12.2.1 + zonejsVersion: 0.11.4 + tsickleVersion: 0.43.0 +11.0.0: + tsVersion: '>=4.0.0 <4.1.0' + rxjsVersion: 6.6.0 + ngPackagrVersion: 11.0.2 + zonejsVersion: 0.11.3 + tsickleVersion: 0.39.1 +10.0.0: + tsVersion: '>=3.9.2 <4.0.0' + rxjsVersion: 6.6.0 + ngPackagrVersion: 10.0.3 + zonejsVersion: 0.10.2 + tsickleVersion: 0.39.1 +9.0.0: + tsVersion: '>=3.6.0 <3.8.0' + rxjsVersion: 6.5.3 + ngPackagrVersion: 9.0.1 + zonejsVersion: 0.10.2 + tsickleVersion: 0.39.1 \ No newline at end of file diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/utils/YamlConfigUtilsTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/utils/YamlConfigUtilsTest.java new file mode 100644 index 0000000000000..e36eac81b80df --- /dev/null +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/utils/YamlConfigUtilsTest.java @@ -0,0 +1,45 @@ +package org.openapitools.codegen.utils; + +import lombok.Data; +import org.testng.annotations.Test; + +import java.util.List; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; + +public class YamlConfigUtilsTest { + + @Test + public void testLoadConfigAsMap() { + Map yamlConfig = YamlConfigUtils.loadAsMap("yamlConfigMap.yaml", YamlConfig.class); + assertThat(yamlConfig.keySet()).containsOnly("firstItem", "secondItem"); + + YamlConfig firstItem = yamlConfig.get("firstItem"); + assertThat(firstItem.getStringConfig()).isEqualTo("Hello"); + assertThat(firstItem.getNumberConfig()).isEqualTo(42); + assertThat(firstItem.getObjectConfig().getAttribut()).isEqualTo("openapi"); + assertThat(firstItem.getArrayConfig()).hasSize(2); + assertThat(firstItem.getArrayConfig().get(0)).isEqualTo("one"); + assertThat(firstItem.getArrayConfig().get(1)).isEqualTo("two"); + + YamlConfig secondItem = yamlConfig.get("secondItem"); + assertThat(secondItem.getStringConfig()).isEqualTo("World"); + assertThat(secondItem.getNumberConfig()).isNull(); + assertThat(secondItem.getObjectConfig()).isNull(); + assertThat(secondItem.getArrayConfig()).isNull(); + } + + @Data + static class YamlConfig { + private String stringConfig; + private Integer numberConfig; + private ObjectConfig objectConfig; + private List arrayConfig; + } + + @Data + static class ObjectConfig { + private String attribut; + } +} \ No newline at end of file diff --git a/modules/openapi-generator/src/test/resources/yamlConfigMap.yaml b/modules/openapi-generator/src/test/resources/yamlConfigMap.yaml new file mode 100644 index 0000000000000..47309d1973217 --- /dev/null +++ b/modules/openapi-generator/src/test/resources/yamlConfigMap.yaml @@ -0,0 +1,10 @@ +firstItem: + stringConfig: 'Hello' + numberConfig: 42 + objectConfig: + attribut: 'openapi' + arrayConfig: + - 'one' + - 'two' +secondItem: + stringConfig: 'World' \ No newline at end of file diff --git a/pom.xml b/pom.xml index 7bdf939e375ea..65aa5564bca4b 100644 --- a/pom.xml +++ b/pom.xml @@ -1246,6 +1246,7 @@ 1.4 4.6.1 1.7.36 + 2.3 3.1.12.2 io.swagger.parser.v3 2.1.22