From a6280d9b5807cb007f984213769c4612b4cae377 Mon Sep 17 00:00:00 2001 From: Carlos Chacin Date: Mon, 17 Feb 2025 04:02:01 -0800 Subject: [PATCH] [java][client] fix: Add static modifier to inner class in Java when useSingleRequestParameter=true (#20590) * fix: Add static modifier to inner class * Apply suggestions from code review Co-authored-by: martin-mfg <2026226+martin-mfg@users.noreply.github.com> * fix: Remove unintended whitespace * fix: Add default=false for useSingleRequestParameter --------- Co-authored-by: martin-mfg <2026226+martin-mfg@users.noreply.github.com> --- docs/generators/java-microprofile.md | 2 +- docs/generators/java.md | 2 +- .../codegen/languages/JavaClientCodegen.java | 20 ++++++++--- .../Java/libraries/microprofile/api.mustache | 8 ++--- .../Java/libraries/restclient/api.mustache | 4 +-- .../Java/libraries/webclient/api.mustache | 6 ++-- .../codegen/java/JavaClientCodegenTest.java | 35 +++++++++++++++++-- 7 files changed, 58 insertions(+), 19 deletions(-) diff --git a/docs/generators/java-microprofile.md b/docs/generators/java-microprofile.md index 9b4937e57c8..978dba2030c 100644 --- a/docs/generators/java-microprofile.md +++ b/docs/generators/java-microprofile.md @@ -100,7 +100,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl |useRuntimeException|Use RuntimeException instead of Exception. Only jersey2, jersey3, okhttp-gson, vertx, microprofile support this option.| |false| |useRxJava2|Whether to use the RxJava2 adapter with the retrofit2 library. IMPORTANT: This option has been deprecated.| |false| |useRxJava3|Whether to use the RxJava3 adapter with the retrofit2 library. IMPORTANT: This option has been deprecated.| |false| -|useSingleRequestParameter|Setting this property to true will generate functions with a single argument containing all API endpoint parameters instead of one argument per parameter. ONLY jersey2, jersey3, okhttp-gson, microprofile, Spring RestClient, Spring WebClient support this option.| |false| +|useSingleRequestParameter|Setting this property to "true" will generate functions with a single argument containing all API endpoint parameters instead of one argument per parameter. ONLY jersey2, jersey3, okhttp-gson, microprofile, Spring RestClient, Spring WebClient support this option. Setting this property to "static" does the same as "true", but also makes the generated arguments class static. Only WebClient supports this option.| |false| |webclientBlockingOperations|Making all WebClient operations blocking(sync). Note that if on operation 'x-webclient-blocking: false' then such operation won't be sync| |false| |withAWSV4Signature|whether to include AWS v4 signature support (only available for okhttp-gson library)| |false| |withXml|whether to include support for application/xml content type and include XML annotations in the model (works with libraries that provide support for JSON and XML)| |false| diff --git a/docs/generators/java.md b/docs/generators/java.md index 28205f47e32..5038eb608aa 100644 --- a/docs/generators/java.md +++ b/docs/generators/java.md @@ -100,7 +100,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl |useRuntimeException|Use RuntimeException instead of Exception. Only jersey2, jersey3, okhttp-gson, vertx, microprofile support this option.| |false| |useRxJava2|Whether to use the RxJava2 adapter with the retrofit2 library. IMPORTANT: This option has been deprecated.| |false| |useRxJava3|Whether to use the RxJava3 adapter with the retrofit2 library. IMPORTANT: This option has been deprecated.| |false| -|useSingleRequestParameter|Setting this property to true will generate functions with a single argument containing all API endpoint parameters instead of one argument per parameter. ONLY jersey2, jersey3, okhttp-gson, microprofile, Spring RestClient, Spring WebClient support this option.| |false| +|useSingleRequestParameter|Setting this property to "true" will generate functions with a single argument containing all API endpoint parameters instead of one argument per parameter. ONLY jersey2, jersey3, okhttp-gson, microprofile, Spring RestClient, Spring WebClient support this option. Setting this property to "static" does the same as "true", but also makes the generated arguments class static. Only WebClient supports this option.| |false| |webclientBlockingOperations|Making all WebClient operations blocking(sync). Note that if on operation 'x-webclient-blocking: false' then such operation won't be sync| |false| |withAWSV4Signature|whether to include AWS v4 signature support (only available for okhttp-gson library)| |false| |withXml|whether to include support for application/xml content type and include XML annotations in the model (works with libraries that provide support for JSON and XML)| |false| diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaClientCodegen.java index 0d8b49d2ddd..03af420a7b0 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaClientCodegen.java @@ -144,7 +144,7 @@ public class JavaClientCodegen extends AbstractJavaCodegen @Setter protected boolean useOneOfDiscriminatorLookup = false; // use oneOf discriminator's mapping for model lookup protected String rootJavaEEPackage; protected Map mpRestClientVersions = new LinkedHashMap<>(); - @Setter(AccessLevel.PRIVATE) protected boolean useSingleRequestParameter = false; + @Setter(AccessLevel.PRIVATE) protected String useSingleRequestParameter = "false"; protected boolean webclientBlockingOperations = false; @Setter protected boolean generateClientAsBean = false; @Setter protected boolean useEnumCaseInsensitive = false; @@ -237,7 +237,7 @@ public class JavaClientCodegen extends AbstractJavaCodegen cliOptions.add(CliOption.newString(CONFIG_KEY_FROM_CLASS_NAME, "If true, set tag as key in @RegisterRestClient. Default to false. Only `microprofile` supports this option.")); cliOptions.add(CliOption.newBoolean(CodegenConstants.USE_ONEOF_DISCRIMINATOR_LOOKUP, CodegenConstants.USE_ONEOF_DISCRIMINATOR_LOOKUP_DESC + " Only jersey2, jersey3, native, okhttp-gson support this option.")); cliOptions.add(CliOption.newString(MICROPROFILE_REST_CLIENT_VERSION, "Version of MicroProfile Rest Client API.")); - cliOptions.add(CliOption.newBoolean(CodegenConstants.USE_SINGLE_REQUEST_PARAMETER, "Setting this property to true will generate functions with a single argument containing all API endpoint parameters instead of one argument per parameter. ONLY jersey2, jersey3, okhttp-gson, microprofile, Spring RestClient, Spring WebClient support this option.")); + cliOptions.add(CliOption.newString(CodegenConstants.USE_SINGLE_REQUEST_PARAMETER, "Setting this property to \"true\" will generate functions with a single argument containing all API endpoint parameters instead of one argument per parameter. ONLY jersey2, jersey3, okhttp-gson, microprofile, Spring RestClient, Spring WebClient support this option. Setting this property to \"static\" does the same as \"true\", but also makes the generated arguments class static. Only WebClient supports this option.").defaultValue("false")); cliOptions.add(CliOption.newBoolean(WEBCLIENT_BLOCKING_OPERATIONS, "Making all WebClient operations blocking(sync). Note that if on operation 'x-webclient-blocking: false' then such operation won't be sync", this.webclientBlockingOperations)); cliOptions.add(CliOption.newBoolean(GENERATE_CLIENT_AS_BEAN, "For resttemplate, configure whether to create `ApiClient.java` and Apis clients as bean (with `@Component` annotation).", this.generateClientAsBean)); cliOptions.add(CliOption.newBoolean(SUPPORT_URL_QUERY, "Generate toUrlQueryString in POJO (default to true). Available on `native`, `apache-httpclient` libraries.")); @@ -334,7 +334,9 @@ public class JavaClientCodegen extends AbstractJavaCodegen convertPropertyToBooleanAndWriteBack(USE_RX_JAVA3, this::setUseRxJava3); convertPropertyToBooleanAndWriteBack(USE_RX_JAVA2, this::setUseRxJava2); } - convertPropertyToBooleanAndWriteBack(CodegenConstants.USE_SINGLE_REQUEST_PARAMETER, this::setUseSingleRequestParameter); + convertPropertyToStringAndWriteBack(CodegenConstants.USE_SINGLE_REQUEST_PARAMETER, this::setUseSingleRequestParameter); + writePropertyBack("singleRequestParameter", getSingleRequestParameter()); + writePropertyBack("staticRequest", getStaticRequest()); if (!useRxJava && !useRxJava2 && !useRxJava3) { additionalProperties.put(DO_NOT_USE_RX, true); @@ -757,7 +759,7 @@ public class JavaClientCodegen extends AbstractJavaCodegen public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List allModels) { super.postProcessOperationsWithModels(objs, allModels); - if (useSingleRequestParameter && (JERSEY2.equals(getLibrary()) || JERSEY3.equals(getLibrary()) || OKHTTP_GSON.equals(getLibrary()))) { + if (this.getSingleRequestParameter() && (JERSEY2.equals(getLibrary()) || JERSEY3.equals(getLibrary()) || OKHTTP_GSON.equals(getLibrary()))) { // loop through operations to set x-group-parameters extension to true if useSingleRequestParameter option is enabled OperationMap operations = objs.getOperations(); if (operations != null) { @@ -1116,7 +1118,15 @@ public class JavaClientCodegen extends AbstractJavaCodegen return this.useOneOfDiscriminatorLookup; } - private boolean getUseSingleRequestParameter() { + public boolean getSingleRequestParameter() { + return "true".equals(getUseSingleRequestParameter()) || "static".equals(getUseSingleRequestParameter()); + } + + public boolean getStaticRequest() { + return "static".equals(this.getUseSingleRequestParameter()); + } + + private String getUseSingleRequestParameter() { return useSingleRequestParameter; } diff --git a/modules/openapi-generator/src/main/resources/Java/libraries/microprofile/api.mustache b/modules/openapi-generator/src/main/resources/Java/libraries/microprofile/api.mustache index 6e25c1f1012..3d2864d3e7b 100644 --- a/modules/openapi-generator/src/main/resources/Java/libraries/microprofile/api.mustache +++ b/modules/openapi-generator/src/main/resources/Java/libraries/microprofile/api.mustache @@ -71,10 +71,10 @@ public interface {{classname}} { {{#hasProduces}} @Produces({ {{#produces}}"{{{mediaType}}}"{{^-last}}, {{/-last}}{{/produces}} }) {{/hasProduces}} -{{^useSingleRequestParameter}} +{{^singleRequestParameter}} {{^vendorExtensions.x-java-is-response-void}}{{#microprofileServer}}{{> server_operation}}{{/microprofileServer}}{{^microprofileServer}}{{> client_operation}}{{/microprofileServer}}{{/vendorExtensions.x-java-is-response-void}}{{#vendorExtensions.x-java-is-response-void}}{{#microprofileMutiny}}Uni{{/microprofileMutiny}}{{^microprofileMutiny}}void{{/microprofileMutiny}}{{/vendorExtensions.x-java-is-response-void}} {{nickname}}({{#allParams}}{{>queryParams}}{{>pathParams}}{{>headerParams}}{{>bodyParams}}{{>formParams}}{{^-last}}, {{/-last}}{{/allParams}}) throws ApiException, ProcessingException; -{{/useSingleRequestParameter}} -{{#useSingleRequestParameter}} +{{/singleRequestParameter}} +{{#singleRequestParameter}} {{^vendorExtensions.x-java-is-response-void}}{{#microprofileMutiny}}Uni<{{{returnType}}}>{{/microprofileMutiny}}{{^microprofileMutiny}}{{{returnType}}}{{/microprofileMutiny}}{{/vendorExtensions.x-java-is-response-void}}{{#vendorExtensions.x-java-is-response-void}}{{#microprofileMutiny}}Uni{{/microprofileMutiny}}{{^microprofileMutiny}}void{{/microprofileMutiny}}{{/vendorExtensions.x-java-is-response-void}} {{nickname}}({{#hasNonBodyParams}}@BeanParam {{operationIdCamelCase}}Request request{{/hasNonBodyParams}}{{#bodyParams}}{{#hasNonBodyParams}}, {{/hasNonBodyParams}}{{>bodyParams}}{{/bodyParams}}) throws ApiException, ProcessingException; {{#hasNonBodyParams}} public class {{operationIdCamelCase}}Request { @@ -114,7 +114,7 @@ public interface {{classname}} { {{/allParams}} } {{/hasNonBodyParams}} -{{/useSingleRequestParameter}} +{{/singleRequestParameter}} {{/operation}} } {{/operations}} diff --git a/modules/openapi-generator/src/main/resources/Java/libraries/restclient/api.mustache b/modules/openapi-generator/src/main/resources/Java/libraries/restclient/api.mustache index 4d637f42bdd..567ae4983e1 100644 --- a/modules/openapi-generator/src/main/resources/Java/libraries/restclient/api.mustache +++ b/modules/openapi-generator/src/main/resources/Java/libraries/restclient/api.mustache @@ -52,7 +52,7 @@ public class {{classname}} { } {{#operation}} -{{#useSingleRequestParameter}} +{{#singleRequestParameter}} {{#hasParams}} {{^hasSingleParam}} @@ -108,7 +108,7 @@ public class {{classname}} { {{/hasSingleParam}} {{/hasParams}} -{{/useSingleRequestParameter}} +{{/singleRequestParameter}} /** * {{summary}} * {{notes}} diff --git a/modules/openapi-generator/src/main/resources/Java/libraries/webclient/api.mustache b/modules/openapi-generator/src/main/resources/Java/libraries/webclient/api.mustache index b1c395b0851..4c88bf7b411 100644 --- a/modules/openapi-generator/src/main/resources/Java/libraries/webclient/api.mustache +++ b/modules/openapi-generator/src/main/resources/Java/libraries/webclient/api.mustache @@ -53,8 +53,8 @@ public class {{classname}} { this.apiClient = apiClient; } - {{#operation}}{{#useSingleRequestParameter}}{{#hasParams}}{{^hasSingleParam}} - public class {{#lambda.titlecase}}{{operationId}}{{/lambda.titlecase}}Request { + {{#operation}}{{#singleRequestParameter}}{{#hasParams}}{{^hasSingleParam}} + public {{#staticRequest}}static {{/staticRequest}}class {{#lambda.titlecase}}{{operationId}}{{/lambda.titlecase}}Request { {{#allParams}} private final {{#isFile}}{{#useAbstractionForFiles}}{{#collectionFormat}}java.util.Collection{{/collectionFormat}}{{^collectionFormat}}org.springframework.core.io.AbstractResource{{/collectionFormat}}{{/useAbstractionForFiles}}{{^useAbstractionForFiles}}{{{dataType}}}{{/useAbstractionForFiles}}{{/isFile}}{{^isFile}}{{{dataType}}}{{/isFile}} {{paramName}}; {{/allParams}} @@ -120,7 +120,7 @@ public class {{classname}} { return this.{{operationId}}WithResponseSpec({{#allParams}}requestParameters.{{paramName}}(){{^-last}}, {{/-last}}{{/allParams}}); } - {{/hasSingleParam}}{{/hasParams}}{{/useSingleRequestParameter}} + {{/hasSingleParam}}{{/hasParams}}{{/singleRequestParameter}} /** * {{summary}} * {{notes}} diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/JavaClientCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/JavaClientCodegenTest.java index 3ce56219fb4..86caacf810b 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/JavaClientCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/JavaClientCodegenTest.java @@ -3187,7 +3187,7 @@ public class JavaClientCodegenTest { .setLibrary(JavaClientCodegen.RESTCLIENT) .setAdditionalProperties(Map.of( CodegenConstants.API_PACKAGE, "xyz.abcdef.api", - CodegenConstants.USE_SINGLE_REQUEST_PARAMETER, true + CodegenConstants.USE_SINGLE_REQUEST_PARAMETER, "true" )) .setInputSpec("src/test/resources/3_1/java/petstore.yaml") .setOutputDir(output.toString().replace("\\", "/")); @@ -3215,7 +3215,7 @@ public class JavaClientCodegenTest { .setLibrary(JavaClientCodegen.RESTCLIENT) .setAdditionalProperties(Map.of( CodegenConstants.API_PACKAGE, "xyz.abcdef.api", - CodegenConstants.USE_SINGLE_REQUEST_PARAMETER, true + CodegenConstants.USE_SINGLE_REQUEST_PARAMETER, "true" )) .setInputSpec("src/test/resources/3_0/duplicated_operationid.yaml") .setOutputDir(output.toString().replace("\\", "/")); @@ -3250,7 +3250,7 @@ public class JavaClientCodegenTest { .setLibrary(JavaClientCodegen.WEBCLIENT) .setAdditionalProperties(Map.of( CodegenConstants.API_PACKAGE, "xyz.abcdef.api", - CodegenConstants.USE_SINGLE_REQUEST_PARAMETER, true + CodegenConstants.USE_SINGLE_REQUEST_PARAMETER, "true" )) .setInputSpec("src/test/resources/3_1/java/petstore.yaml") .setOutputDir(output.toString().replace("\\", "/")); @@ -3272,6 +3272,35 @@ public class JavaClientCodegenTest { ); } + @Test public void testWebClientWithUseSingleRequestParameter_static() { + final Path output = newTempFolder(); + final CodegenConfigurator configurator = new CodegenConfigurator() + .setGeneratorName("java") + .setLibrary(JavaClientCodegen.WEBCLIENT) + .setAdditionalProperties(Map.of( + CodegenConstants.API_PACKAGE, "xyz.abcdef.api", + CodegenConstants.USE_SINGLE_REQUEST_PARAMETER, "static" + )) + .setInputSpec("src/test/resources/3_1/java/petstore.yaml") + .setOutputDir(output.toString().replace("\\", "/")); + + new DefaultGenerator().opts(configurator.toClientOptInput()).generate(); + + TestUtils.assertFileContains( + output.resolve("src/main/java/xyz/abcdef/api/PetApi.java"), + "public static class DeletePetRequest {", + "DeletePetRequest(Long petId, String apiKey)", + "Long petId()", + "String apiKey()", + "public Mono deletePet(DeletePetRequest requestParameters) throws WebClientResponseException {", + "public Mono> deletePetWithHttpInfo(DeletePetRequest requestParameters) throws WebClientResponseException {", + "public ResponseSpec deletePetWithResponseSpec(DeletePetRequest requestParameters) throws WebClientResponseException {", + "public Mono deletePet(Long petId, String apiKey) throws WebClientResponseException {", + "public Mono> deletePetWithHttpInfo(Long petId, String apiKey) throws WebClientResponseException {", + "public ResponseSpec deletePetWithResponseSpec(Long petId, String apiKey) throws WebClientResponseException {" + ); + } + @Test public void testGenerateParameterId() { final Path output = newTempFolder();