Skip to content

Commit

Permalink
feat(typescript-angular): add options to override angular deps + refa…
Browse files Browse the repository at this point in the history
…ctor deps definition

add tsVersion, rxjsVersion, ngPackagrVersion and zonejsVersion options to override default config if new version of Angular is available but not yet implemented in openapi-generator
refactor Angular dependencies definition in separate readable yaml config file

fix #20204
  • Loading branch information
Thibaud SOWA committed Nov 29, 2024
1 parent 21d19fe commit e2746e2
Show file tree
Hide file tree
Showing 7 changed files with 250 additions and 118 deletions.
5 changes: 5 additions & 0 deletions modules/openapi-generator/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,11 @@
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>${snakeyaml.version}</version>
</dependency>
</dependencies>
<repositories>
<repository>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.*;
Expand All @@ -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;

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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<String, AngularDependencies> 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"));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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 <T> type of config object to generate
* @return config object generated
*/
public static <T> Map<String, T> loadAsMap(String configFile, Class<T> 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 <T> MapConstructor(LoaderOptions loaderOptions, Class<T> 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);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -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
Loading

0 comments on commit e2746e2

Please sign in to comment.