diff --git a/docs/generators/java-camel.md b/docs/generators/java-camel.md index e77c38151eb3..055498e71fae 100644 --- a/docs/generators/java-camel.md +++ b/docs/generators/java-camel.md @@ -92,6 +92,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| +|springHttpClientAdapter|Allows users to choose between different HTTP client implementations for Spring HTTP interfaces (`web-client`, `rest-client`, `rest-template`).| |web-client| |testOutput|Set output folder for models and APIs tests| |${project.build.directory}/generated-test-sources/openapi| |title|server title name or client service name| |OpenAPI Spring| |unhandledException|Declare operation methods to throw a generic exception and allow unhandled exceptions (useful for Spring `@ControllerAdvice` directives).| |false| diff --git a/docs/generators/spring.md b/docs/generators/spring.md index b71cefb3c126..2673ed2f54c0 100644 --- a/docs/generators/spring.md +++ b/docs/generators/spring.md @@ -85,6 +85,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| +|springHttpClientAdapter|Allows users to choose between different HTTP client implementations for Spring HTTP interfaces (`web-client`, `rest-client`, `rest-template`).| |web-client| |testOutput|Set output folder for models and APIs tests| |${project.build.directory}/generated-test-sources/openapi| |title|server title name or client service name| |OpenAPI Spring| |unhandledException|Declare operation methods to throw a generic exception and allow unhandled exceptions (useful for Spring `@ControllerAdvice` directives).| |false| diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/SpringCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/SpringCodegen.java index cb3c52a99c75..912d9d7112cb 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/SpringCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/SpringCodegen.java @@ -36,8 +36,10 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; +import java.util.stream.Stream; import lombok.Getter; +import lombok.RequiredArgsConstructor; import lombok.Setter; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Pair; @@ -102,6 +104,7 @@ public class SpringCodegen extends AbstractJavaCodegen public static final String SPRING_BOOT = "spring-boot"; public static final String SPRING_CLOUD_LIBRARY = "spring-cloud"; public static final String SPRING_HTTP_INTERFACE = "spring-http-interface"; + public static final String SPRING_HTTP_CLIENT_ADAPTER = "springHttpClientAdapter"; public static final String API_FIRST = "apiFirst"; public static final String SPRING_CONTROLLER = "useSpringController"; public static final String HATEOAS = "hateoas"; @@ -126,6 +129,25 @@ public class SpringCodegen extends AbstractJavaCodegen } } + @Getter + @RequiredArgsConstructor + public enum SpringHttpClientAdapter { + web_client("web-client", "Use WebClientAdapter", "httpInterfacesWebClientConfiguration.mustache"), + rest_client("rest-client", "Use RestClientAdapter", "httpInterfacesRestClientConfiguration.mustache"), + rest_template("rest-template", "Use RestTemplateAdapter", "httpInterfacesRestTemplateConfiguration.mustache"); + + private final String key; + private final String description; + private final String templateFileName; + + static SpringHttpClientAdapter fromKey(String key) { + return Stream.of(values()).filter(value -> value.getKey().equals(key)).findFirst().orElseThrow( + () -> new IllegalArgumentException("Invalid SpringHttpClientAdapter key: " + key) + ); + } + + } + public static final String OPEN_BRACE = "{"; public static final String CLOSE_BRACE = "}"; @@ -165,6 +187,7 @@ public class SpringCodegen extends AbstractJavaCodegen protected boolean generatedConstructorWithRequiredArgs = true; @Getter @Setter protected RequestMappingMode requestMappingMode = RequestMappingMode.controller; + @Setter SpringHttpClientAdapter springHttpClientAdapter = SpringHttpClientAdapter.web_client; public SpringCodegen() { super(); @@ -268,6 +291,10 @@ public SpringCodegen() { generatedConstructorWithRequiredArgs)); cliOptions.add(new CliOption(RESOURCE_FOLDER, RESOURCE_FOLDER_DESC).defaultValue(this.getResourceFolder())); + cliOptions.add(CliOption.newString(SPRING_HTTP_CLIENT_ADAPTER, + "Allows users to choose between different HTTP client implementations for Spring HTTP interfaces (`web-client`, `rest-client`, `rest-template`).") + .defaultValue(SpringHttpClientAdapter.web_client.getKey()) + ); supportedLibraries.put(SPRING_BOOT, "Spring-boot Server application."); supportedLibraries.put(SPRING_CLOUD_LIBRARY, "Spring-Cloud-Feign client with Spring-Boot auto-configured settings."); @@ -443,7 +470,9 @@ public void processOpts() { useJakartaEe=true; applyJakartaPackage(); } + convertPropertyToStringAndWriteBack(RESOURCE_FOLDER, this::setResourceFolder); + convertPropertyToTypeAndWriteBack(SPRING_HTTP_CLIENT_ADAPTER, SpringHttpClientAdapter::fromKey, this::setSpringHttpClientAdapter); typeMapping.put("file", "org.springframework.core.io.Resource"); importMapping.put("org.springframework.core.io.Resource", "org.springframework.core.io.Resource"); @@ -528,7 +557,12 @@ public void processOpts() { } } } else if (SPRING_HTTP_INTERFACE.equals(library)) { - supportingFiles.add(new SupportingFile("httpInterfacesConfiguration.mustache", + if (!reactive && springHttpClientAdapter == SpringHttpClientAdapter.web_client) { + LOGGER.warn("Configuration mismatch: The 'web-client' is selected as the HTTP client adapter, " + + "but 'reactive' is set to 'false'. " + + "Consider using 'rest-template' or 'rest-client' for non-reactive configurations."); + } + supportingFiles.add(new SupportingFile(springHttpClientAdapter.getTemplateFileName(), (sourceFolder + File.separator + configPackage).replace(".", java.io.File.separator), "HttpInterfacesAbstractConfigurator.java")); writePropertyBack(USE_BEANVALIDATION, false); } diff --git a/modules/openapi-generator/src/main/resources/JavaSpring/libraries/spring-http-interface/httpInterfacesRestClientConfiguration.mustache b/modules/openapi-generator/src/main/resources/JavaSpring/libraries/spring-http-interface/httpInterfacesRestClientConfiguration.mustache new file mode 100644 index 000000000000..9a1b2cec2843 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/JavaSpring/libraries/spring-http-interface/httpInterfacesRestClientConfiguration.mustache @@ -0,0 +1,38 @@ +/** +* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech) ({{{generatorVersion}}}). +* https://openapi-generator.tech +* Do not edit the class manually. +*/ +package {{configPackage}}; + +{{#apiInfo}} + {{#apis}} +import {{apiPackage}}.{{classname}}; + {{/apis}} +{{/apiInfo}} + +import org.springframework.context.annotation.Bean; +import org.springframework.web.client.RestClient; +import org.springframework.web.client.support.RestClientAdapter; +import org.springframework.web.service.invoker.HttpServiceProxyFactory; + +public abstract class HttpInterfacesAbstractConfigurator { + + private final RestClient restClient; + + public HttpInterfacesAbstractConfigurator(final RestClient restClient) { + this.restClient = restClient; + } + +{{#apiInfo}} +{{#apis}} + @Bean(name = "{{configPackage}}.HttpInterfacesAbstractConfigurator.{{classVarName}}") + {{classname}} {{classVarName}}HttpProxy() { + HttpServiceProxyFactory factory = HttpServiceProxyFactory.builder(RestClientAdapter.create(restClient)).build(); + return factory.createClient({{classname}}.class); + } + +{{/apis}} +{{/apiInfo}} + +} diff --git a/modules/openapi-generator/src/main/resources/JavaSpring/libraries/spring-http-interface/httpInterfacesRestTemplateConfiguration.mustache b/modules/openapi-generator/src/main/resources/JavaSpring/libraries/spring-http-interface/httpInterfacesRestTemplateConfiguration.mustache new file mode 100644 index 000000000000..1540cf43bc13 --- /dev/null +++ b/modules/openapi-generator/src/main/resources/JavaSpring/libraries/spring-http-interface/httpInterfacesRestTemplateConfiguration.mustache @@ -0,0 +1,38 @@ +/** +* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech) ({{{generatorVersion}}}). +* https://openapi-generator.tech +* Do not edit the class manually. +*/ +package {{configPackage}}; + +{{#apiInfo}} + {{#apis}} +import {{apiPackage}}.{{classname}}; + {{/apis}} +{{/apiInfo}} + +import org.springframework.context.annotation.Bean; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.client.support.RestTemplateAdapter; +import org.springframework.web.service.invoker.HttpServiceProxyFactory; + +public abstract class HttpInterfacesAbstractConfigurator { + + private final RestTemplate restTemplate; + + public HttpInterfacesAbstractConfigurator(final RestTemplate restTemplate) { + this.restTemplate = restTemplate; + } + +{{#apiInfo}} +{{#apis}} + @Bean(name = "{{configPackage}}.HttpInterfacesAbstractConfigurator.{{classVarName}}") + {{classname}} {{classVarName}}HttpProxy() { + HttpServiceProxyFactory factory = HttpServiceProxyFactory.builder(RestTemplateAdapter.create(restTemplate)).build(); + return factory.createClient({{classname}}.class); + } + +{{/apis}} +{{/apiInfo}} + +} diff --git a/modules/openapi-generator/src/main/resources/JavaSpring/libraries/spring-http-interface/httpInterfacesConfiguration.mustache b/modules/openapi-generator/src/main/resources/JavaSpring/libraries/spring-http-interface/httpInterfacesWebClientConfiguration.mustache similarity index 100% rename from modules/openapi-generator/src/main/resources/JavaSpring/libraries/spring-http-interface/httpInterfacesConfiguration.mustache rename to modules/openapi-generator/src/main/resources/JavaSpring/libraries/spring-http-interface/httpInterfacesWebClientConfiguration.mustache diff --git a/modules/openapi-generator/src/main/resources/JavaSpring/libraries/spring-http-interface/paramDoc.mustache b/modules/openapi-generator/src/main/resources/JavaSpring/libraries/spring-http-interface/paramDoc.mustache new file mode 100644 index 000000000000..e69de29bb2d1