Skip to content

Commit

Permalink
Helidon Se server enhancements (OpenAPITools#49)
Browse files Browse the repository at this point in the history
* add gradleProject and useAbstractClass options, generate concrete implementation classes, interfaces for API are used by default

* change old tests, add new tests, change behaviour related to the fullProject option

Signed-off-by: aserkes <andrii.serkes@oracle.com>
  • Loading branch information
aserkes authored Sep 2, 2022
1 parent 7ec59be commit 74cb874
Show file tree
Hide file tree
Showing 10 changed files with 272 additions and 131 deletions.
13 changes: 1 addition & 12 deletions docs/generators/java-helidon-server.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,12 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|bigDecimalAsString|Treat BigDecimal values as Strings to avoid precision loss.| |false|
|booleanGetterPrefix|Set booleanGetterPrefix| |get|
|dateLibrary|Option. Date library to use|<dl><dt>**joda**</dt><dd>Joda (for legacy app only)</dd><dt>**legacy**</dt><dd>Legacy java.util.Date</dd><dt>**java8-localdatetime**</dt><dd>Java 8 using LocalDateTime (for legacy app only)</dd><dt>**java8**</dt><dd>Java 8 native JSR310 (preferred for jdk 1.8+)</dd></dl>|java8|
|developerEmail|developer email in generated pom.xml| |team@openapitools.org|
|developerName|developer name in generated pom.xml| |OpenAPI-Generator Contributors|
|developerOrganization|developer organization in generated pom.xml| |OpenAPITools.org|
|developerOrganizationUrl|developer organization URL in generated pom.xml| |http://openapitools.org|
|disableHtmlEscaping|Disable HTML escaping of JSON strings when using gson (needed to avoid problems with byte[] fields)| |false|
|disallowAdditionalPropertiesIfNotPresent|If false, the 'additionalProperties' implementation (set to true by default) is compliant with the OAS and JSON schema specifications. If true (default), keep the old (incorrect) behaviour that 'additionalProperties' is set to false by default.|<dl><dt>**false**</dt><dd>The 'additionalProperties' implementation is compliant with the OAS and JSON schema specifications.</dd><dt>**true**</dt><dd>Keep the old (incorrect) behaviour that 'additionalProperties' is set to false by default.</dd></dl>|true|
|discriminatorCaseSensitive|Whether the discriminator value lookup should be case-sensitive or not. This option only works for Java API client| |true|
|ensureUniqueParams|Whether to ensure parameter names are unique in an operation (rename parameters that are not).| |true|
|enumUnknownDefaultCase|If the server adds new enum cases, that are unknown by an old spec/client, the client will fail to parse the network response.With this option enabled, each enum will have a new case, 'unknown_default_open_api', so that when the server sends an enum case that is not known by the client/spec, they can safely fallback to this case.|<dl><dt>**false**</dt><dd>No changes to the enum's are made, this is the default option.</dd><dt>**true**</dt><dd>With this option enabled, each enum will have a new case, 'unknown_default_open_api', so that when the enum case sent by the server is not known by the client/spec, can safely be decoded to this case.</dd></dl>|false|
|fullJavaUtil|whether to use fully qualified name for classes under java.util. This option only works for Java API client| |false|
|fullProject|Whether to generate full project with registered services and configured routing| |false|
|fullProject|If set to true, it will generate all files; if set to false, it will only generate API files. If unspecified, the behavior depends on whether a project exists or not: if it does not, same as true; if it does, same as false. Note that test files are never overwritten.| ||
|groupId|groupId in generated pom.xml| |org.openapitools|
|helidonVersion|Helidon version for generated code| |2.5.2|
|hideGenerationTimestamp|Hides the generation timestamp when files are generated.| |false|
Expand All @@ -53,14 +48,8 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|licenseUrl|The URL of the license| |http://unlicense.org|
|modelPackage|package for generated models| |org.openapitools.server.model|
|openApiNullable|Enable OpenAPI Jackson Nullable library| |true|
|parentArtifactId|parent artifactId in generated pom N.B. parentGroupId, parentArtifactId and parentVersion must all be specified for any of them to take effect| |null|
|parentGroupId|parent groupId in generated pom N.B. parentGroupId, parentArtifactId and parentVersion must all be specified for any of them to take effect| |null|
|parentVersion|parent version in generated pom N.B. parentGroupId, parentArtifactId and parentVersion must all be specified for any of them to take effect| |null|
|performBeanValidation|Perform BeanValidation| |false|
|prependFormOrBodyParameters|Add form or body parameters to the beginning of the parameter list.| |false|
|scmConnection|SCM connection in generated pom.xml| |scm:git:git@github.com:openapitools/openapi-generator.git|
|scmDeveloperConnection|SCM developer connection in generated pom.xml| |scm:git:git@github.com:openapitools/openapi-generator.git|
|scmUrl|SCM URL in generated pom.xml| |https://github.com/openapitools/openapi-generator|
|serializableModel|boolean - toggle &quot;implements Serializable&quot; for generated models| |false|
|serializationLibrary|Serialization library, defaults to Jackson|<dl><dt>**jsonb**</dt><dd>Use JSON-B as serialization library</dd><dt>**jackson**</dt><dd>Use Jackson as serialization library</dd></dl>|null|
|snapshotVersion|Uses a SNAPSHOT version.|<dl><dt>**true**</dt><dd>Use a SnapShot Version</dd><dt>**false**</dt><dd>Use a Release Version</dd></dl>|null|
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
package org.openapitools.codegen.languages;

import java.io.File;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
Expand Down Expand Up @@ -47,14 +50,19 @@ public class JavaHelidonServerCodegen extends JavaHelidonCommonCodegen {

private final Logger LOGGER = LoggerFactory.getLogger(JavaHelidonServerCodegen.class);

//TODO remove it if MP does not support it
public static final String INTERFACE_ONLY = "interfaceOnly";
public static final String USE_ABSTRACT_CLASS = "useAbstractClass";
public static final String GRADLE_PROJECT = "gradleProject";

protected boolean useBeanValidation = true;
protected String implFolder = "src/main/java";
protected String serializationLibrary = null;

//TODO remove it if MP does not support it
private boolean interfaceOnly = false;
private boolean fullProject = false;
private boolean useAbstractClass = false;
private boolean gradleProject = false;

public JavaHelidonServerCodegen() {
super();
Expand All @@ -71,7 +79,7 @@ public JavaHelidonServerCodegen() {
artifactId = "openapi-java-server";
apiPackage = invokerPackage + ".api";
modelPackage = invokerPackage + ".model";
sourceFolder = "src" + File.separator + "main"+ File.separator + "java";
sourceFolder = "src" + File.separator + "main" + File.separator + "java";

// clioOptions default redefinition need to be updated
updateOption(CodegenConstants.INVOKER_PACKAGE, this.getInvokerPackage());
Expand All @@ -85,6 +93,10 @@ public JavaHelidonServerCodegen() {
cliOptions.add(CliOption.newBoolean(PERFORM_BEANVALIDATION, "Perform BeanValidation"));
cliOptions.add(CliOption.newBoolean(INTERFACE_ONLY,
"Whether to generate only API interface stubs without the server files.", interfaceOnly));
cliOptions.add(CliOption.newBoolean(USE_ABSTRACT_CLASS,
"Whether to generate abstract classes for REST API instead of interfaces.", useAbstractClass));
cliOptions.add(CliOption.newBoolean(GRADLE_PROJECT,
"Whether to generate gradle project instead of maven.", gradleProject));

// clear model and api doc template as this codegen
// does not support auto-generated markdown doc at the moment
Expand Down Expand Up @@ -126,15 +138,22 @@ public void processOpts() {
supportingFiles.clear();
dateLibrary = "java8";

supportingFiles.add(new SupportingFile("pom.mustache", "", "pom.xml"));
supportingFiles.add(new SupportingFile("README.mustache", "", "README.md"));
supportingFiles.add(new SupportingFile("openapi.mustache",
("src/main/resources/META-INF").replace("/", java.io.File.separator), "openapi.yml"));
supportingFiles.add(new SupportingFile("logging.mustache",
("src.main.resources").replace(".", java.io.File.separator), "logging.properties"));
supportingFiles.add(new SupportingFile("package-info.mustache",
(sourceFolder + File.separator + invokerPackage).replace(".", java.io.File.separator),
"package-info.java"));
SupportingFile pomFile = new SupportingFile("pom.mustache", "", "pom.xml");
SupportingFile readmeFile = new SupportingFile("README.mustache", "", "README.md");
SupportingFile openApiFile = new SupportingFile("openapi.mustache",
("src/main/resources/META-INF").replace("/", File.separator), "openapi.yml");
SupportingFile logFile = new SupportingFile("logging.mustache",
("src.main.resources").replace(".", File.separator), "logging.properties");
SupportingFile packageInfoFile = new SupportingFile("package-info.mustache",
(sourceFolder + File.separator + invokerPackage).replace(".", File.separator),
"package-info.java");
List<SupportingFile> modifiable = new ArrayList<>();
modifiable.add(pomFile);
modifiable.add(readmeFile);
modifiable.add(logFile);
modifiable.add(packageInfoFile);
List<SupportingFile> unmodifiable = new ArrayList<>();
unmodifiable.add(openApiFile);

if (additionalProperties.containsKey(USE_BEANVALIDATION)) {
this.setUseBeanValidation(convertPropertyToBoolean(USE_BEANVALIDATION));
Expand All @@ -152,11 +171,18 @@ public void processOpts() {
additionalProperties.remove(INTERFACE_ONLY);
}

if (additionalProperties.containsKey(FULL_PROJECT)) {
fullProject = Boolean.parseBoolean(additionalProperties.get(FULL_PROJECT).toString());
if (additionalProperties.containsKey(USE_ABSTRACT_CLASS)) {
useAbstractClass = Boolean.parseBoolean(additionalProperties.get(USE_ABSTRACT_CLASS).toString());
}
if (!fullProject) {
additionalProperties.remove(FULL_PROJECT);
if (!useAbstractClass) {
additionalProperties.remove(USE_ABSTRACT_CLASS);
}

if (additionalProperties.containsKey(GRADLE_PROJECT)) {
gradleProject = Boolean.parseBoolean(additionalProperties.get(GRADLE_PROJECT).toString());
}
if (!gradleProject) {
additionalProperties.remove(GRADLE_PROJECT);
}

if (!additionalProperties.containsKey(MICROPROFILE_ROOT_PACKAGE)) {
Expand All @@ -177,22 +203,28 @@ public void processOpts() {
String resourceFolder = "src" + File.separator + "main" + File.separator + "resources";
String metaInfFolder = resourceFolder + File.separator + "META-INF";
supportingFiles.add(new SupportingFile("RestApplication.mustache", invokerFolder, "RestApplication.java"));
supportingFiles.add(new SupportingFile("microprofile-config.properties.mustache", metaInfFolder, "microprofile-config.properties"));
supportingFiles.add(new SupportingFile("microprofile-config.properties.mustache", metaInfFolder, "microprofile" +
"-config.properties"));
supportingFiles.add(new SupportingFile("beans.xml.mustache", metaInfFolder, "beans.xml"));
processSupportingFiles(modifiable, unmodifiable);
} else if (isLibrary(HELIDON_SE)) {
artifactId = "openapi-helidon-se-server";
if (!interfaceOnly) {
supportingFiles.add(new SupportingFile("application.mustache",
("src.main.resources").replace(".", java.io.File.separator), "application.yaml"));
supportingFiles.add(new SupportingFile("mainTest.mustache",
(testFolder + File.separator + invokerPackage).replace(".", java.io.File.separator),
"MainTest.java"));
supportingFiles.add(new SupportingFile("main.mustache",
(sourceFolder + File.separator + invokerPackage).replace(".", java.io.File.separator),
"Main.java"));
supportingFiles.add(new SupportingFile("validatorUtils.mustache",
(sourceFolder + File.separator + apiPackage).replace(".", java.io.File.separator),
"ValidatorUtils.java"));

//TODO move this lines from this clause if MP will support it
addApiTemplateFiles();

modifiable.add(new SupportingFile("application.mustache",
("src.main.resources").replace(".", java.io.File.separator), "application.yaml"));
modifiable.add(new SupportingFile("mainTest.mustache",
(testFolder + File.separator + invokerPackage).replace(".", java.io.File.separator),
"MainTest.java"));
modifiable.add(new SupportingFile("main.mustache",
(sourceFolder + File.separator + invokerPackage).replace(".", java.io.File.separator),
"Main.java"));
unmodifiable.add(new SupportingFile("validatorUtils.mustache",
(sourceFolder + File.separator + apiPackage).replace(".", java.io.File.separator),
"ValidatorUtils.java"));
if (useAbstractClass) {
importMapping.put("Map", "java.util.Map");
importMapping.put("HashMap", "java.util.HashMap");
importMapping.put("InputStream", "java.io.InputStream");
Expand All @@ -205,9 +237,13 @@ public void processOpts() {
importMapping.put("ByteArrayInputStream", "java.io.ByteArrayInputStream");
}
importMapping.put("Handler", "io.helidon.webserver.Handler");
//TODO after adding gradle support for Helidon MP move these 2 lines from this clause to the top of the method
supportingFiles.add(new SupportingFile("build.gradle.mustache", "", "build.gradle"));
supportingFiles.add(new SupportingFile("settings.gradle.mustache", "", "settings.gradle"));
//TODO after adding gradle support for Helidon MP move these lines from this clause to the top of the method
if (gradleProject) {
modifiable.add(new SupportingFile("build.gradle.mustache", "", "build.gradle"));
modifiable.add(new SupportingFile("settings.gradle.mustache", "", "settings.gradle"));
modifiable.remove(pomFile);
}
processSupportingFiles(modifiable, unmodifiable);
} else if (isLibrary(HELIDON_NIMA)) {
throw new UnsupportedOperationException("Not implemented");
} else if (isLibrary(HELIDON_NIMA_ANNOTATIONS)) {
Expand Down Expand Up @@ -249,24 +285,34 @@ public void processOpts() {
}
}

private void addApiTemplateFiles() {
Boolean fullProject = !additionalProperties.containsKey(FULL_PROJECT) ? null :
Boolean.parseBoolean(additionalProperties.get(FULL_PROJECT).toString());
if (fullProject == null && !projectFilesExist()) {
apiTemplateFiles.put("apiImpl.mustache", "Impl.java");
} else if (Boolean.TRUE.equals(fullProject)) {
apiTemplateFiles.put("apiImpl.mustache", "Impl.java");
}
}

@Override
public CodegenOperation fromOperation(String path, String httpMethod, Operation operation, List<Server> servers) {
CodegenOperation codegenOperation = super.fromOperation(path, httpMethod, operation, servers);
if (HELIDON_SE.equals(getLibrary())) {
if (additionalProperties.containsKey(JACKSON)){
if (additionalProperties.containsKey(JACKSON)) {
codegenOperation.imports.add("ObjectMapper");
}
if (additionalProperties.containsKey(SERIALIZATION_LIBRARY_JSONB)){
if (additionalProperties.containsKey(SERIALIZATION_LIBRARY_JSONB)) {
codegenOperation.imports.add("Jsonb");
codegenOperation.imports.add("JsonbBuilder");
}
if (codegenOperation.bodyParam != null) {
codegenOperation.imports.add("Handler");
}
if (codegenOperation.queryParams.size() > 0 && !interfaceOnly) {
if (codegenOperation.queryParams.size() > 0 && useAbstractClass) {
codegenOperation.imports.add("List");
}
if (codegenOperation.formParams.size() > 0 && !interfaceOnly) {
if (codegenOperation.formParams.size() > 0 && useAbstractClass) {
codegenOperation.imports.add("Map");
codegenOperation.imports.add("HashMap");
codegenOperation.imports.add("InputStream");
Expand Down Expand Up @@ -376,5 +422,19 @@ public void setSerializationLibrary(String serializationLibrary) {
throw new IllegalArgumentException("Unexpected serializationLibrary value: " + serializationLibrary);
}
}

/**
* Check if pom file and src directory already exist.
*
* @return outcome of test
*/
@Override
protected boolean projectFilesExist() {
Path projectFolder = Paths.get(getOutputTestFolder());
Path pom = projectFolder.resolve("pom.xml");
Path buildGradle = projectFolder.resolve("build.gradle");
Path src = projectFolder.resolve(Paths.get(sourceFolder, invokerPackage.replace('.', File.separatorChar)));
return (pom.toFile().exists() || buildGradle.toFile().exists()) && src.toFile().exists();
}
}

Loading

0 comments on commit 74cb874

Please sign in to comment.