Skip to content

Commit

Permalink
feat (JAVA SPRING RESTCLIENT) 19406: Add single request parameter for…
Browse files Browse the repository at this point in the history
… Spring RestClient (#19430)

* feat (JAVA SPRING RESTCLIENT) 19406: Add single request parameter for Spring RestClient

Closes #19406

* chore (JAVA SPRING RESTCLIENT) 19406: Add new sample to jdk 17 tests

* fix (JAVA SPRING RESTCLIENT) 19406: Fix sample build
  • Loading branch information
Nicklas2751 authored Sep 4, 2024
1 parent f54b0e2 commit e4112c5
Show file tree
Hide file tree
Showing 212 changed files with 26,418 additions and 6 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/samples-java-client-jdk17.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ on:
paths:
- samples/client/petstore/java/resttemplate-jakarta/**
- samples/client/petstore/java/webclient-jakarta/**
- samples/client/petstore/java/restclient-*/**
- samples/client/petstore/java/restclient-*/**
jobs:
build:
name: Build Java Client JDK17
Expand All @@ -25,6 +25,7 @@ jobs:
- samples/client/petstore/java/restclient
- samples/client/petstore/java/restclient-nullable-arrays
- samples/client/petstore/java/restclient-swagger2
- samples/client/petstore/java/restclient-useSingleRequestParameter
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
Expand Down
9 changes: 9 additions & 0 deletions bin/configs/java-restclient-useSingleRequestParameter.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
generatorName: java
outputDir: samples/client/petstore/java/restclient-useSingleRequestParameter
library: restclient
inputSpec: modules/openapi-generator/src/test/resources/3_0/petstore-with-fake-endpoints-models-for-testing.yaml
templateDir: modules/openapi-generator/src/main/resources/Java
additionalProperties:
artifactId: singleparam-restclient
hideGenerationTimestamp: "true"
useSingleRequestParameter: true
2 changes: 1 addition & 1 deletion docs/generators/java-microprofile.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,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 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 support 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|
Expand Down
2 changes: 1 addition & 1 deletion docs/generators/java.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,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 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 support 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|
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ public class CodegenOperation {
isResponseBinary = false, isResponseFile = false, isResponseOptional = false, hasReference = false, defaultReturnType = false,
isRestfulIndex, isRestfulShow, isRestfulCreate, isRestfulUpdate, isRestfulDestroy,
isRestful, isDeprecated, isCallbackRequest, uniqueItems, hasDefaultResponse = false, hasConstantParams = false,
hasErrorResponseObject; // if 4xx, 5xx responses have at least one error object defined
hasErrorResponseObject, // if 4xx, 5xx responses have at least one error object defined
hasSingleParam = false; // if the operation has only one parameter;
public CodegenProperty returnProperty;
public String path, operationId, returnType, returnFormat, httpMethod, returnBaseType,
returnContainer, summary, unescapedNotes, notes, baseName, defaultResponse;
Expand Down Expand Up @@ -351,6 +352,7 @@ public String toString() {
sb.append(", hasReference=").append(hasReference);
sb.append(", hasDefaultResponse=").append(hasDefaultResponse);
sb.append(", hasErrorResponseObject=").append(hasErrorResponseObject);
sb.append(", hasSingleParam=").append(hasSingleParam);
sb.append(", isRestfulIndex=").append(isRestfulIndex);
sb.append(", isRestfulShow=").append(isRestfulShow);
sb.append(", isRestfulCreate=").append(isRestfulCreate);
Expand Down Expand Up @@ -431,6 +433,7 @@ public boolean equals(Object o) {
hasReference == that.hasReference &&
hasDefaultResponse == that.hasDefaultResponse &&
hasErrorResponseObject == that.hasErrorResponseObject &&
hasSingleParam == that.hasSingleParam &&
isRestfulIndex == that.isRestfulIndex &&
isRestfulShow == that.isRestfulShow &&
isRestfulCreate == that.isRestfulCreate &&
Expand Down Expand Up @@ -500,6 +503,6 @@ public int hashCode() {
pathParams, queryParams, headerParams, formParams, cookieParams, requiredParams, returnProperty, optionalParams,
authMethods, tags, responses, callbacks, imports, examples, requestBodyExamples, externalDocs,
vendorExtensions, nickname, operationIdOriginal, operationIdLowerCase, operationIdCamelCase,
operationIdSnakeCase, hasErrorResponseObject, requiredAndNotNullableParams, notNullableParams, constantParams);
operationIdSnakeCase, hasErrorResponseObject, hasSingleParam, requiredAndNotNullableParams, notNullableParams, constantParams);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4738,6 +4738,9 @@ public CodegenOperation fromOperation(String path,
}
op.hasRequiredParams = op.requiredParams.size() > 0;

// check if the operation has only a single parameter
op.hasSingleParam = op.allParams.size() == 1;

// set Restful Flag
op.isRestfulShow = op.isRestfulShow();
op.isRestfulIndex = op.isRestfulIndex();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ public JavaClientCodegen() {
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 support this option."));
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 support this option."));
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."));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,79 @@ public class {{classname}} {
}

{{#operation}}
{{#useSingleRequestParameter}}
{{#hasParams}}
{{#hasSingleParam}}
// It has a single param!
/*
{{#allParams}}
* {{paramName}}
{{/allParams}}
*/
{{/hasSingleParam}}
{{^hasSingleParam}}
// It has NO single param!
/*
{{#allParams}}
* {{paramName}}
{{/allParams}}
*/
{{/hasSingleParam}}
{{^hasSingleParam}}

public record {{#lambda.titlecase}}{{operationId}}{{/lambda.titlecase}}Request({{#allParams}}{{#isFile}}{{#useAbstractionForFiles}}{{#collectionFormat}}java.util.Collection<org.springframework.core.io.AbstractResource>{{/collectionFormat}}{{^collectionFormat}}org.springframework.core.io.AbstractResource{{/collectionFormat}}{{/useAbstractionForFiles}}{{^useAbstractionForFiles}}{{{dataType}}}{{/useAbstractionForFiles}}{{/isFile}}{{^isFile}}{{{dataType}}}{{/isFile}} {{paramName}}{{^-last}}, {{/-last}}{{/allParams}}){}

/**
* {{summary}}
* {{notes}}
{{#responses}} * <p><b>{{code}}</b>{{#message}} - {{.}}{{/message}}
{{/responses}} * @param requestParameters The {{operationId}} request parameters as object
{{#returnType}} * @return {{.}}
{{/returnType}} * @throws RestClientResponseException if an error occurs while attempting to invoke the API
{{#externalDocs}}
* {{description}}
* @see <a href="{{url}}">{{summary}} Documentation</a>
{{/externalDocs}}
*/
public {{#returnType}}{{#isResponseFile}}{{#useAbstractionForFiles}}org.springframework.core.io.Resource{{/useAbstractionForFiles}}{{^useAbstractionForFiles}}{{{returnType}}}{{/useAbstractionForFiles}}{{/isResponseFile}}{{^isResponseFile}}{{{returnType}}}{{/isResponseFile}} {{/returnType}}{{^returnType}}void {{/returnType}}{{operationId}}({{#lambda.titlecase}}{{operationId}}{{/lambda.titlecase}}Request requestParameters) throws RestClientResponseException {
{{#returnType}}return {{/returnType}}this.{{operationId}}({{#allParams}}requestParameters.{{paramName}}(){{^-last}}, {{/-last}}{{/allParams}});
}

/**
* {{summary}}
* {{notes}}
{{#responses}} * <p><b>{{code}}</b>{{#message}} - {{.}}{{/message}}
{{/responses}} * @param requestParameters The {{operationId}} request parameters as object
{{#returnType}} * @return ResponseEntity&lt;{{.}}&gt;
{{/returnType}} * @throws RestClientResponseException if an error occurs while attempting to invoke the API
{{#externalDocs}}
* {{description}}
* @see <a href="{{url}}">{{summary}} Documentation</a>
{{/externalDocs}}
*/
public {{#returnType}}ResponseEntity<{{#isResponseFile}}{{#useAbstractionForFiles}}org.springframework.core.io.Resource{{/useAbstractionForFiles}}{{^useAbstractionForFiles}}{{{returnType}}}{{/useAbstractionForFiles}}{{/isResponseFile}}{{^isResponseFile}}{{{returnType}}}{{/isResponseFile}}>{{/returnType}}{{^returnType}}ResponseEntity<Void>{{/returnType}} {{operationId}}WithHttpInfo({{#lambda.titlecase}}{{operationId}}{{/lambda.titlecase}}Request requestParameters) throws RestClientResponseException {
return this.{{operationId}}WithHttpInfo({{#allParams}}requestParameters.{{paramName}}(){{^-last}}, {{/-last}}{{/allParams}});
}

/**
* {{summary}}
* {{notes}}
{{#responses}} * <p><b>{{code}}</b>{{#message}} - {{.}}{{/message}}
{{/responses}} * @param requestParameters The {{operationId}} request parameters as object
* @return ResponseSpec
* @throws RestClientResponseException if an error occurs while attempting to invoke the API
{{#externalDocs}}
* {{description}}
* @see <a href="{{url}}">{{summary}} Documentation</a>
{{/externalDocs}}
*/
public ResponseSpec {{operationId}}WithResponseSpec({{#lambda.titlecase}}{{operationId}}{{/lambda.titlecase}}Request requestParameters) throws RestClientResponseException {
return this.{{operationId}}WithResponseSpec({{#allParams}}requestParameters.{{paramName}}(){{^-last}}, {{/-last}}{{/allParams}});
}

{{/hasSingleParam}}
{{/hasParams}}
{{/useSingleRequestParameter}}
/**
* {{summary}}
* {{notes}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4935,4 +4935,30 @@ public void testAllVars_issue_18340() {
CodegenModel springCat2 = springCodegen.fromModel("Cat2", cat2Model);
assertThat(getNames(springCat2.allVars)).isEqualTo(List.of("petType", "name"));
}

@Test
public void testMultipleRequestParameter_hasSingleParamFalse() {
// Given
DefaultCodegen codegen = new DefaultCodegen();
final OpenAPI openAPI = TestUtils.parseFlattenSpec("src/test/resources/3_0/content-data.yaml");
codegen.setOpenAPI(openAPI);
String path = "/jsonQueryParams";
CodegenOperation codegenOperation = codegen.fromOperation(path, "GET", openAPI.getPaths().get(path).getGet(), null);

// When & Then
assertThat(codegenOperation.hasSingleParam).isFalse();
}

@Test
public void testSingleRequestParameter_hasSingleParamTrue() {
// Given
DefaultCodegen codegen = new DefaultCodegen();
final OpenAPI openAPI = TestUtils.parseFlattenSpec("src/test/resources/2_0/petstore-with-fake-endpoints-models-for-testing.yaml");
codegen.setOpenAPI(openAPI);
String path = "/fake/inline-additionalProperties";
CodegenOperation codegenOperation = codegen.fromOperation(path, "POST", openAPI.getPaths().get(path).getPost(), null);

// When & Then
assertThat(codegenOperation.hasSingleParam).isTrue();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2988,4 +2988,30 @@ public void shouldGenerateOAuthTokenSuppliers() {
);
}

@Test public void testRestClientWithUseSingleRequestParameter_issue_19406() {
final Path output = newTempFolder();
final CodegenConfigurator configurator = new CodegenConfigurator()
.setGeneratorName("java")
.setLibrary(JavaClientCodegen.RESTCLIENT)
.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 record DeletePetRequest(Long petId, String apiKey){}",
"public void deletePet(DeletePetRequest requestParameters) throws RestClientResponseException {",
"public ResponseEntity<Void> deletePetWithHttpInfo(DeletePetRequest requestParameters) throws RestClientResponseException {",
"public ResponseSpec deletePetWithResponseSpec(DeletePetRequest requestParameters) throws RestClientResponseException {",
"public void deletePet(Long petId, String apiKey) throws RestClientResponseException {",
"public ResponseEntity<Void> deletePetWithHttpInfo(Long petId, String apiKey) throws RestClientResponseException {",
"public ResponseSpec deletePetWithResponseSpec(Long petId, String apiKey) throws RestClientResponseException {"
);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven
#
# This file is auto-generated by OpenAPI Generator (https://openapi-generator.tech)

name: Java CI with Maven

on:
push:
branches: [ main, master ]
pull_request:
branches: [ main, master ]

jobs:
build:
name: Build OpenAPI Petstore
runs-on: ubuntu-latest
strategy:
matrix:
java: [ 17, 21 ]
steps:
- uses: actions/checkout@v4
- name: Set up JDK
uses: actions/setup-java@v4
with:
java-version: ${{ matrix.java }}
distribution: 'temurin'
cache: maven
- name: Build with Maven
run: mvn -B package --no-transfer-progress --file pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
*.class

# Mobile Tools for Java (J2ME)
.mtj.tmp/

# Package Files #
*.jar
*.war
*.ear

# exclude jar for gradle wrapper
!gradle/wrapper/*.jar

# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*

# build files
**/target
target
.gradle
build
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
Loading

0 comments on commit e4112c5

Please sign in to comment.