feat (JAVA NATIVE): add support for useSingleRequestParameter to java native client (#21331)

This commit is contained in:
Nicklas Wiegandt 2025-06-02 09:36:59 +02:00 committed by GitHub
parent bce88c93ba
commit 5997acb592
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 64 additions and 30 deletions

View File

@ -101,7 +101,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. Setting this property to "static" does the same as "true", but also makes the generated arguments class static with single parameter instantiation.| |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 native, 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 with single parameter instantiation.| |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|

View File

@ -101,7 +101,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. Setting this property to "static" does the same as "true", but also makes the generated arguments class static with single parameter instantiation.| |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 native, 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 with single parameter instantiation.| |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|

View File

@ -239,7 +239,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.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 with single parameter instantiation.").defaultValue("false"));
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 native, 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 with single parameter instantiation.").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, restclient and webclient, 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."));
@ -800,7 +800,7 @@ public class JavaClientCodegen extends AbstractJavaCodegen
public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List<ModelMap> allModels) {
super.postProcessOperationsWithModels(objs, allModels);
if (this.getSingleRequestParameter() && (isLibrary(JERSEY2) || isLibrary(JERSEY3) || isLibrary(OKHTTP_GSON))) {
if (this.getSingleRequestParameter() && (isLibrary(JERSEY2) || isLibrary(JERSEY3) || isLibrary(OKHTTP_GSON) || isLibrary(NATIVE))) {
// loop through operations to set x-group-parameters extension to true if useSingleRequestParameter option is enabled
OperationMap operations = objs.getOperations();
if (operations != null) {

View File

@ -106,7 +106,7 @@ public class {{classname}} {
/**
* {{summary}}
* {{notes}}
* @param apiRequest {@link API{{operationId}}Request}
* @param apiRequest {@link API{{#lambda.titlecase}}{{operationId}}{{/lambda.titlecase}}Request}
{{#returnType}}
* @return {{#asyncNative}}CompletableFuture&lt;{{/asyncNative}}{{returnType}}{{#asyncNative}}&gt;{{/asyncNative}}
{{/returnType}}
@ -127,7 +127,7 @@ public class {{classname}} {
{{#isDeprecated}}
@Deprecated
{{/isDeprecated}}
public {{#returnType}}{{#asyncNative}}CompletableFuture<{{{returnType}}}>{{/asyncNative}}{{^asyncNative}}{{{returnType}}}{{/asyncNative}}{{/returnType}}{{^returnType}}{{#asyncNative}}CompletableFuture<Void>{{/asyncNative}}{{^asyncNative}}void{{/asyncNative}}{{/returnType}} {{operationId}}(API{{operationId}}Request apiRequest) throws ApiException {
public {{#returnType}}{{#asyncNative}}CompletableFuture<{{{returnType}}}>{{/asyncNative}}{{^asyncNative}}{{{returnType}}}{{/asyncNative}}{{/returnType}}{{^returnType}}{{#asyncNative}}CompletableFuture<Void>{{/asyncNative}}{{^asyncNative}}void{{/asyncNative}}{{/returnType}} {{operationId}}(API{{#lambda.titlecase}}{{operationId}}{{/lambda.titlecase}}Request apiRequest) throws ApiException {
{{#allParams}}
{{>nullable_var_annotations}}
{{{dataType}}} {{paramName}} = apiRequest.{{paramName}}();
@ -138,7 +138,7 @@ public class {{classname}} {
/**
* {{summary}}
* {{notes}}
* @param apiRequest {@link API{{operationId}}Request}
* @param apiRequest {@link API{{#lambda.titlecase}}{{operationId}}{{/lambda.titlecase}}Request}
* @return {{#asyncNative}}CompletableFuture&lt;{{/asyncNative}}ApiResponse&lt;{{returnType}}{{^returnType}}Void{{/returnType}}&gt;{{#asyncNative}}&gt;{{/asyncNative}}
* @throws ApiException if fails to make API call
{{#isDeprecated}}
@ -152,7 +152,7 @@ public class {{classname}} {
{{#isDeprecated}}
@Deprecated
{{/isDeprecated}}
public {{#asyncNative}}CompletableFuture<{{/asyncNative}}ApiResponse<{{{returnType}}}{{^returnType}}Void{{/returnType}}>{{#asyncNative}}>{{/asyncNative}} {{operationId}}WithHttpInfo(API{{operationId}}Request apiRequest) throws ApiException {
public {{#asyncNative}}CompletableFuture<{{/asyncNative}}ApiResponse<{{{returnType}}}{{^returnType}}Void{{/returnType}}>{{#asyncNative}}>{{/asyncNative}} {{operationId}}WithHttpInfo(API{{#lambda.titlecase}}{{operationId}}{{/lambda.titlecase}}Request apiRequest) throws ApiException {
{{#allParams}}
{{{dataType}}} {{paramName}} = apiRequest.{{paramName}}();
{{/allParams}}
@ -574,7 +574,7 @@ public class {{classname}} {
{{#vendorExtensions.x-group-parameters}}
{{#hasParams}}
public static final class API{{operationId}}Request {
public static final class API{{#lambda.titlecase}}{{operationId}}{{/lambda.titlecase}}Request {
{{#requiredParams}}
{{>nullable_var_annotations}}
private {{{dataType}}} {{paramName}}; // {{description}} (required)
@ -584,7 +584,7 @@ public class {{classname}} {
private {{{dataType}}} {{paramName}}; // {{description}} (optional{{^isContainer}}{{#defaultValue}}, default to {{.}}{{/defaultValue}}{{/isContainer}})
{{/optionalParams}}
private API{{operationId}}Request(Builder builder) {
private API{{#lambda.titlecase}}{{operationId}}{{/lambda.titlecase}}Request(Builder builder) {
{{#requiredParams}}
this.{{paramName}} = builder.{{paramName}};
{{/requiredParams}}
@ -616,8 +616,8 @@ public class {{classname}} {
return this;
}
{{/allParams}}
public API{{operationId}}Request build() {
return new API{{operationId}}Request(this);
public API{{#lambda.titlecase}}{{operationId}}{{/lambda.titlecase}}Request build() {
return new API{{#lambda.titlecase}}{{operationId}}{{/lambda.titlecase}}Request(this);
}
}
}

View File

@ -3237,6 +3237,7 @@ public class JavaClientCodegenTest {
"String apiKey()",
"DeletePetRequest apiKey(@jakarta.annotation.Nullable String apiKey) {",
"public void deletePet(DeletePetRequest requestParameters) throws RestClientResponseException {",
"Pet getPetById(@jakarta.annotation.Nonnull Long petId) throws RestClientResponseException",
"public ResponseEntity<Void> deletePetWithHttpInfo(DeletePetRequest requestParameters) throws RestClientResponseException {",
"public ResponseSpec deletePetWithResponseSpec(DeletePetRequest requestParameters) throws RestClientResponseException {",
"public void deletePet(@jakarta.annotation.Nonnull Long petId, @jakarta.annotation.Nullable String apiKey) throws RestClientResponseException {",
@ -3665,4 +3666,37 @@ public class JavaClientCodegenTest {
assertTrue(defaultFields.get("testNullableEmptyReference").getVariable(0).getInitializer().get().isObjectCreationExpr());
assertTrue(defaultFields.get("testNullableComplexReference").getVariable(0).getInitializer().get().isMethodCallExpr());
}
@Test
public void testNativeClientWithUseSingleRequestParameter() {
final Path output = newTempFolder();
final CodegenConfigurator configurator = new CodegenConfigurator()
.setGeneratorName("java")
.setLibrary(NATIVE)
.setAdditionalProperties(Map.of(
CodegenConstants.API_PACKAGE, "xyz.abcdef.api",
CodegenConstants.USE_SINGLE_REQUEST_PARAMETER, "true"
))
.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 final class APIDeletePetRequest {",
"private APIDeletePetRequest(Builder builder) {",
"public Builder petId(@javax.annotation.Nonnull Long petId) {",
"public Builder apiKey(@javax.annotation.Nullable String apiKey) {",
"Long petId()",
"String apiKey()",
"public void deletePet(APIDeletePetRequest apiRequest) throws ApiException {",
"Pet getPetById(@javax.annotation.Nonnull Long petId) throws ApiException",
"public ApiResponse<Void> deletePetWithHttpInfo(APIDeletePetRequest apiRequest) throws ApiException {",
"public void deletePet(@javax.annotation.Nonnull Long petId, @javax.annotation.Nullable String apiKey) throws ApiException {",
"public ApiResponse<Void> deletePetWithHttpInfo(@javax.annotation.Nonnull Long petId, @javax.annotation.Nullable String apiKey) throws ApiException {"
);
TestUtils.assertFileNotContains(output.resolve("src/main/java/xyz/abcdef/api/PetApi.java"),
"public record DeletePetRequest(Long petId, String apiKey){}");
}
}

View File

@ -1501,11 +1501,11 @@ public class FakeApi {
/**
* Fake endpoint to test group parameters (optional)
* Fake endpoint to test group parameters (optional)
* @param apiRequest {@link APItestGroupParametersRequest}
* @param apiRequest {@link APITestGroupParametersRequest}
* @return CompletableFuture&lt;Void&gt;
* @throws ApiException if fails to make API call
*/
public CompletableFuture<Void> testGroupParameters(APItestGroupParametersRequest apiRequest) throws ApiException {
public CompletableFuture<Void> testGroupParameters(APITestGroupParametersRequest apiRequest) throws ApiException {
@javax.annotation.Nonnull
Integer requiredStringGroup = apiRequest.requiredStringGroup();
@javax.annotation.Nonnull
@ -1524,11 +1524,11 @@ public class FakeApi {
/**
* Fake endpoint to test group parameters (optional)
* Fake endpoint to test group parameters (optional)
* @param apiRequest {@link APItestGroupParametersRequest}
* @param apiRequest {@link APITestGroupParametersRequest}
* @return CompletableFuture&lt;ApiResponse&lt;Void&gt;&gt;
* @throws ApiException if fails to make API call
*/
public CompletableFuture<ApiResponse<Void>> testGroupParametersWithHttpInfo(APItestGroupParametersRequest apiRequest) throws ApiException {
public CompletableFuture<ApiResponse<Void>> testGroupParametersWithHttpInfo(APITestGroupParametersRequest apiRequest) throws ApiException {
Integer requiredStringGroup = apiRequest.requiredStringGroup();
Boolean requiredBooleanGroup = apiRequest.requiredBooleanGroup();
Long requiredInt64Group = apiRequest.requiredInt64Group();
@ -1662,7 +1662,7 @@ public class FakeApi {
}
public static final class APItestGroupParametersRequest {
public static final class APITestGroupParametersRequest {
@javax.annotation.Nonnull
private Integer requiredStringGroup; // Required String in group parameters (required)
@javax.annotation.Nonnull
@ -1676,7 +1676,7 @@ public class FakeApi {
@javax.annotation.Nullable
private Long int64Group; // Integer in group parameters (optional)
private APItestGroupParametersRequest(Builder builder) {
private APITestGroupParametersRequest(Builder builder) {
this.requiredStringGroup = builder.requiredStringGroup;
this.requiredBooleanGroup = builder.requiredBooleanGroup;
this.requiredInt64Group = builder.requiredInt64Group;
@ -1744,8 +1744,8 @@ public class FakeApi {
this.int64Group = int64Group;
return this;
}
public APItestGroupParametersRequest build() {
return new APItestGroupParametersRequest(this);
public APITestGroupParametersRequest build() {
return new APITestGroupParametersRequest(this);
}
}
}

View File

@ -318,7 +318,7 @@ public class FakeApiTest {
Boolean booleanGroup = null;
Long int64Group = null;
FakeApi.APItestGroupParametersRequest request = FakeApi.APItestGroupParametersRequest.newBuilder()
FakeApi.APITestGroupParametersRequest request = FakeApi.APITestGroupParametersRequest.newBuilder()
.requiredStringGroup(requiredStringGroup)
.requiredBooleanGroup(requiredBooleanGroup)
.requiredInt64Group(requiredInt64Group)

View File

@ -1395,10 +1395,10 @@ public class FakeApi {
/**
* Fake endpoint to test group parameters (optional)
* Fake endpoint to test group parameters (optional)
* @param apiRequest {@link APItestGroupParametersRequest}
* @param apiRequest {@link APITestGroupParametersRequest}
* @throws ApiException if fails to make API call
*/
public void testGroupParameters(APItestGroupParametersRequest apiRequest) throws ApiException {
public void testGroupParameters(APITestGroupParametersRequest apiRequest) throws ApiException {
@javax.annotation.Nonnull
Integer requiredStringGroup = apiRequest.requiredStringGroup();
@javax.annotation.Nonnull
@ -1417,11 +1417,11 @@ public class FakeApi {
/**
* Fake endpoint to test group parameters (optional)
* Fake endpoint to test group parameters (optional)
* @param apiRequest {@link APItestGroupParametersRequest}
* @param apiRequest {@link APITestGroupParametersRequest}
* @return ApiResponse&lt;Void&gt;
* @throws ApiException if fails to make API call
*/
public ApiResponse<Void> testGroupParametersWithHttpInfo(APItestGroupParametersRequest apiRequest) throws ApiException {
public ApiResponse<Void> testGroupParametersWithHttpInfo(APITestGroupParametersRequest apiRequest) throws ApiException {
Integer requiredStringGroup = apiRequest.requiredStringGroup();
Boolean requiredBooleanGroup = apiRequest.requiredBooleanGroup();
Long requiredInt64Group = apiRequest.requiredInt64Group();
@ -1552,7 +1552,7 @@ public class FakeApi {
}
public static final class APItestGroupParametersRequest {
public static final class APITestGroupParametersRequest {
@javax.annotation.Nonnull
private Integer requiredStringGroup; // Required String in group parameters (required)
@javax.annotation.Nonnull
@ -1566,7 +1566,7 @@ public class FakeApi {
@javax.annotation.Nullable
private Long int64Group; // Integer in group parameters (optional)
private APItestGroupParametersRequest(Builder builder) {
private APITestGroupParametersRequest(Builder builder) {
this.requiredStringGroup = builder.requiredStringGroup;
this.requiredBooleanGroup = builder.requiredBooleanGroup;
this.requiredInt64Group = builder.requiredInt64Group;
@ -1634,8 +1634,8 @@ public class FakeApi {
this.int64Group = int64Group;
return this;
}
public APItestGroupParametersRequest build() {
return new APItestGroupParametersRequest(this);
public APITestGroupParametersRequest build() {
return new APITestGroupParametersRequest(this);
}
}
}

View File

@ -317,7 +317,7 @@ public class FakeApiTest {
Boolean booleanGroup = null;
Long int64Group = null;
FakeApi.APItestGroupParametersRequest request = FakeApi.APItestGroupParametersRequest.newBuilder()
FakeApi.APITestGroupParametersRequest request = FakeApi.APITestGroupParametersRequest.newBuilder()
.requiredStringGroup(requiredStringGroup)
.requiredBooleanGroup(requiredBooleanGroup)
.requiredInt64Group(requiredInt64Group)