diff --git a/modules/openapi-generator/pom.xml b/modules/openapi-generator/pom.xml
index fb37ae9eafb9..8f9990c7c7ed 100644
--- a/modules/openapi-generator/pom.xml
+++ b/modules/openapi-generator/pom.xml
@@ -385,7 +385,6 @@
com.github.javaparser
javaparser-core
3.24.9
- test
com.googlecode.java-diff-utils
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 2104047f125c..c822defb7c1e 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
@@ -17,6 +17,13 @@
package org.openapitools.codegen.languages;
+import com.github.javaparser.StaticJavaParser;
+import com.github.javaparser.ast.CompilationUnit;
+import com.github.javaparser.ast.body.MethodDeclaration;
+import com.github.javaparser.ast.body.Parameter;
+import com.github.javaparser.ast.expr.AnnotationExpr;
+import com.github.javaparser.ast.type.ClassOrInterfaceType;
+import com.github.javaparser.ast.type.Type;
import com.samskivert.mustache.Mustache;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.Operation;
@@ -50,7 +57,6 @@
import java.net.URL;
import java.util.*;
import java.util.regex.Matcher;
-import java.util.regex.Pattern;
import java.util.stream.Collectors;
import static org.apache.commons.lang3.StringUtils.isNotEmpty;
@@ -1279,9 +1285,15 @@ public CodegenOperation fromOperation(String path, String httpMethod, Operation
importMapping.put("Pageable", "org.springframework.data.domain.Pageable");
}
- Set provideArgsClassSet = reformatProvideArgsParams(operation);
+ ProvideArgsParams provideArgsParams = reformatProvideArgsParams(operation);
CodegenOperation codegenOperation = super.fromOperation(path, httpMethod, operation, servers);
+ if (!provideArgsParams.names.isEmpty()) {
+ codegenOperation.vendorExtensions.put("springProvideArgsNames", provideArgsParams.names);
+ }
+ if (!provideArgsParams.delegateArgs.isEmpty()) {
+ codegenOperation.vendorExtensions.put("springProvideArgsDelegate", provideArgsParams.delegateArgs);
+ }
// add org.springframework.format.annotation.DateTimeFormat when needed
codegenOperation.allParams.stream().filter(p -> p.isDate || p.isDateTime).findFirst()
@@ -1310,8 +1322,8 @@ public CodegenOperation fromOperation(String path, String httpMethod, Operation
generatePageableConstraintValidation, useBeanValidation,
generateSortValidation, SpringPageableScanUtils.AnnotationSyntax.JAVA);
}
- if (codegenOperation.vendorExtensions.containsKey("x-spring-provide-args") && !provideArgsClassSet.isEmpty()) {
- codegenOperation.imports.addAll(provideArgsClassSet);
+ if (codegenOperation.vendorExtensions.containsKey("x-spring-provide-args") && !provideArgsParams.imports.isEmpty()) {
+ codegenOperation.imports.addAll(provideArgsParams.imports);
}
if (isSpringCodegen()) {
@@ -1395,41 +1407,82 @@ public CodegenOperation fromOperation(String path, String httpMethod, Operation
return codegenOperation;
}
- private Set reformatProvideArgsParams(Operation operation) {
- Set provideArgsClassSet = new HashSet<>();
+ private ProvideArgsParams reformatProvideArgsParams(Operation operation) {
+ ProvideArgsParams provideArgsParams = new ProvideArgsParams();
Object argObj = operation.getExtensions().get("x-spring-provide-args");
if (argObj instanceof List) {
List provideArgs = (List) argObj;
if (!provideArgs.isEmpty()) {
List formattedArgs = new ArrayList<>();
+ List formattedArgNames = new ArrayList<>();
+ List formattedDelegateArgs = new ArrayList<>();
for (String oneArg : provideArgs) {
if (StringUtils.isNotEmpty(oneArg)) {
- String regexp = "(?@)?(?(?(\\w+\\.)*)(?\\w+))(?\\(.*?\\))?\\s?";
- Matcher matcher = Pattern.compile(regexp).matcher(oneArg);
- List newArgs = new ArrayList<>();
- while (matcher.find()) {
- String className = matcher.group("ClassName");
- String classPath = matcher.group("ClassPath");
- String packageName = matcher.group("PackageName");
- String params = matcher.group("Params");
- String annoTag = matcher.group("AnnotationTag");
- String shortPhrase = StringUtils.join(annoTag, className, params);
- newArgs.add(shortPhrase);
- if (StringUtils.isNotEmpty(packageName)) {
- importMapping.put(className, classPath);
- provideArgsClassSet.add(className);
- LOGGER.trace("put import mapping {} {}", className, classPath);
- }
- }
- String newArg = String.join(" ", newArgs);
+ Parameter parameter = parseProvideArgParameter(oneArg);
+ collectImportsAndSimplify(parameter, provideArgsParams);
+
+ String newArg = parameter.toString();
LOGGER.trace("new arg {} {}", newArg);
formattedArgs.add(newArg);
+
+ Parameter delegateParameter = parameter.clone();
+ delegateParameter.getAnnotations().clear();
+ formattedDelegateArgs.add(delegateParameter.toString());
+ formattedArgNames.add(parameter.getNameAsString());
}
}
operation.getExtensions().put("x-spring-provide-args", formattedArgs);
+ provideArgsParams.names.addAll(formattedArgNames);
+ provideArgsParams.delegateArgs.addAll(formattedDelegateArgs);
+ }
+ }
+ return provideArgsParams;
+ }
+
+ private Parameter parseProvideArgParameter(String oneArg) {
+ CompilationUnit compilationUnit = StaticJavaParser.parse(String.format(Locale.ROOT, "class Dummy { void method(%s) {} }", oneArg));
+ return compilationUnit.findFirst(MethodDeclaration.class)
+ .orElseThrow(() -> new IllegalArgumentException("Unable to parse x-spring-provide-args parameter: " + oneArg))
+ .getParameter(0);
+ }
+
+ private void collectImportsAndSimplify(Parameter parameter, ProvideArgsParams provideArgsParams) {
+ parameter.findAll(AnnotationExpr.class).forEach(annotation -> {
+ String annotationName = annotation.getNameAsString();
+ if (annotationName.contains(".")) {
+ String simpleName = annotation.getName().getIdentifier();
+ importMapping.put(simpleName, annotationName);
+ provideArgsParams.imports.add(simpleName);
+ annotation.setName(simpleName);
+ LOGGER.trace("put import mapping {} {}", simpleName, annotationName);
+ }
+ });
+
+ parameter.getType().toClassOrInterfaceType().ifPresent(type -> simplifyClassOrInterfaceType(type, provideArgsParams));
+ }
+
+ private void simplifyClassOrInterfaceType(ClassOrInterfaceType type, ProvideArgsParams provideArgsParams) {
+ type.getTypeArguments().stream()
+ .flatMap(Collection::stream)
+ .map(Type::toClassOrInterfaceType)
+ .flatMap(Optional::stream)
+ .forEach(classOrInterfaceType -> simplifyClassOrInterfaceType(classOrInterfaceType, provideArgsParams));
+ if (type.getScope().isPresent()) {
+ String typeName = type.getNameWithScope();
+ if (typeName.contains(".")) {
+ String simpleName = type.getNameAsString();
+ importMapping.put(simpleName, typeName);
+ provideArgsParams.imports.add(simpleName);
+ type.setScope(null);
+ LOGGER.trace("put import mapping {} {}", simpleName, typeName);
}
}
- return provideArgsClassSet;
+ }
+
+ private static final class ProvideArgsParams {
+ private final Set imports = new HashSet<>();
+ private final List names = new ArrayList<>();
+ private final List delegateArgs = new ArrayList<>();
}
@Override
diff --git a/modules/openapi-generator/src/main/resources/JavaSpring/api.mustache b/modules/openapi-generator/src/main/resources/JavaSpring/api.mustache
index 5ac4a949ca1d..bcdae723588d 100644
--- a/modules/openapi-generator/src/main/resources/JavaSpring/api.mustache
+++ b/modules/openapi-generator/src/main/resources/JavaSpring/api.mustache
@@ -279,22 +279,22 @@ public interface {{classname}} {
{{#allParams}}{{>queryParams}}{{>pathParams}}{{>headerParams}}{{>bodyParams}}{{>formParams}}{{>cookieParams}}{{^-last}},
{{/-last}}{{/allParams}}{{#includeHttpRequestContext}}{{#hasParams}},
{{/hasParams}}{{#swagger2AnnotationLibrary}}@Parameter(hidden = true){{/swagger2AnnotationLibrary}} final {{#reactive}}ServerWebExchange exchange{{/reactive}}{{^reactive}}HttpServletRequest servletRequest{{/reactive}}{{/includeHttpRequestContext}}{{#vendorExtensions.x-spring-paginated}}{{#hasParams}},
- {{/hasParams}}{{^hasParams}}{{#includeHttpRequestContext}},{{/includeHttpRequestContext}}{{/hasParams}}{{#vendorExtensions.x-pageable-extra-annotation}}{{{.}}} {{/vendorExtensions.x-pageable-extra-annotation}}{{#springDocDocumentationProvider}}@ParameterObject{{/springDocDocumentationProvider}} final Pageable pageable{{/vendorExtensions.x-spring-paginated}}{{#vendorExtensions.x-spring-provide-args}}{{#hasParams}},
- {{/hasParams}}{{^hasParams}}{{#includeHttpRequestContext}},{{/includeHttpRequestContext}}{{/hasParams}}{{#swagger2AnnotationLibrary}}@Parameter(hidden = true){{/swagger2AnnotationLibrary}} {{{.}}}{{^hasParams}}{{^-last}}{{^reactive}},{{/reactive}}
- {{/-last}}{{/hasParams}}{{/vendorExtensions.x-spring-provide-args}}
+ {{/hasParams}}{{^hasParams}}{{#includeHttpRequestContext}},{{/includeHttpRequestContext}}{{/hasParams}}{{#vendorExtensions.x-pageable-extra-annotation}}{{{.}}} {{/vendorExtensions.x-pageable-extra-annotation}}{{#springDocDocumentationProvider}}@ParameterObject{{/springDocDocumentationProvider}} final Pageable pageable{{/vendorExtensions.x-spring-paginated}}{{#vendorExtensions.x-spring-provide-args}}{{#-first}}{{#hasParams}},
+ {{/hasParams}}{{^hasParams}}{{#includeHttpRequestContext}},{{/includeHttpRequestContext}}{{^includeHttpRequestContext}}{{#vendorExtensions.x-spring-paginated}},{{/vendorExtensions.x-spring-paginated}}{{/includeHttpRequestContext}}{{/hasParams}}{{/-first}}{{#swagger2AnnotationLibrary}}@Parameter(hidden = true){{/swagger2AnnotationLibrary}} {{{.}}}{{^-last}},
+ {{/-last}}{{/vendorExtensions.x-spring-provide-args}}
){{#unhandledException}} throws Exception{{/unhandledException}}{{^jdk8-default-interface}};{{/jdk8-default-interface}}{{#jdk8-default-interface}} {
{{#delegate-method}}
- {{^isVoid}}return {{/isVoid}}{{#isVoid}}{{#useResponseEntity}}return {{/useResponseEntity}}{{^useResponseEntity}}{{#reactive}}return {{/reactive}}{{/useResponseEntity}}{{/isVoid}}{{operationId}}({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}}{{#includeHttpRequestContext}}{{#hasParams}}, {{/hasParams}}{{#reactive}}exchange{{/reactive}}{{^reactive}}servletRequest{{/reactive}}{{/includeHttpRequestContext}}{{#vendorExtensions.x-spring-paginated}}{{#hasParams}}, {{/hasParams}}{{^hasParams}}{{#reactive}}, {{/reactive}}{{/hasParams}}pageable{{/vendorExtensions.x-spring-paginated}});
+ {{^isVoid}}return {{/isVoid}}{{#isVoid}}{{#useResponseEntity}}return {{/useResponseEntity}}{{^useResponseEntity}}{{#reactive}}return {{/reactive}}{{/useResponseEntity}}{{/isVoid}}{{operationId}}({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}}{{#includeHttpRequestContext}}{{#hasParams}}, {{/hasParams}}{{#reactive}}exchange{{/reactive}}{{^reactive}}servletRequest{{/reactive}}{{/includeHttpRequestContext}}{{#vendorExtensions.x-spring-paginated}}{{#hasParams}}, {{/hasParams}}{{^hasParams}}{{#reactive}}, {{/reactive}}{{/hasParams}}pageable{{/vendorExtensions.x-spring-paginated}}{{#vendorExtensions.springProvideArgsNames}}{{#-first}}{{#hasParams}}, {{/hasParams}}{{^hasParams}}{{#includeHttpRequestContext}}, {{/includeHttpRequestContext}}{{^includeHttpRequestContext}}{{#vendorExtensions.x-spring-paginated}}, {{/vendorExtensions.x-spring-paginated}}{{/includeHttpRequestContext}}{{/hasParams}}{{/-first}}{{{.}}}{{^-last}}, {{/-last}}{{/vendorExtensions.springProvideArgsNames}});
}
// Override this method
- {{#jdk8-default-interface}}default {{/jdk8-default-interface}} {{>responseType}} {{operationId}}({{#allParams}}{{^isFile}}{{^isBodyParam}}{{>optionalDataType}}{{/isBodyParam}}{{#isBodyParam}}{{^reactive}}{{>optionalDataType}}{{/reactive}}{{#reactive}}{{^isArray}}Mono<{{{dataType}}}>{{/isArray}}{{#isArray}}Flux<{{{baseType}}}>{{/isArray}}{{/reactive}}{{/isBodyParam}}{{/isFile}}{{#isFile}}{{#reactive}}{{#isArray}}Flux<{{/isArray}}Part{{#isArray}}>{{/isArray}}{{/reactive}}{{^reactive}}{{#isArray}}List<{{/isArray}}MultipartFile{{#isArray}}>{{/isArray}}{{/reactive}}{{/isFile}} {{paramName}}{{^-last}}, {{/-last}}{{/allParams}}{{#includeHttpRequestContext}}{{#hasParams}}, {{/hasParams}} final {{#reactive}}ServerWebExchange exchange{{/reactive}}{{^reactive}}HttpServletRequest servletRequest{{/reactive}}{{/includeHttpRequestContext}}{{#vendorExtensions.x-spring-paginated}}{{#hasParams}}, {{/hasParams}}{{^hasParams}}{{#reactive}}, {{/reactive}}{{/hasParams}}final Pageable pageable{{/vendorExtensions.x-spring-paginated}}){{#unhandledException}} throws Exception{{/unhandledException}} {
+ {{#jdk8-default-interface}}default {{/jdk8-default-interface}} {{>responseType}} {{operationId}}({{#allParams}}{{^isFile}}{{^isBodyParam}}{{>optionalDataType}}{{/isBodyParam}}{{#isBodyParam}}{{^reactive}}{{>optionalDataType}}{{/reactive}}{{#reactive}}{{^isArray}}Mono<{{{dataType}}}>{{/isArray}}{{#isArray}}Flux<{{{baseType}}}>{{/isArray}}{{/reactive}}{{/isBodyParam}}{{/isFile}}{{#isFile}}{{#reactive}}{{#isArray}}Flux<{{/isArray}}Part{{#isArray}}>{{/isArray}}{{/reactive}}{{^reactive}}{{#isArray}}List<{{/isArray}}MultipartFile{{#isArray}}>{{/isArray}}{{/reactive}}{{/isFile}} {{paramName}}{{^-last}}, {{/-last}}{{/allParams}}{{#includeHttpRequestContext}}{{#hasParams}}, {{/hasParams}} final {{#reactive}}ServerWebExchange exchange{{/reactive}}{{^reactive}}HttpServletRequest servletRequest{{/reactive}}{{/includeHttpRequestContext}}{{#vendorExtensions.x-spring-paginated}}{{#hasParams}}, {{/hasParams}}{{^hasParams}}{{#reactive}}, {{/reactive}}{{/hasParams}}final Pageable pageable{{/vendorExtensions.x-spring-paginated}}{{#vendorExtensions.springProvideArgsDelegate}}{{#-first}}{{#hasParams}}, {{/hasParams}}{{^hasParams}}{{#includeHttpRequestContext}}, {{/includeHttpRequestContext}}{{^includeHttpRequestContext}}{{#vendorExtensions.x-spring-paginated}}, {{/vendorExtensions.x-spring-paginated}}{{/includeHttpRequestContext}}{{/hasParams}}{{/-first}}{{{.}}}{{^-last}}, {{/-last}}{{/vendorExtensions.springProvideArgsDelegate}}){{#unhandledException}} throws Exception{{/unhandledException}} {
{{/delegate-method}}
{{^isDelegate}}
{{>methodBody}}{{! prevent indent}}
{{/isDelegate}}
{{#isDelegate}}
- {{^isVoid}}return {{/isVoid}}{{#isVoid}}{{#useResponseEntity}}return {{/useResponseEntity}}{{^useResponseEntity}}{{#reactive}}return {{/reactive}}{{/useResponseEntity}}{{/isVoid}}getDelegate().{{operationId}}({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}}{{#includeHttpRequestContext}}{{#hasParams}}, {{/hasParams}}{{#reactive}}exchange{{/reactive}}{{^reactive}}servletRequest{{/reactive}}{{/includeHttpRequestContext}}{{#vendorExtensions.x-spring-paginated}}{{#hasParams}}, {{/hasParams}}{{^hasParams}}{{#reactive}}, {{/reactive}}{{/hasParams}}pageable{{/vendorExtensions.x-spring-paginated}});
+ {{^isVoid}}return {{/isVoid}}{{#isVoid}}{{#useResponseEntity}}return {{/useResponseEntity}}{{^useResponseEntity}}{{#reactive}}return {{/reactive}}{{/useResponseEntity}}{{/isVoid}}getDelegate().{{operationId}}({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}}{{#includeHttpRequestContext}}{{#hasParams}}, {{/hasParams}}{{#reactive}}exchange{{/reactive}}{{^reactive}}servletRequest{{/reactive}}{{/includeHttpRequestContext}}{{#vendorExtensions.x-spring-paginated}}{{#hasParams}}, {{/hasParams}}{{^hasParams}}{{#reactive}}, {{/reactive}}{{/hasParams}}pageable{{/vendorExtensions.x-spring-paginated}}{{#vendorExtensions.springProvideArgsNames}}{{#-first}}{{#hasParams}}, {{/hasParams}}{{^hasParams}}{{#includeHttpRequestContext}}, {{/includeHttpRequestContext}}{{^includeHttpRequestContext}}{{#vendorExtensions.x-spring-paginated}}, {{/vendorExtensions.x-spring-paginated}}{{/includeHttpRequestContext}}{{/hasParams}}{{/-first}}{{{.}}}{{^-last}}, {{/-last}}{{/vendorExtensions.springProvideArgsNames}});
{{/isDelegate}}
}{{/jdk8-default-interface}}
diff --git a/modules/openapi-generator/src/main/resources/JavaSpring/apiController.mustache b/modules/openapi-generator/src/main/resources/JavaSpring/apiController.mustache
index 7de0ac763bcf..17ea2c148d5e 100644
--- a/modules/openapi-generator/src/main/resources/JavaSpring/apiController.mustache
+++ b/modules/openapi-generator/src/main/resources/JavaSpring/apiController.mustache
@@ -123,7 +123,10 @@ public class {{classname}}Controller implements {{classname}} {
public {{#responseWrapper}}{{.}}<{{/responseWrapper}}{{#useResponseEntity}}ResponseEntity<{{/useResponseEntity}}{{>returnTypes}}{{#useResponseEntity}}>{{/useResponseEntity}}{{#responseWrapper}}>{{/responseWrapper}} {{operationId}}(
{{#allParams}}{{>queryParams}}{{>pathParams}}{{>headerParams}}{{>bodyParams}}{{>formParams}}{{>cookieParams}}{{^-last}},
{{/-last}}{{/allParams}}{{#vendorExtensions.x-spring-paginated}}{{#hasParams}},
- {{/hasParams}}{{^hasParams}}{{#reactive}},{{/reactive}}{{/hasParams}}{{#springDocDocumentationProvider}}@ParameterObject {{/springDocDocumentationProvider}}final Pageable pageable{{/vendorExtensions.x-spring-paginated}}
+ {{/hasParams}}{{^hasParams}}{{#reactive}},{{/reactive}}{{/hasParams}}{{#springDocDocumentationProvider}}@ParameterObject {{/springDocDocumentationProvider}}final Pageable pageable{{/vendorExtensions.x-spring-paginated}}{{#vendorExtensions.x-spring-provide-args}}{{#-first}}{{#hasParams}},
+ {{/hasParams}}{{^hasParams}}{{#vendorExtensions.x-spring-paginated}},
+ {{/vendorExtensions.x-spring-paginated}}{{/hasParams}}{{/-first}}{{#swagger2AnnotationLibrary}}@Parameter(hidden = true){{/swagger2AnnotationLibrary}} {{{.}}}{{^-last}},
+ {{/-last}}{{/vendorExtensions.x-spring-provide-args}}
) {
{{^isDelegate}}
{{^async}}
@@ -139,7 +142,7 @@ public class {{classname}}Controller implements {{classname}} {
{{/async}}
{{/isDelegate}}
{{#isDelegate}}
- {{^isVoid}}return {{/isVoid}}{{#isVoid}}{{#useResponseEntity}}return {{/useResponseEntity}}{{^useResponseEntity}}{{#reactive}}return {{/reactive}}{{/useResponseEntity}}{{/isVoid}}delegate.{{operationId}}({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}}{{#vendorExtensions.x-spring-paginated}}{{#hasParams}}, {{/hasParams}}{{^hasParams}}{{#reactive}}, {{/reactive}}{{/hasParams}}pageable{{/vendorExtensions.x-spring-paginated}});
+ {{^isVoid}}return {{/isVoid}}{{#isVoid}}{{#useResponseEntity}}return {{/useResponseEntity}}{{^useResponseEntity}}{{#reactive}}return {{/reactive}}{{/useResponseEntity}}{{/isVoid}}delegate.{{operationId}}({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}}{{#vendorExtensions.x-spring-paginated}}{{#hasParams}}, {{/hasParams}}{{^hasParams}}{{#reactive}}, {{/reactive}}{{/hasParams}}pageable{{/vendorExtensions.x-spring-paginated}}{{#vendorExtensions.springProvideArgsNames}}{{#-first}}{{#hasParams}}, {{/hasParams}}{{^hasParams}}{{#vendorExtensions.x-spring-paginated}}, {{/vendorExtensions.x-spring-paginated}}{{/hasParams}}{{/-first}}{{{.}}}{{^-last}}, {{/-last}}{{/vendorExtensions.springProvideArgsNames}});
{{/isDelegate}}
}
diff --git a/modules/openapi-generator/src/main/resources/JavaSpring/apiDelegate.mustache b/modules/openapi-generator/src/main/resources/JavaSpring/apiDelegate.mustache
index 12ed158dedb5..20a9560d2dce 100644
--- a/modules/openapi-generator/src/main/resources/JavaSpring/apiDelegate.mustache
+++ b/modules/openapi-generator/src/main/resources/JavaSpring/apiDelegate.mustache
@@ -79,7 +79,7 @@ public interface {{classname}}Delegate {
{{/isDeprecated}}
{{#jdk8-default-interface}}default {{/jdk8-default-interface}}{{>responseType}} {{operationId}}({{#allParams}}{{^isFile}}{{^isBodyParam}}{{>optionalDataType}}{{/isBodyParam}}{{#isBodyParam}}{{^reactive}}{{>optionalDataType}}{{/reactive}}{{#reactive}}{{^isArray}}Mono<{{{dataType}}}>{{/isArray}}{{#isArray}}Flux<{{{baseType}}}>{{/isArray}}{{/reactive}}{{/isBodyParam}}{{/isFile}}{{#isFile}}{{#reactive}}{{#isArray}}Flux<{{/isArray}}Part{{#isArray}}>{{/isArray}}{{/reactive}}{{^reactive}}{{#isArray}}List<{{/isArray}}{{#isFormParam}}MultipartFile{{/isFormParam}}{{^isFormParam}}{{>optionalDataType}}{{/isFormParam}}{{#isArray}}>{{/isArray}}{{/reactive}}{{/isFile}} {{paramName}}{{^-last}},
{{/-last}}{{/allParams}}{{#includeHttpRequestContext}}{{#hasParams}},
- {{/hasParams}}{{#reactive}}ServerWebExchange exchange{{/reactive}}{{^reactive}}HttpServletRequest servletRequest{{/reactive}}{{/includeHttpRequestContext}}{{#vendorExtensions.x-spring-paginated}}{{#hasParams}}, {{/hasParams}}{{^hasParams}}{{#includeHttpRequestContext}}, {{/includeHttpRequestContext}}{{/hasParams}}final Pageable pageable{{/vendorExtensions.x-spring-paginated}}){{#unhandledException}} throws Exception{{/unhandledException}}{{^jdk8-default-interface}};{{/jdk8-default-interface}}{{#jdk8-default-interface}} {
+ {{/hasParams}}{{#reactive}}ServerWebExchange exchange{{/reactive}}{{^reactive}}HttpServletRequest servletRequest{{/reactive}}{{/includeHttpRequestContext}}{{#vendorExtensions.x-spring-paginated}}{{#hasParams}}, {{/hasParams}}{{^hasParams}}{{#includeHttpRequestContext}}, {{/includeHttpRequestContext}}{{/hasParams}}final Pageable pageable{{/vendorExtensions.x-spring-paginated}}{{#vendorExtensions.springProvideArgsDelegate}}{{#-first}}{{#hasParams}}, {{/hasParams}}{{^hasParams}}{{#includeHttpRequestContext}}, {{/includeHttpRequestContext}}{{^includeHttpRequestContext}}{{#vendorExtensions.x-spring-paginated}}, {{/vendorExtensions.x-spring-paginated}}{{/includeHttpRequestContext}}{{/hasParams}}{{/-first}}{{{.}}}{{^-last}}, {{/-last}}{{/vendorExtensions.springProvideArgsDelegate}}){{#unhandledException}} throws Exception{{/unhandledException}}{{^jdk8-default-interface}};{{/jdk8-default-interface}}{{#jdk8-default-interface}} {
{{>methodBody}}{{! prevent indent}}
}{{/jdk8-default-interface}}
diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java
index e7a4f02f7f0b..9a8377309121 100644
--- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java
+++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java
@@ -38,6 +38,8 @@
import org.openapitools.codegen.languages.features.BeanValidationFeatures;
import org.openapitools.codegen.languages.features.CXFServerFeatures;
import org.openapitools.codegen.languages.features.DocumentationProviderFeatures;
+import org.openapitools.codegen.model.ModelMap;
+import org.openapitools.codegen.model.OperationsMap;
import org.openapitools.codegen.testutils.ConfigAssert;
import org.testng.Assert;
import org.testng.annotations.DataProvider;
@@ -8147,6 +8149,155 @@ void schemaMappingWithNullableAllOfRendersNullableJavaProperty() throws IOExcept
}
@Test
+ public void shouldPassXSpringProvideArgsToOverridableMethodWithApiInterfaceRequestMapping() throws IOException {
+ File output = Files.createTempDirectory("test").toFile().getCanonicalFile();
+ output.deleteOnExit();
+ String outputPath = output.getAbsolutePath().replace('\\', '/');
+
+ final OpenAPI openAPI = TestUtils.parseFlattenSpec(
+ "src/test/resources/3_0/spring/x-spring-provide-args-api-interface.yaml");
+ final SpringCodegen codegen = new SpringCodegen();
+ codegen.setOpenAPI(openAPI);
+ codegen.setLibrary(SPRING_BOOT);
+ codegen.setOutputDir(output.getAbsolutePath());
+ codegen.additionalProperties().put(INTERFACE_ONLY, "true");
+ codegen.additionalProperties().put(DELEGATE_PATTERN, "true");
+ codegen.additionalProperties().put(REQUEST_MAPPING_OPTION, SpringCodegen.RequestMappingMode.api_interface.name());
+
+ ClientOptInput input = new ClientOptInput();
+ input.openAPI(openAPI);
+ input.config(codegen);
+
+ DefaultGenerator generator = new DefaultGenerator();
+ generator.setGenerateMetadata(false);
+ generator.setGeneratorPropertyDefault(CodegenConstants.MODEL_TESTS, "false");
+ generator.setGeneratorPropertyDefault(CodegenConstants.MODEL_DOCS, "false");
+ generator.setGeneratorPropertyDefault(CodegenConstants.APIS, "true");
+
+ generator.opts(input).generate();
+
+ JavaFileAssert.assertThat(Paths.get(outputPath + "/src/main/java/org/openapitools/api/FooApi.java"))
+ .fileContains("default ResponseEntity _foo(")
+ .fileContains("@Parameter(hidden = true) @Size(max = 64) String providedArg")
+ .fileContains("return foo(providedArg);")
+ .fileContains("default ResponseEntity foo(String providedArg)");
+ }
+
+ @Test
+ public void shouldIncludeXSpringProvideArgsInDelegateWithApiInterfaceRequestMapping() throws IOException {
+ File output = Files.createTempDirectory("test").toFile().getCanonicalFile();
+ output.deleteOnExit();
+ String outputPath = output.getAbsolutePath().replace('\\', '/');
+
+ final OpenAPI openAPI = TestUtils.parseFlattenSpec(
+ "src/test/resources/3_0/spring/x-spring-provide-args-api-interface.yaml");
+ final SpringCodegen codegen = new SpringCodegen();
+ codegen.setOpenAPI(openAPI);
+ codegen.setLibrary(SPRING_BOOT);
+ codegen.setOutputDir(output.getAbsolutePath());
+ codegen.additionalProperties().put(DELEGATE_PATTERN, "true");
+ codegen.additionalProperties().put(REQUEST_MAPPING_OPTION, SpringCodegen.RequestMappingMode.api_interface.name());
+
+ ClientOptInput input = new ClientOptInput();
+ input.openAPI(openAPI);
+ input.config(codegen);
+
+ DefaultGenerator generator = new DefaultGenerator();
+ generator.setGenerateMetadata(false);
+ generator.setGeneratorPropertyDefault(CodegenConstants.MODEL_TESTS, "false");
+ generator.setGeneratorPropertyDefault(CodegenConstants.MODEL_DOCS, "false");
+ generator.setGeneratorPropertyDefault(CodegenConstants.APIS, "true");
+
+ generator.opts(input).generate();
+
+ JavaFileAssert.assertThat(Paths.get(outputPath + "/src/main/java/org/openapitools/api/FooApi.java"))
+ .fileContains("@Parameter(hidden = true) @Size(max = 64) String providedArg")
+ .fileContains("return getDelegate().foo(providedArg);");
+ JavaFileAssert.assertThat(Paths.get(outputPath + "/src/main/java/org/openapitools/api/FooApiDelegate.java"))
+ .fileContains("default ResponseEntity foo(String providedArg)");
+ }
+
+ @Test
+ public void shouldPassXSpringProvideArgsFromControllerToDelegate() throws IOException {
+ File output = Files.createTempDirectory("test").toFile().getCanonicalFile();
+ output.deleteOnExit();
+ String outputPath = output.getAbsolutePath().replace('\\', '/');
+
+ final OpenAPI openAPI = TestUtils.parseFlattenSpec(
+ "src/test/resources/3_0/spring/x-spring-provide-args-api-interface.yaml");
+ final SpringCodegen codegen = new SpringCodegen() {
+ @Override
+ public void processOpts() {
+ super.processOpts();
+ additionalProperties().put("_api_controller_impl_", true);
+ }
+
+ @Override
+ public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List allModels) {
+ OperationsMap operations = super.postProcessOperationsWithModels(objs, allModels);
+ operations.put("_api_controller_impl_", true);
+ return operations;
+ }
+ };
+ codegen.setOpenAPI(openAPI);
+ codegen.setLibrary(SPRING_BOOT);
+ codegen.setOutputDir(output.getAbsolutePath());
+ codegen.additionalProperties().put(DELEGATE_PATTERN, "true");
+
+ ClientOptInput input = new ClientOptInput();
+ input.openAPI(openAPI);
+ input.config(codegen);
+
+ DefaultGenerator generator = new DefaultGenerator();
+ generator.setGenerateMetadata(false);
+ generator.setGeneratorPropertyDefault(CodegenConstants.MODEL_TESTS, "false");
+ generator.setGeneratorPropertyDefault(CodegenConstants.MODEL_DOCS, "false");
+ generator.setGeneratorPropertyDefault(CodegenConstants.APIS, "true");
+
+ generator.opts(input).generate();
+
+ JavaFileAssert.assertThat(Paths.get(outputPath + "/src/main/java/org/openapitools/api/FooApiController.java"))
+ .fileContains("@Parameter(hidden = true) @Size(max = 64) String providedArg")
+ .fileContains("return delegate.foo(providedArg);");
+ JavaFileAssert.assertThat(Paths.get(outputPath + "/src/main/java/org/openapitools/api/FooApiDelegate.java"))
+ .fileContains("default ResponseEntity foo(String providedArg)");
+ }
+
+ @Test
+ public void shouldIncludeXSpringProvideArgsWithInterfaceOnlyWithoutDelegatePattern() throws IOException {
+ File output = Files.createTempDirectory("test").toFile().getCanonicalFile();
+ output.deleteOnExit();
+ String outputPath = output.getAbsolutePath().replace('\\', '/');
+
+ final OpenAPI openAPI = TestUtils.parseFlattenSpec(
+ "src/test/resources/3_0/spring/x-spring-provide-args-api-interface.yaml");
+ final SpringCodegen codegen = new SpringCodegen();
+ codegen.setOpenAPI(openAPI);
+ codegen.setLibrary(SPRING_BOOT);
+ codegen.setOutputDir(output.getAbsolutePath());
+ codegen.additionalProperties().put(INTERFACE_ONLY, "true");
+ codegen.additionalProperties().put(DELEGATE_PATTERN, "false");
+ codegen.additionalProperties().put(REQUEST_MAPPING_OPTION, SpringCodegen.RequestMappingMode.api_interface.name());
+
+ ClientOptInput input = new ClientOptInput();
+ input.openAPI(openAPI);
+ input.config(codegen);
+
+ DefaultGenerator generator = new DefaultGenerator();
+ generator.setGenerateMetadata(false);
+ generator.setGeneratorPropertyDefault(CodegenConstants.MODEL_TESTS, "false");
+ generator.setGeneratorPropertyDefault(CodegenConstants.MODEL_DOCS, "false");
+ generator.setGeneratorPropertyDefault(CodegenConstants.APIS, "true");
+
+ generator.opts(input).generate();
+
+ JavaFileAssert.assertThat(Paths.get(outputPath + "/src/main/java/org/openapitools/api/FooApi.java"))
+ .fileContains("default ResponseEntity foo(")
+ .fileContains("@Parameter(hidden = true) @Size(max = 64) String providedArg")
+ .fileDoesNotContain("default ResponseEntity _foo(")
+ .fileDoesNotContain("return foo(providedArg);");
+ }
+
void issue24003() throws IOException {
Map files = generateFromContract(
"src/test/resources/3_0/spring/issue_24003.yaml", SPRING_BOOT,
@@ -8162,4 +8313,4 @@ void issue24003() throws IOException {
JavaFileAssert.assertThat(files.get("UserBrLockDTO.java")).implementsInterfaces("BrLockDTO")
.fileDoesNotContain("@JsonTypeName");
}
-}
\ No newline at end of file
+}
diff --git a/modules/openapi-generator/src/test/resources/3_0/spring/x-spring-provide-args-api-interface.yaml b/modules/openapi-generator/src/test/resources/3_0/spring/x-spring-provide-args-api-interface.yaml
new file mode 100644
index 000000000000..c59210f90e70
--- /dev/null
+++ b/modules/openapi-generator/src/test/resources/3_0/spring/x-spring-provide-args-api-interface.yaml
@@ -0,0 +1,15 @@
+openapi: 3.0.3
+info:
+ title: x-spring-provide-args api interface test
+ version: 1.0.0
+paths:
+ /foo:
+ get:
+ tags:
+ - foo
+ operationId: foo
+ x-spring-provide-args:
+ - "@jakarta.validation.constraints.Size(max = 64) String providedArg"
+ responses:
+ "204":
+ description: no content