diff --git a/springdoc-openapi-common/src/main/java/org/springdoc/core/AbstractRequestService.java b/springdoc-openapi-common/src/main/java/org/springdoc/core/AbstractRequestService.java index a520e0cca..0f45b4321 100644 --- a/springdoc-openapi-common/src/main/java/org/springdoc/core/AbstractRequestService.java +++ b/springdoc-openapi-common/src/main/java/org/springdoc/core/AbstractRequestService.java @@ -292,7 +292,7 @@ else if (!RequestMethod.GET.equals(requestMethod)) { applyBeanValidatorAnnotations(requestBodyInfo.getRequestBody(), parameterAnnotations, methodParameter.isOptional()); } - customiseParameter(parameter, parameterInfo); + GenericParameterService.replaceParameter(operationParameters, parameter, customiseParameter(parameter, parameterInfo)); } } @@ -366,9 +366,16 @@ public static Collection getHeaders(MethodAttributes methodAttributes * @param parameterInfo the parameter info * @return the parameter */ - protected Parameter customiseParameter(Parameter parameter, ParameterInfo parameterInfo) { - parameterCustomizers.ifPresent(customizers -> customizers.forEach(customizer -> customizer.customize(parameter, parameterInfo.getMethodParameter()))); - return parameter; + protected Parameter customiseParameter(final Parameter parameter, final ParameterInfo parameterInfo) { + return parameterCustomizers + .map(customizers -> { + Parameter customizedParameter = parameter; + for (final ParameterCustomizer customizer : customizers) { + customizedParameter = customizer.customize(customizedParameter, parameterInfo.getMethodParameter()); + } + return customizedParameter; + }) + .orElse(parameter); } /** diff --git a/springdoc-openapi-common/src/main/java/org/springdoc/core/GenericParameterService.java b/springdoc-openapi-common/src/main/java/org/springdoc/core/GenericParameterService.java index 8bd924a89..ddf30a0aa 100644 --- a/springdoc-openapi-common/src/main/java/org/springdoc/core/GenericParameterService.java +++ b/springdoc-openapi-common/src/main/java/org/springdoc/core/GenericParameterService.java @@ -140,6 +140,32 @@ public static boolean isFile(Class type) { return FILE_TYPES.stream().anyMatch(clazz -> clazz.isAssignableFrom(type)); } + /** + * Replace existingParamDoc. The argument named asis will be replaced to the argument named tobe. + * @param existingParamDoc the existing param doc + * @param asis the parameter. If null, the do nothing. otherwise, replace to tobe. + * @param tobe the parameter. If null, just remove asis. + */ + public static void replaceParameter(List existingParamDoc, Parameter asis, Parameter tobe) { + int index = -1; + if (asis != null) { + index = existingParamDoc.stream() + .filter(param -> asis.getName().equals(param.getName())) + .findFirst() + .map(existingParamDoc::indexOf) + .orElse(-1); + } + if (index == -1) { + return; + } + + if (tobe == null) { + existingParamDoc.remove(index); + } else { + existingParamDoc.set(index, tobe); + } + } + /** * Merge parameter parameter. * diff --git a/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/app172/HelloController.java b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/app172/HelloController.java new file mode 100644 index 000000000..a32f71b2b --- /dev/null +++ b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/app172/HelloController.java @@ -0,0 +1,15 @@ +package test.org.springdoc.api.app172; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +import test.org.springdoc.api.app172.annotation.MyIdPathVariable; +import test.org.springdoc.api.app172.model.MyObj; + +@RestController +public class HelloController { + @GetMapping("/test/{objId}") + String test(@MyIdPathVariable MyObj obj) { + return obj.getContent(); + } +} diff --git a/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/app172/MyConfiguration.java b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/app172/MyConfiguration.java new file mode 100644 index 000000000..868724a60 --- /dev/null +++ b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/app172/MyConfiguration.java @@ -0,0 +1,15 @@ +package test.org.springdoc.api.app172; + +import java.util.List; + +import org.springframework.context.annotation.Configuration; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +public class MyConfiguration implements WebMvcConfigurer { + @Override + public void addArgumentResolvers(List resolvers) { + resolvers.add(new MyObjArgumentResolver()); + } +} diff --git a/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/app172/MyObjArgumentResolver.java b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/app172/MyObjArgumentResolver.java new file mode 100644 index 000000000..6ee571690 --- /dev/null +++ b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/app172/MyObjArgumentResolver.java @@ -0,0 +1,25 @@ +package test.org.springdoc.api.app172; + +import org.springframework.core.MethodParameter; +import org.springframework.web.bind.support.WebDataBinderFactory; +import org.springframework.web.context.request.NativeWebRequest; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.method.support.ModelAndViewContainer; + +import test.org.springdoc.api.app172.annotation.MyIdPathVariable; +import test.org.springdoc.api.app172.model.MyObj; + +public class MyObjArgumentResolver implements HandlerMethodArgumentResolver { + + @Override + public boolean supportsParameter(MethodParameter parameter) { + return parameter.hasParameterAnnotation(MyIdPathVariable.class) && + MyObj.class.isAssignableFrom(parameter.getParameterType()); + } + + @Override + public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, + NativeWebRequest webRequest, WebDataBinderFactory binderFactory) { + return new MyObj("id", "content"); + } +} diff --git a/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/app172/SpringDocApp172Test.java b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/app172/SpringDocApp172Test.java new file mode 100644 index 000000000..4d4f98fef --- /dev/null +++ b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/app172/SpringDocApp172Test.java @@ -0,0 +1,11 @@ +package test.org.springdoc.api.app172; + +import org.springframework.boot.autoconfigure.SpringBootApplication; + +import test.org.springdoc.api.AbstractSpringDocTest; + +public class SpringDocApp172Test extends AbstractSpringDocTest { + + @SpringBootApplication + static class SpringDocTestApp {} +} diff --git a/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/app172/annotation/MyIdPathVariable.java b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/app172/annotation/MyIdPathVariable.java new file mode 100644 index 000000000..e86283be9 --- /dev/null +++ b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/app172/annotation/MyIdPathVariable.java @@ -0,0 +1,11 @@ +package test.org.springdoc.api.app172.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.PARAMETER) +public @interface MyIdPathVariable { +} diff --git a/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/app172/customizer/MyPathParameterCustomizer.java b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/app172/customizer/MyPathParameterCustomizer.java new file mode 100644 index 000000000..635c64a4d --- /dev/null +++ b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/app172/customizer/MyPathParameterCustomizer.java @@ -0,0 +1,24 @@ +package test.org.springdoc.api.app172.customizer; + +import org.springdoc.core.customizers.ParameterCustomizer; +import org.springframework.core.MethodParameter; +import org.springframework.stereotype.Component; + +import io.swagger.v3.oas.models.media.StringSchema; +import io.swagger.v3.oas.models.parameters.Parameter; +import io.swagger.v3.oas.models.parameters.PathParameter; +import test.org.springdoc.api.app172.annotation.MyIdPathVariable; + +@Component +public class MyPathParameterCustomizer implements ParameterCustomizer { + @Override + public Parameter customize(Parameter parameterModel, MethodParameter methodParameter) { + if (methodParameter.hasParameterAnnotation(MyIdPathVariable.class)) { + Parameter alternativeParameter = new PathParameter(); + alternativeParameter.setName("objId"); + alternativeParameter.setSchema(new StringSchema()); + return alternativeParameter; + } + return parameterModel; + } +} diff --git a/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/app172/model/MyObj.java b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/app172/model/MyObj.java new file mode 100644 index 000000000..19a4c8683 --- /dev/null +++ b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/app172/model/MyObj.java @@ -0,0 +1,19 @@ +package test.org.springdoc.api.app172.model; + +public class MyObj { + private final String id; + private final String content; + + public MyObj(String id, String content) { + this.id = id; + this.content = content; + } + + public String getId() { + return id; + } + + public String getContent() { + return content; + } +} diff --git a/springdoc-openapi-webmvc-core/src/test/resources/results/app172.json b/springdoc-openapi-webmvc-core/src/test/resources/results/app172.json new file mode 100644 index 000000000..99061b9c1 --- /dev/null +++ b/springdoc-openapi-webmvc-core/src/test/resources/results/app172.json @@ -0,0 +1,48 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "OpenAPI definition", + "version": "v0" + }, + "servers": [ + { + "url": "http://localhost", + "description": "Generated server url" + } + ], + "paths": { + "/test/{objId}": { + "get": { + "tags": [ + "hello-controller" + ], + "operationId": "test", + "parameters": [ + { + "name": "objId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "type": "string" + } + } + } + } + } + } + } + }, + "components": { + "schemas": {} + } +}