Skip to content

Commit

Permalink
#18388: Add Mutiny support to JaxRS (#18389)
Browse files Browse the repository at this point in the history
* #18388: Add Mutiny support to JaxRS

* Updated samples

* Updated doccs

* Updated example to 3_0

* Updated sample
  • Loading branch information
lizzyTheLizard committed Apr 17, 2024
1 parent 7b0f963 commit 213564a
Show file tree
Hide file tree
Showing 89 changed files with 15,217 additions and 2 deletions.
15 changes: 15 additions & 0 deletions bin/configs/jaxrs-spec-quarkus-mutiny.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
generatorName: jaxrs-spec
outputDir: samples/server/petstore/jaxrs-spec-quarkus-mutiny
inputSpec: modules/openapi-generator/src/test/resources/3_0/petstore-with-fake-endpoints-models-for-testing.yaml
templateDir: modules/openapi-generator/src/main/resources/JavaJaxRS/spec
additionalProperties:
artifactId: jaxrs-spec-petstore-server
serializableModel: "true"
hideGenerationTimestamp: "true"
implicitHeadersRegex: (api_key|enum_header_string)
generateBuilders: "true"
useMicroProfileOpenAPIAnnotations: "true"
useAsync: "true"
useMutiny: "true"
library: "quarkus"
dateLibrary: "java8-localdatetime"
1 change: 1 addition & 0 deletions docs/generators/jaxrs-cxf-cdi.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|useBeanValidation|Use BeanValidation API annotations| |true|
|useJakartaEe|whether to use Jakarta EE namespace instead of javax| |false|
|useMicroProfileOpenAPIAnnotations|Whether to generate Microprofile OpenAPI annotations. Only valid when library is set to quarkus.| |false|
|useMutiny|Whether to use Smallrye Mutiny instead of CompletionStage for asynchronous computation. Only valid when library is set to quarkus.| |false|
|useOneOfInterfaces|whether to use a java interface to describe a set of oneOf options, where each option is a class that implements the interface| |false|
|useSwaggerAnnotations|Whether to generate Swagger annotations.| |true|
|useTags|use tags for creating interface and controller classnames| |false|
Expand Down
1 change: 1 addition & 0 deletions docs/generators/jaxrs-spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|useBeanValidation|Use BeanValidation API annotations| |true|
|useJakartaEe|whether to use Jakarta EE namespace instead of javax| |false|
|useMicroProfileOpenAPIAnnotations|Whether to generate Microprofile OpenAPI annotations. Only valid when library is set to quarkus.| |false|
|useMutiny|Whether to use Smallrye Mutiny instead of CompletionStage for asynchronous computation. Only valid when library is set to quarkus.| |false|
|useOneOfInterfaces|whether to use a java interface to describe a set of oneOf options, where each option is a class that implements the interface| |false|
|useSwaggerAnnotations|Whether to generate Swagger annotations.| |true|
|useTags|use tags for creating interface and controller classnames| |false|
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ public class JavaJAXRSSpecServerCodegen extends AbstractJavaJAXRSServerCodegen {
public static final String GENERATE_POM = "generatePom";
public static final String USE_SWAGGER_ANNOTATIONS = "useSwaggerAnnotations";
public static final String USE_MICROPROFILE_OPENAPI_ANNOTATIONS = "useMicroProfileOpenAPIAnnotations";
public static final String USE_MUTINY = "useMutiny";
public static final String OPEN_API_SPEC_FILE_LOCATION = "openApiSpecFileLocation";
public static final String GENERATE_BUILDERS = "generateBuilders";

Expand All @@ -53,6 +54,7 @@ public class JavaJAXRSSpecServerCodegen extends AbstractJavaJAXRSServerCodegen {
private boolean generateBuilders = false;
private boolean useSwaggerAnnotations = true;
private boolean useMicroProfileOpenAPIAnnotations = false;
private boolean useMutiny = false;

protected boolean useGzipFeature = false;
private boolean useJackson = false;
Expand Down Expand Up @@ -120,6 +122,7 @@ public JavaJAXRSSpecServerCodegen() {
cliOptions.add(CliOption.newBoolean(USE_MICROPROFILE_OPENAPI_ANNOTATIONS, "Whether to generate Microprofile OpenAPI annotations. Only valid when library is set to quarkus.", useMicroProfileOpenAPIAnnotations));
cliOptions.add(CliOption.newString(OPEN_API_SPEC_FILE_LOCATION, "Location where the file containing the spec will be generated in the output folder. No file generated when set to null or empty string."));
cliOptions.add(CliOption.newBoolean(SUPPORT_ASYNC, "Wrap responses in CompletionStage type, allowing asynchronous computation (requires JAX-RS 2.1).", supportAsync));
cliOptions.add(CliOption.newBoolean(USE_MUTINY, "Whether to use Smallrye Mutiny instead of CompletionStage for asynchronous computation. Only valid when library is set to quarkus.", useMutiny));
}

@Override
Expand Down Expand Up @@ -167,6 +170,12 @@ public void processOpts() {
writePropertyBack(USE_MICROPROFILE_OPENAPI_ANNOTATIONS, useMicroProfileOpenAPIAnnotations);
}

if (QUARKUS_LIBRARY.equals(library)) {
if (additionalProperties.containsKey(USE_MUTINY)) {
useMutiny = Boolean.parseBoolean(additionalProperties.get(USE_MUTINY).toString());
}
writePropertyBack(USE_MUTINY, useMutiny);
}

if (additionalProperties.containsKey(GENERATE_BUILDERS)) {
generateBuilders = Boolean.parseBoolean(additionalProperties.get(GENERATE_BUILDERS).toString());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,13 @@ import io.swagger.annotations.*;
{{/useSwaggerAnnotations}}

{{#supportAsync}}
{{#useMutiny}}
import io.smallrye.mutiny.Uni;
{{/useMutiny}}
{{^useMutiny}}
import java.util.concurrent.CompletionStage;
import java.util.concurrent.CompletableFuture;
{{/useMutiny}}
{{/supportAsync}}

import java.io.InputStream;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,6 @@
{{^vendorExtensions.x-java-is-response-void}}@org.eclipse.microprofile.openapi.annotations.media.Content(schema = @org.eclipse.microprofile.openapi.annotations.media.Schema(implementation = {{{baseType}}}.class{{#vendorExtensions.x-microprofile-open-api-return-schema-container}}, type = {{{.}}} {{/vendorExtensions.x-microprofile-open-api-return-schema-container}}{{#vendorExtensions.x-microprofile-open-api-return-unique-items}}, uniqueItems = true {{/vendorExtensions.x-microprofile-open-api-return-unique-items}})){{/vendorExtensions.x-java-is-response-void}}
}){{^-last}},{{/-last}}{{/responses}}
}){{/hasProduces}}{{/useMicroProfileOpenAPIAnnotations}}
public {{#supportAsync}}CompletionStage<{{/supportAsync}}Response{{#supportAsync}}>{{/supportAsync}} {{nickname}}({{#allParams}}{{>queryParams}}{{>pathParams}}{{>cookieParams}}{{>headerParams}}{{>bodyParams}}{{>formParams}}{{^-last}},{{/-last}}{{/allParams}}) {
return {{#supportAsync}}CompletableFuture.supplyAsync(() -> {{/supportAsync}}Response.ok().entity("magic!").build(){{#supportAsync}}){{/supportAsync}};
public {{#supportAsync}}{{#useMutiny}}Uni{{/useMutiny}}{{^useMutiny}}CompletionStage{{/useMutiny}}<{{/supportAsync}}Response{{#supportAsync}}>{{/supportAsync}} {{nickname}}({{#allParams}}{{>queryParams}}{{>pathParams}}{{>cookieParams}}{{>headerParams}}{{>bodyParams}}{{>formParams}}{{^-last}},{{/-last}}{{/allParams}}) {
return {{#supportAsync}}{{#useMutiny}}Uni.createFrom().item({{/useMutiny}}{{^useMutiny}}CompletableFuture.supplyAsync(() -> {{/useMutiny}}{{/supportAsync}}Response.ok().entity("magic!").build(){{#supportAsync}}){{/supportAsync}};
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@
{{#useSwaggerAnnotations}}
<io.swagger.annotations.version>1.6.10</io.swagger.annotations.version>
{{/useSwaggerAnnotations}}
{{#useMutiny}}
<smallrye.rest.client.version>1.2.1</smallrye.rest.client.version>
{{/useMutiny}}
</properties>
<dependencyManagement>
<dependencies>
Expand Down Expand Up @@ -109,6 +112,14 @@
<scope>provided</scope>
</dependency>
{{/useSwaggerAnnotations}}
{{#useMutiny}}
<dependency>
<groupId>io.smallrye</groupId>
<artifactId>smallrye-rest-client</artifactId>
<version>${smallrye.rest.client.version}</version>
<scope>test</scope>
</dependency>
{{/useMutiny}}
</dependencies>
<build>
<plugins>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{{#useMutiny}}Uni{{/useMutiny}}{{^useMutiny}}CompletionStage{{/useMutiny}}<{{#returnResponse}}Response{{/returnResponse}}{{^returnResponse}}{{#returnContainer}}{{#isMap}}Map<String, {{{returnBaseType}}}>{{/isMap}}{{#isArray}}{{{returnContainer}}}<{{{returnBaseType}}}>{{/isArray}}{{/returnContainer}}{{^returnContainer}}{{{returnBaseType}}}{{/returnContainer}}{{/returnResponse}}>
Original file line number Diff line number Diff line change
Expand Up @@ -808,6 +808,103 @@ public void generateApiForQuarkusWithGzipFeature() throws Exception {
);
}

@Test
public void generateApiForQuarkusWithoutMutiny() throws Exception {
final File output = Files.createTempDirectory("test").toFile();
output.deleteOnExit();

final OpenAPI openAPI = new OpenAPIParser()
.readLocation("src/test/resources/3_0/issue_4832.yaml", null, new ParseOptions()).getOpenAPI();

codegen.setOutputDir(output.getAbsolutePath());
codegen.setLibrary(QUARKUS_LIBRARY);
codegen.additionalProperties().put(SUPPORT_ASYNC, true);
codegen.additionalProperties().put(USE_TAGS, true); //And use tags to generate everything in PingApi.java

final ClientOptInput input = new ClientOptInput()
.openAPI(openAPI)
.config(codegen); //Using JavaJAXRSSpecServerCodegen

final DefaultGenerator generator = new DefaultGenerator();
final List<File> files = generator.opts(input).generate(); //When generating files

//Then the java files are compilable
validateJavaSourceFiles(files);

//And the generated class contains CompletionStage<Response>
TestUtils.ensureContainsFile(files, output, "src/gen/java/org/openapitools/api/PingApi.java");
TestUtils.assertFileContains(output.toPath().resolve("src/gen/java/org/openapitools/api/PingApi.java"),
"CompletionStage<Response> pingGetBoolean", //Support primitive types response
"CompletionStage<Response> pingGetInteger" //Support primitive types response
);
}

@Test
public void generateApiForQuarkusWithMutinyApi() throws Exception {
final File output = Files.createTempDirectory("test").toFile();
output.deleteOnExit();

final OpenAPI openAPI = new OpenAPIParser()
.readLocation("src/test/resources/3_0/issue_4832.yaml", null, new ParseOptions()).getOpenAPI();

codegen.setOutputDir(output.getAbsolutePath());
codegen.setLibrary(QUARKUS_LIBRARY);
codegen.additionalProperties().put(USE_TAGS, true); //And use tags to generate everything in PingApi.java
codegen.additionalProperties().put(SUPPORT_ASYNC, true);
codegen.additionalProperties().put(INTERFACE_ONLY, true);
codegen.additionalProperties().put(USE_MUTINY, true);

final ClientOptInput input = new ClientOptInput()
.openAPI(openAPI)
.config(codegen); //Using JavaJAXRSSpecServerCodegen

final DefaultGenerator generator = new DefaultGenerator();
final List<File> files = generator.opts(input).generate(); //When generating files

//Then the java files are compilable
validateJavaSourceFiles(files);

//And the generated class contains CompletionStage<Response>
TestUtils.ensureContainsFile(files, output, "src/gen/java/org/openapitools/api/PingApi.java");
TestUtils.assertFileContains(output.toPath().resolve("src/gen/java/org/openapitools/api/PingApi.java"),
"Uni<Boolean> pingGetBoolean", //Support primitive types response
"Uni<Integer> pingGetInteger" //Support primitive types response
);
}


@Test
public void generateApiForQuarkusWithMutinyImpl() throws Exception {
final File output = Files.createTempDirectory("test").toFile();
output.deleteOnExit();

final OpenAPI openAPI = new OpenAPIParser()
.readLocation("src/test/resources/3_0/issue_4832.yaml", null, new ParseOptions()).getOpenAPI();

codegen.setOutputDir(output.getAbsolutePath());
codegen.setLibrary(QUARKUS_LIBRARY);
codegen.additionalProperties().put(USE_TAGS, true); //And use tags to generate everything in PingApi.java
codegen.additionalProperties().put(SUPPORT_ASYNC, true);
codegen.additionalProperties().put(USE_MUTINY, true);

final ClientOptInput input = new ClientOptInput()
.openAPI(openAPI)
.config(codegen); //Using JavaJAXRSSpecServerCodegen

final DefaultGenerator generator = new DefaultGenerator();
final List<File> files = generator.opts(input).generate(); //When generating files

//Then the java files are compilable
validateJavaSourceFiles(files);

//And the generated class contains CompletionStage<Response>
TestUtils.ensureContainsFile(files, output, "src/gen/java/org/openapitools/api/PingApi.java");
TestUtils.assertFileContains(output.toPath().resolve("src/gen/java/org/openapitools/api/PingApi.java"),
"Uni<Response> pingGetBoolean", //Support primitive types response
"Uni<Response> pingGetInteger" //Support primitive types response
);
}

@Test
public void testHandleRequiredAndReadOnlyPropertiesCorrectly() throws Exception {
File output = Files.createTempDirectory("test").toFile().getCanonicalFile();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
*
!target/*-runner
!target/*-runner.jar
!target/lib/*
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# OpenAPI Generator Ignore
# Generated by openapi-generator https://github.com/openapitools/openapi-generator

# Use this file to prevent files from being overwritten by the generator.
# The patterns follow closely to .gitignore or .dockerignore.

# As an example, the C# client generator defines ApiClient.cs.
# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line:
#ApiClient.cs

# You can match any string of characters against a directory, file or extension with a single asterisk (*):
#foo/*/qux
# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux

# You can recursively match patterns against a directory, file or extension with a double asterisk (**):
#foo/**/qux
# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux

# You can also negate patterns with an exclamation (!).
# For example, you can ignore all files in a docs folder with the file extension .md:
#docs/*.md
# Then explicitly reverse the ignore rule for a single file:
#!docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
.dockerignore
README.md
pom.xml
src/gen/java/org/openapitools/api/AnotherFakeApi.java
src/gen/java/org/openapitools/api/FakeApi.java
src/gen/java/org/openapitools/api/FakeClassnameTestApi.java
src/gen/java/org/openapitools/api/FooApi.java
src/gen/java/org/openapitools/api/PetApi.java
src/gen/java/org/openapitools/api/RestApplication.java
src/gen/java/org/openapitools/api/RestResourceRoot.java
src/gen/java/org/openapitools/api/StoreApi.java
src/gen/java/org/openapitools/api/UserApi.java
src/gen/java/org/openapitools/model/AdditionalPropertiesClass.java
src/gen/java/org/openapitools/model/AllOfWithSingleRef.java
src/gen/java/org/openapitools/model/Animal.java
src/gen/java/org/openapitools/model/ArrayOfArrayOfNumberOnly.java
src/gen/java/org/openapitools/model/ArrayOfNumberOnly.java
src/gen/java/org/openapitools/model/ArrayTest.java
src/gen/java/org/openapitools/model/Capitalization.java
src/gen/java/org/openapitools/model/Cat.java
src/gen/java/org/openapitools/model/Category.java
src/gen/java/org/openapitools/model/ChildWithNullable.java
src/gen/java/org/openapitools/model/ClassModel.java
src/gen/java/org/openapitools/model/Client.java
src/gen/java/org/openapitools/model/DeprecatedObject.java
src/gen/java/org/openapitools/model/Dog.java
src/gen/java/org/openapitools/model/EnumArrays.java
src/gen/java/org/openapitools/model/EnumClass.java
src/gen/java/org/openapitools/model/EnumTest.java
src/gen/java/org/openapitools/model/FakeBigDecimalMap200Response.java
src/gen/java/org/openapitools/model/FileSchemaTestClass.java
src/gen/java/org/openapitools/model/Foo.java
src/gen/java/org/openapitools/model/FooGetDefaultResponse.java
src/gen/java/org/openapitools/model/FormatTest.java
src/gen/java/org/openapitools/model/HasOnlyReadOnly.java
src/gen/java/org/openapitools/model/HealthCheckResult.java
src/gen/java/org/openapitools/model/MapTest.java
src/gen/java/org/openapitools/model/MixedPropertiesAndAdditionalPropertiesClass.java
src/gen/java/org/openapitools/model/Model200Response.java
src/gen/java/org/openapitools/model/ModelApiResponse.java
src/gen/java/org/openapitools/model/ModelFile.java
src/gen/java/org/openapitools/model/ModelList.java
src/gen/java/org/openapitools/model/ModelReturn.java
src/gen/java/org/openapitools/model/Name.java
src/gen/java/org/openapitools/model/NullableClass.java
src/gen/java/org/openapitools/model/NumberOnly.java
src/gen/java/org/openapitools/model/ObjectWithDeprecatedFields.java
src/gen/java/org/openapitools/model/Order.java
src/gen/java/org/openapitools/model/OuterComposite.java
src/gen/java/org/openapitools/model/OuterEnum.java
src/gen/java/org/openapitools/model/OuterEnumDefaultValue.java
src/gen/java/org/openapitools/model/OuterEnumInteger.java
src/gen/java/org/openapitools/model/OuterEnumIntegerDefaultValue.java
src/gen/java/org/openapitools/model/OuterObjectWithEnumProperty.java
src/gen/java/org/openapitools/model/ParentWithNullable.java
src/gen/java/org/openapitools/model/Pet.java
src/gen/java/org/openapitools/model/ReadOnlyFirst.java
src/gen/java/org/openapitools/model/SingleRefType.java
src/gen/java/org/openapitools/model/SpecialModelName.java
src/gen/java/org/openapitools/model/Tag.java
src/gen/java/org/openapitools/model/TestInlineFreeformAdditionalPropertiesRequest.java
src/gen/java/org/openapitools/model/User.java
src/main/docker/Dockerfile.jvm
src/main/docker/Dockerfile.native
src/main/resources/META-INF/openapi.yaml
src/main/resources/application.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
7.5.0-SNAPSHOT
33 changes: 33 additions & 0 deletions samples/server/petstore/jaxrs-spec-quarkus-mutiny/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# JAX-RS server with OpenAPI using Quarkus

## Overview
This server was generated by the [OpenAPI Generator](https://openapi-generator.tech) project. By using an
[OpenAPI-Spec](https://openapis.org), you can easily generate a server stub.

This is an example of building a OpenAPI-enabled JAX-RS server.
This example uses the [JAX-RS](https://jax-rs-spec.java.net/) framework and
the [Eclipse-MicroProfile-OpenAPI](https://github.com/eclipse/microprofile-open-api) addition.

The pom file is configured to use [Quarkus](https://quarkus.io/) as application server.


To start the server in dev mode, run this maven command:

```bash
mvn compile quarkus:dev
```

You can then call your server endpoints under:

```
http://localhost:8080/
```

In dev-mode, you can open Swagger-UI at:

```
http://localhost:8080/swagger-ui/
```

Read more in the [Quarkus OpenAPI guide](https://quarkus.io/guides/openapi-swaggerui-guide).

0 comments on commit 213564a

Please sign in to comment.