From 2a217a299352352428cf04e65883d4a1f45c1781 Mon Sep 17 00:00:00 2001 From: Thibault Vallin Date: Wed, 7 Sep 2022 17:34:28 +0200 Subject: [PATCH] [Server MP] REST API files only + class/interface implementation (#50) --- .../languages/JavaHelidonServerCodegen.java | 16 ++----- .../server/libraries/mp/api.mustache | 4 +- .../server/libraries/mp/apiAbstract.mustache | 2 +- .../server/libraries/mp/apiImpl.mustache | 32 +++++++++++++ .../JavaHelidonMpServerCodegenTest.java | 47 ++++++++++++++++--- .../FunctionalHelidonMPServerTest.java | 38 +++++++++++++-- 6 files changed, 115 insertions(+), 24 deletions(-) create mode 100644 modules/openapi-generator/src/main/resources/java-helidon/server/libraries/mp/apiImpl.mustache diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaHelidonServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaHelidonServerCodegen.java index 4d0436bc2b90..048c5dfa7fa7 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaHelidonServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaHelidonServerCodegen.java @@ -50,7 +50,6 @@ 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"; @@ -59,7 +58,6 @@ public class JavaHelidonServerCodegen extends JavaHelidonCommonCodegen { 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 useAbstractClass = false; private boolean gradleProject = false; @@ -138,6 +136,7 @@ public void processOpts() { supportingFiles.clear(); dateLibrary = "java8"; + addApiTemplateFiles(); SupportingFile pomFile = new SupportingFile("pom.mustache", "", "pom.xml"); SupportingFile readmeFile = new SupportingFile("README.mustache", "", "README.md"); SupportingFile openApiFile = new SupportingFile("openapi.mustache", @@ -183,6 +182,10 @@ public void processOpts() { } if (!gradleProject) { additionalProperties.remove(GRADLE_PROJECT); + } else { + modifiable.add(new SupportingFile("build.gradle.mustache", "", "build.gradle")); + modifiable.add(new SupportingFile("settings.gradle.mustache", "", "settings.gradle")); + modifiable.remove(pomFile); } if (!additionalProperties.containsKey(MICROPROFILE_ROOT_PACKAGE)) { @@ -210,9 +213,6 @@ public void processOpts() { } else if (isLibrary(HELIDON_SE)) { artifactId = "openapi-helidon-se-server"; - //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", @@ -237,12 +237,6 @@ 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 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"); diff --git a/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/mp/api.mustache b/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/mp/api.mustache index 83949787a0d0..31bedcb7a645 100644 --- a/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/mp/api.mustache +++ b/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/mp/api.mustache @@ -22,11 +22,11 @@ import javax.validation.Valid;{{/useBeanValidation}} @Consumes({ {{#consumes}}"{{{mediaType}}}"{{^-last}}, {{/-last}}{{/consumes}} }){{/hasConsumes}}{{#hasProduces}} @Produces({ {{#produces}}"{{{mediaType}}}"{{^-last}}, {{/-last}}{{/produces}} }){{/hasProduces}} {{>generatedAnnotation}} -public {{#interfaceOnly}}interface{{/interfaceOnly}}{{^interfaceOnly}}{{^fullProject}}abstract {{/fullProject}}class{{/interfaceOnly}} {{classname}} { +public {{#interfaceOnly}}interface{{/interfaceOnly}}{{^interfaceOnly}}{{#useAbstractClass}}abstract {{/useAbstractClass}}class{{/interfaceOnly}} {{classname}} { {{#operations}} {{#operation}} -{{#interfaceOnly}}{{>apiAbstract}}{{/interfaceOnly}}{{^interfaceOnly}}{{^fullProject}}{{>apiAbstract}}{{/fullProject}}{{#fullProject}}{{>apiMethod}}{{/fullProject}}{{/interfaceOnly}} +{{#interfaceOnly}}{{>apiAbstract}}{{/interfaceOnly}}{{^interfaceOnly}}{{#useAbstractClass}}{{>apiAbstract}}{{/useAbstractClass}}{{^useAbstractClass}}{{>apiMethod}}{{/useAbstractClass}}{{/interfaceOnly}} {{/operation}} } {{/operations}} diff --git a/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/mp/apiAbstract.mustache b/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/mp/apiAbstract.mustache index 5aa8d5548fe6..bbed9724d481 100644 --- a/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/mp/apiAbstract.mustache +++ b/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/mp/apiAbstract.mustache @@ -2,4 +2,4 @@ @Path("{{{path}}}"){{/subresourceOperation}}{{#hasConsumes}} @Consumes({ {{#consumes}}"{{{mediaType}}}"{{^-last}}, {{/-last}}{{/consumes}} }){{/hasConsumes}}{{#hasProduces}} @Produces({ {{#produces}}"{{{mediaType}}}"{{^-last}}, {{/-last}}{{/produces}} }){{/hasProduces}} - abstract {{#supportAsync}}{{>returnAsyncTypeInterface}}{{/supportAsync}}{{^supportAsync}}{{#returnResponse}}Response{{/returnResponse}}{{^returnResponse}}{{{returnType}}}{{/returnResponse}}{{/supportAsync}} {{nickname}}({{#allParams}}{{>queryParams}}{{>pathParams}}{{>cookieParams}}{{>headerParams}}{{>bodyParams}}{{>formParams}}{{^-last}}, {{/-last}}{{/allParams}}); \ No newline at end of file + abstract {{#supportAsync}}{{>returnAsyncTypeInterface}}{{/supportAsync}}{{^supportAsync}}Response{{/supportAsync}} {{nickname}}({{#allParams}}{{>queryParams}}{{>pathParams}}{{>cookieParams}}{{>headerParams}}{{>bodyParams}}{{>formParams}}{{^-last}}, {{/-last}}{{/allParams}}); \ No newline at end of file diff --git a/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/mp/apiImpl.mustache b/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/mp/apiImpl.mustache new file mode 100644 index 000000000000..437678bb8058 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/java-helidon/server/libraries/mp/apiImpl.mustache @@ -0,0 +1,32 @@ +{{>licenseInfo}} +package {{package}}; + +{{#imports}}import {{import}}; +{{/imports}} + +import {{rootJavaEEPackage}}.ws.rs.*; +import {{rootJavaEEPackage}}.ws.rs.core.Response; + +{{#supportAsync}} +import java.util.concurrent.CompletionStage; +import java.util.concurrent.CompletableFuture; +{{/supportAsync}} + +import java.io.InputStream; +import java.util.Map; +import java.util.List; +{{#useBeanValidation}}import javax.validation.constraints.*; +import javax.validation.Valid;{{/useBeanValidation}} + +@Path("{{commonPath}}"){{#hasConsumes}} +@Consumes({ {{#consumes}}"{{{mediaType}}}"{{^-last}}, {{/-last}}{{/consumes}} }){{/hasConsumes}}{{#hasProduces}} +@Produces({ {{#produces}}"{{{mediaType}}}"{{^-last}}, {{/-last}}{{/produces}} }){{/hasProduces}} +{{>generatedAnnotation}} +public class {{classname}}Impl {{#interfaceOnly}}implements{{/interfaceOnly}}{{^interfaceOnly}}extends{{/interfaceOnly}} {{classname}} { +{{#operations}} +{{#operation}} + +{{>apiMethod}} +{{/operation}} +} +{{/operations}} \ No newline at end of file diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/helidon/JavaHelidonMpServerCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/helidon/JavaHelidonMpServerCodegenTest.java index ff577fbe74df..71adde215f76 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/helidon/JavaHelidonMpServerCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/helidon/JavaHelidonMpServerCodegenTest.java @@ -1,16 +1,20 @@ package org.openapitools.codegen.java.helidon; import org.openapitools.codegen.DefaultGenerator; +import org.openapitools.codegen.TestUtils; import org.openapitools.codegen.config.CodegenConfigurator; import org.openapitools.codegen.java.assertions.JavaFileAssert; import org.testng.annotations.BeforeMethod; -import org.testng.annotations.Ignore; import org.testng.annotations.Test; import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; +import java.util.Objects; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; public class JavaHelidonMpServerCodegenTest { @@ -37,6 +41,7 @@ private CodegenConfigurator createConfigurator() { private void generate(CodegenConfigurator config) { generator.opts(config.toClientOptInput()); + generator.setGenerateMetadata(false); generator.generate(); } @@ -44,12 +49,21 @@ private void generate() { generate(createConfigurator()); } - //TODO remove it or change after MP implements new fullProject option - @Ignore @Test - public void testAbstractClass() { + public void testRestApiFilesOnly() { generate(createConfigurator().addAdditionalProperty("fullProject", "false")); + JavaFileAssert.assertThat(Paths.get(apiPackage + "/PetService.java")) + .fileContains("public class PetService"); + + File outputFile = Paths.get(outputPath).toFile(); + assertThat(Objects.requireNonNull(outputFile.listFiles()).length, is(1)); + } + + @Test + public void testAbstractClass() { + generate(createConfigurator().addAdditionalProperty("useAbstractClass", "true")); + JavaFileAssert.assertThat(Paths.get(apiPackage + "/PetService.java")) .fileContains("public abstract class PetService") .assertMethod("addPet", "Pet") @@ -59,7 +73,13 @@ public void testAbstractClass() { .fileContains("public abstract class StoreService") .assertMethod("placeOrder", "Order") .doesNotHaveImplementation() - .hasReturnType("Order"); + .hasReturnType("Response"); + + JavaFileAssert.assertThat(Paths.get(apiPackage + "/StoreServiceImpl.java")) + .fileContains("public class StoreServiceImpl extends StoreService") + .assertMethod("placeOrder", "Order") + .hasReturnType("Response") + .bodyContainsLines("return Response.ok().entity(\"magic!\").build();"); } @Test @@ -152,7 +172,22 @@ public void doGenerateInterfaceOnly() { .fileContains("public interface StoreService") .assertMethod("placeOrder", "Order") .doesNotHaveImplementation() - .hasReturnType("Order"); + .hasReturnType("Response"); + + JavaFileAssert.assertThat(Paths.get(apiPackage + "/StoreServiceImpl.java")) + .fileContains("public class StoreServiceImpl implements StoreService") + .assertMethod("placeOrder", "Order") + .hasReturnType("Response") + .bodyContainsLines("return Response.ok().entity(\"magic!\").build();"); + } + + @Test + public void testGenerateGradleProject() { + generate(createConfigurator().addAdditionalProperty("gradleProject", "true")); + + assertThat(Paths.get(outputPath + "/build.gradle").toFile().exists(), is(true)); + assertThat(Paths.get(outputPath + "/settings.gradle").toFile().exists(), is(true)); + TestUtils.assertFileNotExists(Paths.get(outputPath + "/pom.xml")); } } \ No newline at end of file diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/helidon/functional/FunctionalHelidonMPServerTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/helidon/functional/FunctionalHelidonMPServerTest.java index 0df8bea10d49..c10cd5a2030c 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/helidon/functional/FunctionalHelidonMPServerTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/helidon/functional/FunctionalHelidonMPServerTest.java @@ -20,11 +20,15 @@ import org.testng.annotations.Ignore; import org.testng.annotations.Test; +import java.io.IOException; +import java.io.UncheckedIOException; import java.nio.file.Files; import java.nio.file.Path; +import java.util.Comparator; +import java.util.stream.Stream; -import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.MatcherAssert.assertThat; public class FunctionalHelidonMPServerTest extends FunctionalBase { @@ -48,11 +52,9 @@ void buildProjectInterfaceOnly() { buildAndVerify("target/openapi-java-server.jar"); } - //TODO remove it or change after MP implements new fullProject option - @Ignore @Test void buildProjectAbstractClasses() { - generate(createConfigurator().addAdditionalProperty(FULL_PROJECT, "false")); + generate(createConfigurator().addAdditionalProperty(USE_ABSTRACT_CLASS, "true")); buildAndVerify("target/openapi-java-server.jar"); } @@ -61,4 +63,32 @@ void buildFullProject() { generate(createConfigurator().addAdditionalProperty(FULL_PROJECT, "true")); buildAndVerify("target/openapi-java-server.jar"); } + + @Test + void verifyFullProjectSemantics() { + // Generate project for first time and record pom's timestamp + generate(createConfigurator()); + buildAndVerify("target/openapi-java-server.jar"); + Path pom1 = outputPath.resolve("pom.xml"); + assertThat(Files.exists(pom1), is(true)); + long lastModified = pom1.toFile().lastModified(); + + // Re-generate project over same directory with fullProject unspecified + generate(createConfigurator(outputPath)); + Path pom2 = outputPath.resolve("pom.xml"); + assertThat(Files.exists(pom2), is(true)); + assertThat(pom2.toFile().lastModified(), is(lastModified)); // not overwritten + + // Re-generate project over same directory with fullProject false + generate(createConfigurator(outputPath).addAdditionalProperty(FULL_PROJECT, "false")); + Path pom3 = outputPath.resolve("pom.xml"); + assertThat(Files.exists(pom3), is(true)); + assertThat(pom3.toFile().lastModified(), is(lastModified)); // not overwritten + + // Re-generate project over same directory with fullProject true + generate(createConfigurator(outputPath).addAdditionalProperty(FULL_PROJECT, "true")); + Path pom4 = outputPath.resolve("pom.xml"); + assertThat(Files.exists(pom4), is(true)); + assertThat(pom4.toFile().lastModified(), is(not(lastModified))); // overwritten + } }