forked from loafle/openapi-generator-original
Add support for asynchronous API for JavaJaxRsSpec (#10919)
* Add support for asynchronous API for JavaJaxRsSpec https://github.com/OpenAPITools/openapi-generator/issues/4832 * Ran the requested command for a PR Run the following to build the project and update samples: ./mvnw clean package ./bin/generate-samples.sh ./bin/utils/export_docs_generators.sh * Set java 8 mode when using supportAsync=true `CompletionStage` are only introduced since 1.8, enabling async should for use java8 Co-authored-by: Thomas Bredzinski <thomas.bredzinski@gemalto.com>
This commit is contained in:
parent
e71ee1bf43
commit
508da12ca7
@ -58,6 +58,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|
||||
|sortModelPropertiesByRequiredFlag|Sort model properties to place required parameters before optional parameters.| |true|
|
||||
|sortParamsByRequiredFlag|Sort method arguments to place required parameters before optional parameters.| |true|
|
||||
|sourceFolder|source folder for generated code| |src/gen/java|
|
||||
|supportAsync|Wrap responses in CompletionStage type, allowing asynchronous computation (requires JAX-RS 2.1).| |false|
|
||||
|title|a title describing the application| |OpenAPI Server|
|
||||
|useBeanValidation|Use BeanValidation API annotations| |true|
|
||||
|useSwaggerAnnotations|Whether to generate Swagger annotations.| |true|
|
||||
|
@ -58,6 +58,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|
||||
|sortModelPropertiesByRequiredFlag|Sort model properties to place required parameters before optional parameters.| |true|
|
||||
|sortParamsByRequiredFlag|Sort method arguments to place required parameters before optional parameters.| |true|
|
||||
|sourceFolder|source folder for generated code| |src/main/java|
|
||||
|supportAsync|Wrap responses in CompletionStage type, allowing asynchronous computation (requires JAX-RS 2.1).| |false|
|
||||
|title|a title describing the application| |OpenAPI Server|
|
||||
|useBeanValidation|Use BeanValidation API annotations| |true|
|
||||
|useSwaggerAnnotations|Whether to generate Swagger annotations.| |true|
|
||||
|
@ -102,6 +102,7 @@ public class JavaJAXRSSpecServerCodegen extends AbstractJavaJAXRSServerCodegen {
|
||||
cliOptions.add(CliOption.newBoolean(RETURN_RESPONSE, "Whether generate API interface should return javax.ws.rs.core.Response instead of a deserialized entity. Only useful if interfaceOnly is true.").defaultValue(String.valueOf(returnResponse)));
|
||||
cliOptions.add(CliOption.newBoolean(USE_SWAGGER_ANNOTATIONS, "Whether to generate Swagger annotations.", useSwaggerAnnotations));
|
||||
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));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -121,6 +122,14 @@ public class JavaJAXRSSpecServerCodegen extends AbstractJavaJAXRSServerCodegen {
|
||||
additionalProperties.remove(RETURN_RESPONSE);
|
||||
}
|
||||
}
|
||||
if (additionalProperties.containsKey(SUPPORT_ASYNC)) {
|
||||
supportAsync = Boolean.parseBoolean(additionalProperties.get(SUPPORT_ASYNC).toString());
|
||||
if (!supportAsync) {
|
||||
additionalProperties.remove(SUPPORT_ASYNC);
|
||||
} else {
|
||||
setJava8ModeAndAdditionalProperties(true);
|
||||
}
|
||||
}
|
||||
if (QUARKUS_LIBRARY.equals(library) || THORNTAIL_LIBRARY.equals(library) || HELIDON_LIBRARY.equals(library) || OPEN_LIBERTY_LIBRARY.equals(library) || KUMULUZEE_LIBRARY.equals(library)) {
|
||||
useSwaggerAnnotations = false;
|
||||
} else {
|
||||
|
@ -9,6 +9,10 @@ import javax.ws.rs.core.Response;
|
||||
{{#useSwaggerAnnotations}}
|
||||
import io.swagger.annotations.*;
|
||||
{{/useSwaggerAnnotations}}
|
||||
{{#supportAsync}}
|
||||
import java.util.concurrent.CompletionStage;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
{{/supportAsync}}
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.Map;
|
||||
|
@ -10,4 +10,4 @@
|
||||
{{/isOAuth}}{{/authMethods}} }{{/hasAuthMethods}}, tags={ {{#vendorExtensions.x-tags}}"{{tag}}"{{^-last}}, {{/-last}}{{/vendorExtensions.x-tags}} })
|
||||
@ApiResponses(value = { {{#responses}}
|
||||
@ApiResponse(code = {{{code}}}, message = "{{{message}}}", response = {{{baseType}}}.class{{#returnContainer}}, responseContainer = "{{{.}}}"{{/returnContainer}}){{^-last}},{{/-last}}{{/responses}} }){{/useSwaggerAnnotations}}
|
||||
{{#returnResponse}}Response{{/returnResponse}}{{^returnResponse}}{{>returnTypeInterface}}{{/returnResponse}} {{nickname}}({{#allParams}}{{>queryParams}}{{>pathParams}}{{>headerParams}}{{>bodyParams}}{{>formParams}}{{^-last}},{{/-last}}{{/allParams}});
|
||||
{{#supportAsync}}{{>returnAsyncTypeInterface}}{{/supportAsync}}{{^supportAsync}}{{#returnResponse}}Response{{/returnResponse}}{{^returnResponse}}{{>returnTypeInterface}}{{/returnResponse}}{{/supportAsync}} {{nickname}}({{#allParams}}{{>queryParams}}{{>pathParams}}{{>headerParams}}{{>bodyParams}}{{>formParams}}{{^-last}},{{/-last}}{{/allParams}});
|
@ -11,6 +11,6 @@
|
||||
@ApiResponses(value = { {{#responses}}
|
||||
@ApiResponse(code = {{{code}}}, message = "{{{message}}}", response = {{{baseType}}}.class{{#containerType}}, responseContainer = "{{{.}}}"{{/containerType}}){{^-last}},{{/-last}}{{/responses}}
|
||||
}){{/useSwaggerAnnotations}}
|
||||
public Response {{nickname}}({{#allParams}}{{>queryParams}}{{>pathParams}}{{>cookieParams}}{{>headerParams}}{{>bodyParams}}{{>formParams}}{{^-last}},{{/-last}}{{/allParams}}) {
|
||||
return Response.ok().entity("magic!").build();
|
||||
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}};
|
||||
}
|
@ -140,6 +140,11 @@
|
||||
{{/useBeanValidation}}
|
||||
</dependencies>
|
||||
<properties>
|
||||
{{#java8}}
|
||||
<java.version>1.8</java.version>
|
||||
<maven.compiler.source>${java.version}</maven.compiler.source>
|
||||
<maven.compiler.target>${java.version}</maven.compiler.target>
|
||||
{{/java8}}
|
||||
<jackson-version>2.9.9</jackson-version>
|
||||
<junit-version>4.13.1</junit-version>
|
||||
{{#useBeanValidation}}
|
||||
|
@ -0,0 +1 @@
|
||||
CompletionStage<{{#returnResponse}}Response{{/returnResponse}}{{^returnResponse}}{{#returnContainer}}{{#isMap}}Map<String, {{{returnBaseType}}}>{{/isMap}}{{#isArray}}{{{returnContainer}}}<{{{returnBaseType}}}>{{/isArray}}{{/returnContainer}}{{^returnContainer}}{{{returnBaseType}}}{{/returnContainer}}{{/returnResponse}}>
|
@ -28,6 +28,11 @@ import java.util.Map;
|
||||
|
||||
import static org.openapitools.codegen.TestUtils.assertFileContains;
|
||||
import static org.openapitools.codegen.TestUtils.validateJavaSourceFiles;
|
||||
import static org.openapitools.codegen.languages.AbstractJavaCodegen.JAVA8_MODE;
|
||||
import static org.openapitools.codegen.languages.AbstractJavaJAXRSServerCodegen.USE_TAGS;
|
||||
import static org.openapitools.codegen.languages.JavaJAXRSSpecServerCodegen.INTERFACE_ONLY;
|
||||
import static org.openapitools.codegen.languages.JavaJAXRSSpecServerCodegen.SUPPORT_ASYNC;
|
||||
import static org.openapitools.codegen.languages.JavaJAXRSSpecServerCodegen.RETURN_RESPONSE;
|
||||
import static org.testng.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
@ -95,6 +100,8 @@ public class JavaJAXRSSpecServerCodegenTest extends JavaJaxrsBaseTest {
|
||||
codegen.additionalProperties().put(CodegenConstants.INVOKER_PACKAGE, "xyz.yyyyy.iiii.invoker");
|
||||
codegen.additionalProperties().put("serverPort", "8088");
|
||||
codegen.additionalProperties().put(JavaJAXRSSpecServerCodegen.OPEN_API_SPEC_FILE_LOCATION, "openapi.yml");
|
||||
codegen.additionalProperties().put(SUPPORT_ASYNC, true);
|
||||
codegen.additionalProperties().put(JAVA8_MODE, false);
|
||||
codegen.processOpts();
|
||||
|
||||
OpenAPI openAPI = new OpenAPI();
|
||||
@ -112,6 +119,8 @@ public class JavaJAXRSSpecServerCodegenTest extends JavaJaxrsBaseTest {
|
||||
Assert.assertEquals(codegen.additionalProperties().get(AbstractJavaJAXRSServerCodegen.SERVER_PORT), "8088");
|
||||
Assert.assertEquals(codegen.getOpenApiSpecFileLocation(), "openapi.yml");
|
||||
Assert.assertEquals(codegen.additionalProperties().get(JavaJAXRSSpecServerCodegen.OPEN_API_SPEC_FILE_LOCATION), "openapi.yml");
|
||||
Assert.assertEquals(codegen.additionalProperties().get(SUPPORT_ASYNC), "true");
|
||||
Assert.assertEquals(codegen.additionalProperties().get(JAVA8_MODE), true); //overridden by supportAsync=true
|
||||
}
|
||||
|
||||
/**
|
||||
@ -419,4 +428,170 @@ public class JavaJAXRSSpecServerCodegenTest extends JavaJaxrsBaseTest {
|
||||
|
||||
assertFileContains(path, "\nimport java.util.Set;\n");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void generateApiWithAsyncSupport() throws Exception {
|
||||
final File output = Files.createTempDirectory("test").toFile();
|
||||
output.deleteOnExit();
|
||||
|
||||
final OpenAPI openAPI = new OpenAPIParser()
|
||||
.readLocation("src/test/resources/3_0/ping.yaml", null, new ParseOptions()).getOpenAPI();
|
||||
|
||||
codegen.setOutputDir(output.getAbsolutePath());
|
||||
codegen.additionalProperties().put(SUPPORT_ASYNC, true); //Given support async is enabled
|
||||
|
||||
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");
|
||||
assertFileContains(output.toPath().resolve("src/gen/java/org/openapitools/api/PingApi.java"),
|
||||
"\nimport java.util.concurrent.CompletionStage;\n",
|
||||
"\nimport java.util.concurrent.CompletableFuture;\n",
|
||||
"\npublic CompletionStage<Response> pingGet() {\n",
|
||||
"\nCompletableFuture.supplyAsync(() -> Response.ok().entity(\"magic!\").build())\n"
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void generateApiWithAsyncSupportAndInterfaceOnly() throws Exception {
|
||||
final File output = Files.createTempDirectory("test").toFile().getCanonicalFile();
|
||||
output.deleteOnExit();
|
||||
|
||||
final OpenAPI openAPI = new OpenAPIParser()
|
||||
.readLocation("src/test/resources/3_0/ping.yaml", null, new ParseOptions()).getOpenAPI();
|
||||
|
||||
codegen.setOutputDir(output.getAbsolutePath());
|
||||
codegen.additionalProperties().put(SUPPORT_ASYNC, true); //Given support async is enabled
|
||||
codegen.additionalProperties().put(INTERFACE_ONLY, true); //And only interfaces are generated
|
||||
|
||||
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 interface contains CompletionStage<Void>
|
||||
TestUtils.ensureContainsFile(files, output, "src/gen/java/org/openapitools/api/PingApi.java");
|
||||
assertFileContains(output.toPath().resolve("src/gen/java/org/openapitools/api/PingApi.java"),
|
||||
"\nimport java.util.concurrent.CompletionStage;\n",
|
||||
"\nCompletionStage<Void> pingGet();\n");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void generateApiWithAsyncSupportAndInterfaceOnlyAndResponse() throws Exception {
|
||||
final File output = Files.createTempDirectory("test").toFile().getCanonicalFile();
|
||||
output.deleteOnExit();
|
||||
|
||||
final OpenAPI openAPI = new OpenAPIParser()
|
||||
.readLocation("src/test/resources/3_0/ping.yaml", null, new ParseOptions()).getOpenAPI();
|
||||
|
||||
codegen.setOutputDir(output.getAbsolutePath());
|
||||
codegen.additionalProperties().put(SUPPORT_ASYNC, true); //Given support async is enabled
|
||||
codegen.additionalProperties().put(INTERFACE_ONLY, true); //And only interfaces are generated
|
||||
codegen.additionalProperties().put(RETURN_RESPONSE, true); //And return type is Response
|
||||
|
||||
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 interface contains CompletionStage<Response>
|
||||
TestUtils.ensureContainsFile(files, output, "src/gen/java/org/openapitools/api/PingApi.java");
|
||||
assertFileContains(output.toPath().resolve( "src/gen/java/org/openapitools/api/PingApi.java"),
|
||||
"\nimport java.util.concurrent.CompletionStage;\n",
|
||||
"\nCompletionStage<Response> pingGet();\n");
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void generatePetstoreAPIWithAsyncSupport() throws Exception {
|
||||
final File output = Files.createTempDirectory("test").toFile().getCanonicalFile();
|
||||
output.deleteOnExit();
|
||||
|
||||
final OpenAPI openAPI = new OpenAPIParser()
|
||||
.readLocation("src/test/resources/3_0/petstore.yaml", null, new ParseOptions()).getOpenAPI();
|
||||
|
||||
codegen.setOutputDir(output.getAbsolutePath());
|
||||
codegen.additionalProperties().put(SUPPORT_ASYNC, true); //Given support async is enabled
|
||||
codegen.additionalProperties().put(INTERFACE_ONLY, true); //And only interfaces are generated
|
||||
|
||||
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 interfaces contains CompletionStage
|
||||
TestUtils.ensureContainsFile(files, output, "src/gen/java/org/openapitools/api/PetApi.java");
|
||||
assertFileContains(output.toPath().resolve("src/gen/java/org/openapitools/api/PetApi.java"),
|
||||
"\nimport java.util.concurrent.CompletionStage;\n",
|
||||
"CompletionStage<Void> deletePet", //Support empty response
|
||||
"CompletionStage<List<Pet>> findPetsByStatus", //Support type of arrays response
|
||||
"CompletionStage<Pet> getPetById" //Support single type response
|
||||
);
|
||||
|
||||
TestUtils.ensureContainsFile(files, output, "src/gen/java/org/openapitools/api/StoreApi.java");
|
||||
assertFileContains(output.toPath().resolve("src/gen/java/org/openapitools/api/StoreApi.java"),
|
||||
"\nimport java.util.concurrent.CompletionStage;\n",
|
||||
"CompletionStage<Map<String, Integer>>" //Support map response
|
||||
);
|
||||
|
||||
TestUtils.ensureContainsFile(files, output, "src/gen/java/org/openapitools/api/UserApi.java");
|
||||
assertFileContains(output.toPath().resolve("src/gen/java/org/openapitools/api/UserApi.java"),
|
||||
"\nimport java.util.concurrent.CompletionStage;\n",
|
||||
"CompletionStage<String>" //Support simple types
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void generatePingWithAsyncSupportPrimitiveType() throws Exception {
|
||||
final File output = Files.createTempDirectory("test").toFile().getCanonicalFile();
|
||||
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.additionalProperties().put(SUPPORT_ASYNC, true); //Given support async is enabled
|
||||
codegen.additionalProperties().put(INTERFACE_ONLY, true); //And only interfaces are generated
|
||||
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 interfaces contains CompletionStage with proper classes instead of primitive types
|
||||
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<Boolean> pingGetBoolean", //Support primitive types response
|
||||
"CompletionStage<Integer> pingGetInteger" //Support primitive types response
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,31 @@
|
||||
openapi: 3.0.1
|
||||
info:
|
||||
title: ping that return primitive types
|
||||
version: '1.0'
|
||||
servers:
|
||||
- url: 'http://localhost:8082/'
|
||||
paths:
|
||||
/pingBoolean:
|
||||
get:
|
||||
operationId: pingGetBoolean
|
||||
tags: [Ping]
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
content:
|
||||
'application/json':
|
||||
schema:
|
||||
type: boolean
|
||||
/pingInteger:
|
||||
get:
|
||||
operationId: pingGetInteger
|
||||
tags: [Ping]
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
content:
|
||||
'application/json':
|
||||
schema:
|
||||
type: integer
|
||||
format: int32
|
||||
|
Loading…
x
Reference in New Issue
Block a user