diff --git a/springdoc-openapi-common/src/main/java/org/springdoc/api/AbstractOpenApiResource.java b/springdoc-openapi-common/src/main/java/org/springdoc/api/AbstractOpenApiResource.java index 668695d35..a72628e99 100644 --- a/springdoc-openapi-common/src/main/java/org/springdoc/api/AbstractOpenApiResource.java +++ b/springdoc-openapi-common/src/main/java/org/springdoc/api/AbstractOpenApiResource.java @@ -84,6 +84,7 @@ import org.springdoc.core.customizers.OpenApiCustomiser; import org.springdoc.core.customizers.OpenApiLocaleCustomizer; import org.springdoc.core.customizers.OperationCustomizer; +import org.springdoc.core.customizers.RouterOperationCustomizer; import org.springdoc.core.filters.OpenApiMethodFilter; import org.springdoc.core.fn.AbstractRouterFunctionVisitor; import org.springdoc.core.fn.RouterFunctionData; @@ -119,6 +120,7 @@ * The type Abstract open api resource. * @author bnasslahsen * @author kevinraddatz + * @author hyeonisism */ public abstract class AbstractOpenApiResource extends SpecFilter { @@ -177,6 +179,11 @@ public abstract class AbstractOpenApiResource extends SpecFilter { */ private final Optional> operationCustomizers; + /** + * The RouterOperation customizers. + */ + private final Optional> routerOperationCustomizers; + /** * The method filters to use. */ @@ -216,6 +223,7 @@ public abstract class AbstractOpenApiResource extends SpecFilter { * @param operationParser the operation parser * @param operationCustomizers the operation customizers * @param openApiCustomisers the open api customisers + * @param routerOperationCustomizers the router operation customisers * @param methodFilters the method filters * @param springDocConfigProperties the spring doc config properties * @param springDocProviders the spring doc providers @@ -225,6 +233,7 @@ protected AbstractOpenApiResource(String groupName, ObjectFactory> operationCustomizers, Optional> openApiCustomisers, + Optional> routerOperationCustomizers, Optional> methodFilters, SpringDocConfigProperties springDocConfigProperties, SpringDocProviders springDocProviders) { super(); @@ -235,6 +244,7 @@ protected AbstractOpenApiResource(String groupName, ObjectFactory requestMethods = new HashSet<>(Arrays.asList(routerOperation.getMethods())); io.swagger.v3.oas.annotations.Operation apiOperation = routerOperation.getOperation(); @@ -608,12 +621,15 @@ protected void calculatePath(RouterOperation routerOperation, Locale locale, Ope * @param consumes the consumes * @param produces the produces * @param headers the headers + * @param params the params * @param locale the locale * @param openAPI the open api */ protected void calculatePath(HandlerMethod handlerMethod, String operationPath, - Set requestMethods, String[] consumes, String[] produces, String[] headers, Locale locale, OpenAPI openAPI) { - this.calculatePath(handlerMethod, new RouterOperation(operationPath, requestMethods.toArray(new RequestMethod[requestMethods.size()]), consumes, produces, headers), locale, openAPI); + Set requestMethods, String[] consumes, String[] produces, String[] headers, String[] params, Locale locale, OpenAPI openAPI) { + this.calculatePath(handlerMethod, + new RouterOperation(operationPath, requestMethods.toArray(new RequestMethod[requestMethods.size()]), consumes, produces, headers, params), + locale, openAPI); } /** @@ -794,7 +810,6 @@ public static boolean containsResponseBody(HandlerMethod handlerMethod) { return responseBodyAnnotation != null; } - /** * Is rest controller boolean. * @@ -849,6 +864,22 @@ protected Operation customiseOperation(Operation operation, HandlerMethod handle return operation; } + /** + * Customise router operation + * @param routerOperation + * @param handlerMethod + * @return the router operation + */ + protected RouterOperation customiseRouterOperation(RouterOperation routerOperation, HandlerMethod handlerMethod) { + if (routerOperationCustomizers.isPresent()) { + List routerOperationCustomizerList = routerOperationCustomizers.get(); + for (RouterOperationCustomizer routerOperationCustomizer : routerOperationCustomizerList) { + routerOperation = routerOperationCustomizer.customize(routerOperation, handlerMethod); + } + } + return routerOperation; + } + /** * Merge routers. * diff --git a/springdoc-openapi-common/src/main/java/org/springdoc/core/GroupedOpenApi.java b/springdoc-openapi-common/src/main/java/org/springdoc/core/GroupedOpenApi.java index c04adae14..a9980085e 100644 --- a/springdoc-openapi-common/src/main/java/org/springdoc/core/GroupedOpenApi.java +++ b/springdoc-openapi-common/src/main/java/org/springdoc/core/GroupedOpenApi.java @@ -31,6 +31,7 @@ import org.apache.commons.lang3.StringUtils; import org.springdoc.core.customizers.OpenApiCustomiser; import org.springdoc.core.customizers.OperationCustomizer; +import org.springdoc.core.customizers.RouterOperationCustomizer; import org.springdoc.core.filters.OpenApiMethodFilter; import org.springframework.util.CollectionUtils; @@ -58,6 +59,11 @@ public class GroupedOpenApi { */ private List operationCustomizers; + /** + * The Router Operation customizers. + */ + private List routerOperationCustomizers; + /** * The Paths to match. */ @@ -120,6 +126,7 @@ private GroupedOpenApi(Builder builder) { this.displayName = StringUtils.defaultIfEmpty(builder.displayName, this.group); this.openApiCustomisers = Objects.requireNonNull(builder.openApiCustomisers); this.operationCustomizers = Objects.requireNonNull(builder.operationCustomizers); + this.routerOperationCustomizers = Objects.requireNonNull(builder.routerOperationCustomizers); this.openApiMethodFilters = Objects.requireNonNull(builder.methodFilters); if (CollectionUtils.isEmpty(this.pathsToMatch) && CollectionUtils.isEmpty(this.packagesToScan) @@ -242,6 +249,15 @@ public List getOpenApiMethodFilters() { return openApiMethodFilters; } + /** + * Gets router operation customizers. + * + * @return the router operation customizers + */ + public List getRouterOperationCustomizers() { + return routerOperationCustomizers; + } + /** * Gets display name. * @@ -266,6 +282,11 @@ public static class Builder { */ private final List operationCustomizers = new ArrayList<>(); + /** + * The Router Operation customizers. + */ + private final List routerOperationCustomizers = new ArrayList<>(); + /** * The methods filters to apply. */ @@ -433,6 +454,17 @@ public Builder addOperationCustomizer(OperationCustomizer operationCustomizer) { return this; } + /** + * Add router operation customizer builder + * + * @param routerOperationCustomizer the router operation customizer + * @return the builder + */ + public Builder addRouterOperationCustomizer(RouterOperationCustomizer routerOperationCustomizer) { + this.routerOperationCustomizers.add(routerOperationCustomizer); + return this; + } + /** * Add method filter. * diff --git a/springdoc-openapi-common/src/main/java/org/springdoc/core/annotations/RouterOperation.java b/springdoc-openapi-common/src/main/java/org/springdoc/core/annotations/RouterOperation.java index 1de7f19d8..d8984ecef 100644 --- a/springdoc-openapi-common/src/main/java/org/springdoc/core/annotations/RouterOperation.java +++ b/springdoc-openapi-common/src/main/java/org/springdoc/core/annotations/RouterOperation.java @@ -39,7 +39,7 @@ * * @author bnasslahsen */ -@Target({ ElementType.TYPE, ElementType.METHOD}) +@Target({ ElementType.TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Inherited public @interface RouterOperation { @@ -95,6 +95,14 @@ */ String[] headers() default {}; + /** + * The parameters of the mapped request, narrowing the primary mapping. + * Same format for any environment: a sequence of "myParam=myValue" style expressions, + * with a request only mapped if each such parameter is found to have the given value. + * @return the string [ ] + */ + String[] params() default {}; + /** * The class of the Handler bean. * @return the class of the Bean diff --git a/springdoc-openapi-common/src/main/java/org/springdoc/core/customizers/RouterOperationCustomizer.java b/springdoc-openapi-common/src/main/java/org/springdoc/core/customizers/RouterOperationCustomizer.java new file mode 100644 index 000000000..2bf93dc60 --- /dev/null +++ b/springdoc-openapi-common/src/main/java/org/springdoc/core/customizers/RouterOperationCustomizer.java @@ -0,0 +1,25 @@ +package org.springdoc.core.customizers; + +import org.springdoc.core.fn.RouterOperation; + +import org.springframework.web.method.HandlerMethod; + +/** + * Implement and register a bean of type {@link RouterOperationCustomizer} to customize an router operation + * based on the handler method input on default OpenAPI descriptions but not groups + * + * @author hyeonisism + */ +@FunctionalInterface +public interface RouterOperationCustomizer { + + /** + * Customize router operation. + * + * @param routerOperation input operation + * @param handlerMethod original handler method + * @return customized router operation + */ + RouterOperation customize(RouterOperation routerOperation, HandlerMethod handlerMethod); + +} diff --git a/springdoc-openapi-common/src/main/java/org/springdoc/core/fn/RouterFunctionData.java b/springdoc-openapi-common/src/main/java/org/springdoc/core/fn/RouterFunctionData.java index 5c4c18b38..52aa744cf 100644 --- a/springdoc-openapi-common/src/main/java/org/springdoc/core/fn/RouterFunctionData.java +++ b/springdoc-openapi-common/src/main/java/org/springdoc/core/fn/RouterFunctionData.java @@ -62,6 +62,11 @@ public class RouterFunctionData { */ private List headers = new ArrayList<>(); + /** + * The Params. + */ + private List params = new ArrayList<>(); + /** * The Query params. */ @@ -93,6 +98,7 @@ public RouterFunctionData(String nestedOrPath, RouterFunctionData functionData) this.consumes = Arrays.asList(functionData.getConsumes()); this.produces = Arrays.asList(functionData.getProduces()); this.headers = Arrays.asList(functionData.getHeaders()); + this.params = Arrays.asList(functionData.getParams()); this.queryParams = functionData.getQueryParams(); this.methods = functionData.getMethods(); this.attributes = functionData.getAttributes(); @@ -181,6 +187,20 @@ public String[] getConsumes() { return consumes.toArray(new String[consumes.size()]); } + /** + * Get params string [ ]. + * + * @return the string [ ] + */ + public String[] getParams() { return params.toArray(new String[params.size()]); } + + /** + * Add params. + * + * @param params the params + */ + public void addParams(String params) { if(StringUtils.isNotBlank(params)) this.params.add(params); } + /** * Add consumes. * diff --git a/springdoc-openapi-common/src/main/java/org/springdoc/core/fn/RouterOperation.java b/springdoc-openapi-common/src/main/java/org/springdoc/core/fn/RouterOperation.java index bd8149663..4fa8e314f 100644 --- a/springdoc-openapi-common/src/main/java/org/springdoc/core/fn/RouterOperation.java +++ b/springdoc-openapi-common/src/main/java/org/springdoc/core/fn/RouterOperation.java @@ -38,6 +38,7 @@ /** * The type Router operation. * @author bnasslahsen + * @author hyeonisism */ public class RouterOperation implements Comparable { @@ -66,6 +67,11 @@ public class RouterOperation implements Comparable { */ private String[] headers; + /** + * The Params. + */ + private String[] params; + /** * The Bean class. */ @@ -116,6 +122,7 @@ public RouterOperation(org.springdoc.core.annotations.RouterOperation routerOper this.parameterTypes = routerOperationAnnotation.parameterTypes(); this.operation = routerOperationAnnotation.operation(); this.headers = routerOperationAnnotation.headers(); + this.params = routerOperationAnnotation.params(); } /** @@ -134,6 +141,7 @@ public RouterOperation(org.springdoc.core.annotations.RouterOperation routerOper this.parameterTypes = routerOperationAnnotation.parameterTypes(); this.operation = routerOperationAnnotation.operation(); this.headers = ArrayUtils.isEmpty(routerOperationAnnotation.headers()) ? routerFunctionData.getHeaders() : routerOperationAnnotation.headers(); + this.params = routerOperationAnnotation.params(); this.queryParams = routerFunctionData.getQueryParams(); } @@ -146,12 +154,13 @@ public RouterOperation(org.springdoc.core.annotations.RouterOperation routerOper * @param produces the produces * @param headers the headers */ - public RouterOperation(String path, RequestMethod[] methods,String[] consumes, String[] produces, String[] headers) { + public RouterOperation(String path, RequestMethod[] methods, String[] consumes, String[] produces, String[] headers, String[] params) { this.path = path; this.methods = methods; - this.consumes=consumes; - this.produces=produces; - this.headers=headers; + this.consumes = consumes; + this.produces = produces; + this.headers = headers; + this.params = params; } /** @@ -165,6 +174,7 @@ public RouterOperation(RouterFunctionData routerFunctionData) { this.consumes = routerFunctionData.getConsumes(); this.produces = routerFunctionData.getProduces(); this.headers = routerFunctionData.getHeaders(); + this.params = routerFunctionData.getParams(); this.queryParams = routerFunctionData.getQueryParams(); Map attributes = routerFunctionData.getAttributes(); @@ -369,6 +379,24 @@ public void setQueryParams(Map queryParams) { this.queryParams = queryParams; } + /** + * Gets params. + * + * @return the params + */ + public String[] getParams() { + return this.params; + } + + /** + * Sets params. + * + * @param params + */ + public void setParams(String[] params) { + this.params = params; + } + /** * Gets operation model. * diff --git a/springdoc-openapi-common/src/test/java/org/springdoc/api/AbstractOpenApiResourceTest.java b/springdoc-openapi-common/src/test/java/org/springdoc/api/AbstractOpenApiResourceTest.java index f2059ab07..20ba4332f 100644 --- a/springdoc-openapi-common/src/test/java/org/springdoc/api/AbstractOpenApiResourceTest.java +++ b/springdoc-openapi-common/src/test/java/org/springdoc/api/AbstractOpenApiResourceTest.java @@ -53,6 +53,7 @@ import org.springdoc.core.SpringDocProviders; import org.springdoc.core.customizers.OpenApiCustomiser; import org.springdoc.core.customizers.OperationCustomizer; +import org.springdoc.core.customizers.RouterOperationCustomizer; import org.springdoc.core.customizers.ServerBaseUrlCustomizer; import org.springdoc.core.filters.OpenApiMethodFilter; import org.springdoc.core.fn.RouterOperation; @@ -136,6 +137,7 @@ void calculatePathFromRouterOperation() { Optional.empty(), Optional.empty(), Optional.empty(), + Optional.empty(), new SpringDocConfigProperties(), springDocProviders ); @@ -207,6 +209,7 @@ void preLoadingModeShouldNotOverwriteServers() throws InterruptedException { Optional.empty(), Optional.of(singletonList(openApiCustomiser)), Optional.empty(), + Optional.empty(), properties, springDocProviders ); @@ -241,6 +244,7 @@ void serverBaseUrlCustomisersTest() throws InterruptedException { Optional.empty(), Optional.empty(), Optional.empty(), + Optional.empty(), properties, springDocProviders ); @@ -290,8 +294,8 @@ void serverBaseUrlCustomisersTest() throws InterruptedException { private static class EmptyPathsOpenApiResource extends AbstractOpenApiResource { - EmptyPathsOpenApiResource(String groupName, ObjectFactory openAPIBuilderObjectFactory, AbstractRequestService requestBuilder, GenericResponseService responseBuilder, OperationService operationParser, Optional> operationCustomizers, Optional> openApiCustomisers, Optional> methodFilters, SpringDocConfigProperties springDocConfigProperties, SpringDocProviders springDocProviders) { - super(groupName, openAPIBuilderObjectFactory, requestBuilder, responseBuilder, operationParser, operationCustomizers, openApiCustomisers, methodFilters, springDocConfigProperties, springDocProviders); + EmptyPathsOpenApiResource(String groupName, ObjectFactory openAPIBuilderObjectFactory, AbstractRequestService requestBuilder, GenericResponseService responseBuilder, OperationService operationParser, Optional> operationCustomizers, Optional> openApiCustomisers, Optional> routerOpeationCustomizers, Optional> methodFilters, SpringDocConfigProperties springDocConfigProperties, SpringDocProviders springDocProviders) { + super(groupName, openAPIBuilderObjectFactory, requestBuilder, responseBuilder, operationParser, operationCustomizers, openApiCustomisers, routerOpeationCustomizers, methodFilters, springDocConfigProperties, springDocProviders); } @Override diff --git a/springdoc-openapi-data-rest/src/main/java/org/springdoc/data/rest/core/DataRestRouterOperationService.java b/springdoc-openapi-data-rest/src/main/java/org/springdoc/data/rest/core/DataRestRouterOperationService.java index 4ad303276..e76f8cefd 100644 --- a/springdoc-openapi-data-rest/src/main/java/org/springdoc/data/rest/core/DataRestRouterOperationService.java +++ b/springdoc-openapi-data-rest/src/main/java/org/springdoc/data/rest/core/DataRestRouterOperationService.java @@ -279,7 +279,7 @@ else if (ControllerType.PROPERTY.equals(controllerType)) MethodResourceMapping methodResourceMapping, HandlerMethod handlerMethod, RequestMethod requestMethod, ResourceMetadata resourceMetadata, String operationPath, ControllerType controllerType) { - RouterOperation routerOperation = new RouterOperation(operationPath, new RequestMethod[] { requestMethod }, null, null, null); + RouterOperation routerOperation = new RouterOperation(operationPath, new RequestMethod[] { requestMethod }, null, null, null, null); MethodAttributes methodAttributes = new MethodAttributes(springDocConfigProperties.getDefaultConsumesMediaType(), springDocConfigProperties.getDefaultProducesMediaType(), dataRestRepository.getLocale()); methodAttributes.calculateConsumesProduces(handlerMethod.getMethod()); routerOperation.setConsumes(methodAttributes.getMethodConsumes()); diff --git a/springdoc-openapi-javadoc/src/test/java/test/org/springdoc/api/app94/SpringDocApp94Test.java b/springdoc-openapi-javadoc/src/test/java/test/org/springdoc/api/app94/SpringDocApp94Test.java index 22828377b..479f52990 100644 --- a/springdoc-openapi-javadoc/src/test/java/test/org/springdoc/api/app94/SpringDocApp94Test.java +++ b/springdoc-openapi-javadoc/src/test/java/test/org/springdoc/api/app94/SpringDocApp94Test.java @@ -36,6 +36,7 @@ import org.springdoc.core.customizers.OpenApiBuilderCustomizer; import org.springdoc.core.customizers.OpenApiCustomiser; import org.springdoc.core.customizers.OperationCustomizer; +import org.springdoc.core.customizers.RouterOperationCustomizer; import org.springdoc.core.filters.OpenApiMethodFilter; import org.springdoc.webmvc.api.OpenApiWebMvcResource; import test.org.springdoc.api.AbstractSpringDocTest; @@ -125,6 +126,7 @@ public RequestMappingHandlerMapping defaultTestHandlerMapping(GreetingController * @param operationCustomizers the operation customizers * @param springDocConfigProperties the spring doc config properties * @param openApiCustomisers the open api customisers + * @param routerOperationCustomizers the router operation customizers * @param methodFilters the method filters * @param springDocProviders the spring doc providers * @return the open api web mvc resource @@ -133,9 +135,9 @@ public RequestMappingHandlerMapping defaultTestHandlerMapping(GreetingController public OpenApiWebMvcResource openApiResource(ObjectFactory openAPIBuilderObjectFactory, AbstractRequestService requestBuilder, GenericResponseService responseBuilder, OperationService operationParser,Optional> operationCustomizers, SpringDocConfigProperties springDocConfigProperties, - Optional> openApiCustomisers, Optional> methodFilters,SpringDocProviders springDocProviders) { + Optional> openApiCustomisers, Optional> routerOperationCustomizers, Optional> methodFilters,SpringDocProviders springDocProviders) { return new OpenApiWebMvcResource(DEFAULT_GROUP_NAME, openAPIBuilderObjectFactory, requestBuilder, responseBuilder, operationParser, - operationCustomizers, openApiCustomisers,methodFilters, springDocConfigProperties, springDocProviders); + operationCustomizers, openApiCustomisers, routerOperationCustomizers, methodFilters, springDocConfigProperties, springDocProviders); } /** diff --git a/springdoc-openapi-webflux-core/src/main/java/org/springdoc/webflux/api/MultipleOpenApiResource.java b/springdoc-openapi-webflux-core/src/main/java/org/springdoc/webflux/api/MultipleOpenApiResource.java index 30c9cc8e0..17c61cd4b 100644 --- a/springdoc-openapi-webflux-core/src/main/java/org/springdoc/webflux/api/MultipleOpenApiResource.java +++ b/springdoc-openapi-webflux-core/src/main/java/org/springdoc/webflux/api/MultipleOpenApiResource.java @@ -162,6 +162,7 @@ private OpenApiResource buildWebFluxOpenApiResource(GroupedOpenApi item) { operationParser, Optional.of(item.getOperationCustomizers()), Optional.of(item.getOpenApiCustomisers()), + Optional.of(item.getRouterOperationCustomizers()), Optional.of(item.getOpenApiMethodFilters()), springDocConfigProperties, springDocProviders @@ -174,6 +175,7 @@ private OpenApiResource buildWebFluxOpenApiResource(GroupedOpenApi item) { operationParser, Optional.of(item.getOperationCustomizers()), Optional.of(item.getOpenApiCustomisers()), + Optional.of(item.getRouterOperationCustomizers()), Optional.of(item.getOpenApiMethodFilters()), springDocConfigProperties, springDocProviders); diff --git a/springdoc-openapi-webflux-core/src/main/java/org/springdoc/webflux/api/OpenApiActuatorResource.java b/springdoc-openapi-webflux-core/src/main/java/org/springdoc/webflux/api/OpenApiActuatorResource.java index 282939ab3..0f618ea13 100644 --- a/springdoc-openapi-webflux-core/src/main/java/org/springdoc/webflux/api/OpenApiActuatorResource.java +++ b/springdoc-openapi-webflux-core/src/main/java/org/springdoc/webflux/api/OpenApiActuatorResource.java @@ -37,6 +37,7 @@ import org.springdoc.core.SpringDocProviders; import org.springdoc.core.customizers.OpenApiCustomiser; import org.springdoc.core.customizers.OperationCustomizer; +import org.springdoc.core.customizers.RouterOperationCustomizer; import org.springdoc.core.filters.OpenApiMethodFilter; import reactor.core.publisher.Mono; @@ -70,12 +71,13 @@ public class OpenApiActuatorResource extends OpenApiResource { * @param operationParser the operation parser * @param operationCustomizers the operation customizers * @param openApiCustomisers the open api customisers + * @param routerOperationCustomizers the router operation customisers * @param methodFilters the method filters * @param springDocConfigProperties the spring doc config properties * @param springDocProviders the spring doc providers */ - public OpenApiActuatorResource(String groupName, ObjectFactory openAPIBuilderObjectFactory, AbstractRequestService requestBuilder, GenericResponseService responseBuilder, OperationService operationParser, Optional> operationCustomizers, Optional> openApiCustomisers, Optional> methodFilters, SpringDocConfigProperties springDocConfigProperties, SpringDocProviders springDocProviders) { - super(groupName, openAPIBuilderObjectFactory, requestBuilder, responseBuilder, operationParser, operationCustomizers, openApiCustomisers, methodFilters, springDocConfigProperties, springDocProviders); + public OpenApiActuatorResource(String groupName, ObjectFactory openAPIBuilderObjectFactory, AbstractRequestService requestBuilder, GenericResponseService responseBuilder, OperationService operationParser, Optional> operationCustomizers, Optional> openApiCustomisers, Optional> routerOperationCustomizers, Optional> methodFilters, SpringDocConfigProperties springDocConfigProperties, SpringDocProviders springDocProviders) { + super(groupName, openAPIBuilderObjectFactory, requestBuilder, responseBuilder, operationParser, operationCustomizers, openApiCustomisers, routerOperationCustomizers, methodFilters, springDocConfigProperties, springDocProviders); } /** @@ -87,12 +89,13 @@ public OpenApiActuatorResource(String groupName, ObjectFactory o * @param operationParser the operation parser * @param operationCustomizers the operation customizers * @param openApiCustomisers the open api customisers + * @param routerOperationCustomizers the router operation customisers * @param methodFilters the method filters * @param springDocConfigProperties the spring doc config properties * @param springDocProviders the spring doc providers */ - public OpenApiActuatorResource(ObjectFactory openAPIBuilderObjectFactory, AbstractRequestService requestBuilder, GenericResponseService responseBuilder, OperationService operationParser, Optional> operationCustomizers, Optional> openApiCustomisers, Optional> methodFilters, SpringDocConfigProperties springDocConfigProperties, SpringDocProviders springDocProviders) { - super(openAPIBuilderObjectFactory, requestBuilder, responseBuilder, operationParser, operationCustomizers, openApiCustomisers, methodFilters, springDocConfigProperties, springDocProviders); + public OpenApiActuatorResource(ObjectFactory openAPIBuilderObjectFactory, AbstractRequestService requestBuilder, GenericResponseService responseBuilder, OperationService operationParser, Optional> operationCustomizers, Optional> openApiCustomisers, Optional> routerOperationCustomizers, Optional> methodFilters, SpringDocConfigProperties springDocConfigProperties, SpringDocProviders springDocProviders) { + super(openAPIBuilderObjectFactory, requestBuilder, responseBuilder, operationParser, operationCustomizers, openApiCustomisers, routerOperationCustomizers, methodFilters, springDocConfigProperties, springDocProviders); } /** diff --git a/springdoc-openapi-webflux-core/src/main/java/org/springdoc/webflux/api/OpenApiResource.java b/springdoc-openapi-webflux-core/src/main/java/org/springdoc/webflux/api/OpenApiResource.java index 190ae9dda..665cfdeab 100644 --- a/springdoc-openapi-webflux-core/src/main/java/org/springdoc/webflux/api/OpenApiResource.java +++ b/springdoc-openapi-webflux-core/src/main/java/org/springdoc/webflux/api/OpenApiResource.java @@ -45,6 +45,7 @@ import org.springdoc.core.SpringDocProviders; import org.springdoc.core.customizers.OpenApiCustomiser; import org.springdoc.core.customizers.OperationCustomizer; +import org.springdoc.core.customizers.RouterOperationCustomizer; import org.springdoc.core.filters.OpenApiMethodFilter; import org.springdoc.core.providers.ActuatorProvider; import org.springdoc.core.providers.SpringWebProvider; @@ -78,6 +79,7 @@ public abstract class OpenApiResource extends AbstractOpenApiResource { * @param operationParser the operation parser * @param operationCustomizers the operation customizers * @param openApiCustomisers the open api customisers + * @param routerOperationCustomizers the router operation customizers * @param methodFilters the method filters * @param springDocConfigProperties the spring doc config properties * @param springDocProviders the spring doc providers @@ -86,10 +88,11 @@ public OpenApiResource(String groupName, ObjectFactory openAPIBu GenericResponseService responseBuilder, OperationService operationParser, Optional> operationCustomizers, Optional> openApiCustomisers, + Optional> routerOperationCustomizers, Optional> methodFilters, SpringDocConfigProperties springDocConfigProperties, SpringDocProviders springDocProviders) { - super(groupName, openAPIBuilderObjectFactory, requestBuilder, responseBuilder, operationParser, operationCustomizers, openApiCustomisers, methodFilters, springDocConfigProperties, springDocProviders); + super(groupName, openAPIBuilderObjectFactory, requestBuilder, responseBuilder, operationParser, operationCustomizers, openApiCustomisers, routerOperationCustomizers, methodFilters, springDocConfigProperties, springDocProviders); } /** @@ -101,6 +104,7 @@ public OpenApiResource(String groupName, ObjectFactory openAPIBu * @param operationParser the operation parser * @param operationCustomizers the operation customizers * @param openApiCustomisers the open api customisers + * @param routerOperationCustomizers the router operation customizers * @param methodFilters the method filters * @param springDocConfigProperties the spring doc config properties * @param springDocProviders the spring doc providers @@ -109,10 +113,11 @@ public OpenApiResource(ObjectFactory openAPIBuilderObjectFactory GenericResponseService responseBuilder, OperationService operationParser, Optional> operationCustomizers, Optional> openApiCustomisers, + Optional> routerOperationCustomizers, Optional> methodFilters, SpringDocConfigProperties springDocConfigProperties, SpringDocProviders springDocProviders) { - super(DEFAULT_GROUP_NAME, openAPIBuilderObjectFactory, requestBuilder, responseBuilder, operationParser, operationCustomizers, openApiCustomisers, methodFilters, springDocConfigProperties, springDocProviders); + super(DEFAULT_GROUP_NAME, openAPIBuilderObjectFactory, requestBuilder, responseBuilder, operationParser, operationCustomizers, openApiCustomisers, routerOperationCustomizers, methodFilters, springDocConfigProperties, springDocProviders); } @@ -195,13 +200,14 @@ protected void calculatePath(Map restControllers, Map requestMethods = requestMappingInfo.getMethodsCondition().getMethods(); // default allowed requestmethods if (requestMethods.isEmpty()) requestMethods = this.getDefaultAllowedHttpMethods(); - calculatePath(handlerMethod, operationPath, requestMethods, consumes, produces, headers, locale, openAPI); + calculatePath(handlerMethod, operationPath, requestMethods, consumes, produces, headers, params, locale, openAPI); } } } diff --git a/springdoc-openapi-webflux-core/src/main/java/org/springdoc/webflux/api/OpenApiWebfluxResource.java b/springdoc-openapi-webflux-core/src/main/java/org/springdoc/webflux/api/OpenApiWebfluxResource.java index 714e7fc7c..d7d2dd58a 100644 --- a/springdoc-openapi-webflux-core/src/main/java/org/springdoc/webflux/api/OpenApiWebfluxResource.java +++ b/springdoc-openapi-webflux-core/src/main/java/org/springdoc/webflux/api/OpenApiWebfluxResource.java @@ -37,6 +37,7 @@ import org.springdoc.core.SpringDocProviders; import org.springdoc.core.customizers.OpenApiCustomiser; import org.springdoc.core.customizers.OperationCustomizer; +import org.springdoc.core.customizers.RouterOperationCustomizer; import org.springdoc.core.filters.OpenApiMethodFilter; import org.springdoc.core.providers.SpringWebProvider; import reactor.core.publisher.Mono; @@ -71,12 +72,13 @@ public class OpenApiWebfluxResource extends OpenApiResource { * @param operationParser the operation parser * @param operationCustomizers the operation customizers * @param openApiCustomisers the open api customisers + * @param routerOperationCustomizers the router operation customizers * @param methodFilters the method filters * @param springDocConfigProperties the spring doc config properties * @param springDocProviders the spring doc providers */ - public OpenApiWebfluxResource(String groupName, ObjectFactory openAPIBuilderObjectFactory, AbstractRequestService requestBuilder, GenericResponseService responseBuilder, OperationService operationParser, Optional> operationCustomizers, Optional> openApiCustomisers, Optional> methodFilters, SpringDocConfigProperties springDocConfigProperties, SpringDocProviders springDocProviders) { - super(groupName, openAPIBuilderObjectFactory, requestBuilder, responseBuilder, operationParser, operationCustomizers, openApiCustomisers, methodFilters, springDocConfigProperties, springDocProviders); + public OpenApiWebfluxResource(String groupName, ObjectFactory openAPIBuilderObjectFactory, AbstractRequestService requestBuilder, GenericResponseService responseBuilder, OperationService operationParser, Optional> operationCustomizers, Optional> openApiCustomisers, Optional> routerOperationCustomizers, Optional> methodFilters, SpringDocConfigProperties springDocConfigProperties, SpringDocProviders springDocProviders) { + super(groupName, openAPIBuilderObjectFactory, requestBuilder, responseBuilder, operationParser, operationCustomizers, openApiCustomisers, routerOperationCustomizers, methodFilters, springDocConfigProperties, springDocProviders); } /** @@ -88,13 +90,14 @@ public OpenApiWebfluxResource(String groupName, ObjectFactory op * @param operationParser the operation parser * @param operationCustomizers the operation customizers * @param openApiCustomisers the open api customisers + * @param routerOperationCustomizers the router operation customizers * @param methodFilters the method filters * @param springDocConfigProperties the spring doc config properties * @param springDocProviders the spring doc providers */ @Autowired - public OpenApiWebfluxResource(ObjectFactory openAPIBuilderObjectFactory, AbstractRequestService requestBuilder, GenericResponseService responseBuilder, OperationService operationParser, Optional> operationCustomizers, Optional> openApiCustomisers,Optional> methodFilters, SpringDocConfigProperties springDocConfigProperties, SpringDocProviders springDocProviders) { - super(openAPIBuilderObjectFactory, requestBuilder, responseBuilder, operationParser, operationCustomizers, openApiCustomisers,methodFilters, springDocConfigProperties, springDocProviders); + public OpenApiWebfluxResource(ObjectFactory openAPIBuilderObjectFactory, AbstractRequestService requestBuilder, GenericResponseService responseBuilder, OperationService operationParser, Optional> operationCustomizers, Optional> openApiCustomisers, Optional> routerOperationCustomizers, Optional> methodFilters, SpringDocConfigProperties springDocConfigProperties, SpringDocProviders springDocProviders) { + super(openAPIBuilderObjectFactory, requestBuilder, responseBuilder, operationParser, operationCustomizers, openApiCustomisers,routerOperationCustomizers, methodFilters, springDocConfigProperties, springDocProviders); } /** diff --git a/springdoc-openapi-webflux-core/src/main/java/org/springdoc/webflux/core/SpringDocWebFluxConfiguration.java b/springdoc-openapi-webflux-core/src/main/java/org/springdoc/webflux/core/SpringDocWebFluxConfiguration.java index b046d6158..856d4bb20 100644 --- a/springdoc-openapi-webflux-core/src/main/java/org/springdoc/webflux/core/SpringDocWebFluxConfiguration.java +++ b/springdoc-openapi-webflux-core/src/main/java/org/springdoc/webflux/core/SpringDocWebFluxConfiguration.java @@ -38,6 +38,7 @@ import org.springdoc.core.customizers.OpenApiCustomiser; import org.springdoc.core.customizers.OperationCustomizer; import org.springdoc.core.customizers.ParameterCustomizer; +import org.springdoc.core.customizers.RouterOperationCustomizer; import org.springdoc.core.filters.OpenApiMethodFilter; import org.springdoc.core.providers.ActuatorProvider; import org.springdoc.core.providers.ObjectMapperProvider; @@ -86,6 +87,7 @@ public class SpringDocWebFluxConfiguration { * @param operationParser the operation parser * @param operationCustomizers the operation customizers * @param openApiCustomisers the open api customisers + * @param routerOperationCustomizers the router operation customizers * @param methodFilters the method filters * @param springDocConfigProperties the spring doc config properties * @param springDocProviders the spring doc providers @@ -99,12 +101,13 @@ OpenApiWebfluxResource openApiResource(ObjectFactory openAPIBuil GenericResponseService responseBuilder, OperationService operationParser, Optional> operationCustomizers, Optional> openApiCustomisers, + Optional> routerOperationCustomizers, Optional> methodFilters, SpringDocConfigProperties springDocConfigProperties, SpringDocProviders springDocProviders) { return new OpenApiWebfluxResource(openAPIBuilderObjectFactory, requestBuilder, responseBuilder, operationParser, operationCustomizers, - openApiCustomisers, methodFilters, springDocConfigProperties, springDocProviders); + openApiCustomisers, routerOperationCustomizers, methodFilters, springDocConfigProperties, springDocProviders); } /** @@ -214,6 +217,7 @@ ActuatorProvider actuatorProvider(ServerProperties serverProperties, * @param operationParser the operation parser * @param operationCustomizers the operation customizers * @param openApiCustomisers the open api customisers + * @param routerOperationCustomizers the router operation customizers * @param methodFilters the method filters * @param springDocConfigProperties the spring doc config properties * @param springDocProviders the spring doc providers @@ -228,12 +232,13 @@ OpenApiActuatorResource actuatorOpenApiResource(ObjectFactory op GenericResponseService responseBuilder, OperationService operationParser, Optional> operationCustomizers, Optional> openApiCustomisers, + Optional> routerOperationCustomizers, Optional> methodFilters, SpringDocConfigProperties springDocConfigProperties, SpringDocProviders springDocProviders) { return new OpenApiActuatorResource(openAPIBuilderObjectFactory, requestBuilder, responseBuilder, operationParser,operationCustomizers, - openApiCustomisers, methodFilters, springDocConfigProperties, springDocProviders); + openApiCustomisers, routerOperationCustomizers, methodFilters, springDocConfigProperties, springDocProviders); } } } diff --git a/springdoc-openapi-webflux-core/src/test/java/test/org/springdoc/api/app187/DuplicatedPathController.java b/springdoc-openapi-webflux-core/src/test/java/test/org/springdoc/api/app187/DuplicatedPathController.java new file mode 100644 index 000000000..a9df9a8dd --- /dev/null +++ b/springdoc-openapi-webflux-core/src/test/java/test/org/springdoc/api/app187/DuplicatedPathController.java @@ -0,0 +1,19 @@ +package test.org.springdoc.api.app187; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class DuplicatedPathController { + + @GetMapping("/duplicated") + public String duplicated1() { + return "globalBeanFiltered"; + } + + @GetMapping(value = "/duplicated", params = "filter=params") + public String duplicated2() { + return "beanFiltered"; + } + +} diff --git a/springdoc-openapi-webflux-core/src/test/java/test/org/springdoc/api/app187/SpringDocApp187Test.java b/springdoc-openapi-webflux-core/src/test/java/test/org/springdoc/api/app187/SpringDocApp187Test.java new file mode 100644 index 000000000..8d3f48628 --- /dev/null +++ b/springdoc-openapi-webflux-core/src/test/java/test/org/springdoc/api/app187/SpringDocApp187Test.java @@ -0,0 +1,36 @@ +package test.org.springdoc.api.app187; + +import org.junit.jupiter.api.Test; +import org.springdoc.core.Constants; +import org.springdoc.core.customizers.RouterOperationCustomizer; +import test.org.springdoc.api.AbstractSpringDocTest; + +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; + +class SpringDocApp187Test extends AbstractSpringDocTest { + + @SpringBootApplication + @ComponentScan(basePackages = { "org.springdoc", "test.org.springdoc.api.app187" }) + static class SpringDocTestApp { + + @Bean + public RouterOperationCustomizer addRouterOperationCustomizer() { + return (routerOperation, handlerMethod) -> { + if (routerOperation.getParams().length > 0) { + routerOperation.setPath(routerOperation.getPath() + "?" + String.join("&", routerOperation.getParams())); + } + return routerOperation; + }; + } + } + + @Test + public void testAddRouterOperationCustomizerBean() { + webTestClient.get().uri(Constants.DEFAULT_API_DOCS_URL).exchange() + .expectStatus().isOk() + .expectBody().json(getContent("results/app187.json"), true); + } + +} diff --git a/springdoc-openapi-webflux-core/src/test/resources/results/app187.json b/springdoc-openapi-webflux-core/src/test/resources/results/app187.json new file mode 100644 index 000000000..a918f64b2 --- /dev/null +++ b/springdoc-openapi-webflux-core/src/test/resources/results/app187.json @@ -0,0 +1,56 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "OpenAPI definition", + "version": "v0" + }, + "servers": [ + { + "url": "", + "description": "Generated server url" + } + ], + "paths": { + "/duplicated": { + "get": { + "tags": [ + "duplicated-path-controller" + ], + "operationId": "duplicated1", + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/duplicated?filter=params": { + "get": { + "tags": [ + "duplicated-path-controller" + ], + "operationId": "duplicated2", + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "type": "string" + } + } + } + } + } + } + } + }, + "components": {} +} diff --git a/springdoc-openapi-webmvc-core/src/main/java/org/springdoc/webmvc/api/MultipleOpenApiResource.java b/springdoc-openapi-webmvc-core/src/main/java/org/springdoc/webmvc/api/MultipleOpenApiResource.java index 4b59ba3e8..e078d6e1a 100644 --- a/springdoc-openapi-webmvc-core/src/main/java/org/springdoc/webmvc/api/MultipleOpenApiResource.java +++ b/springdoc-openapi-webmvc-core/src/main/java/org/springdoc/webmvc/api/MultipleOpenApiResource.java @@ -161,6 +161,7 @@ private OpenApiResource buildWebMvcOpenApiResource(GroupedOpenApi item) { operationParser, Optional.of(item.getOperationCustomizers()), Optional.of(item.getOpenApiCustomisers()), + Optional.of(item.getRouterOperationCustomizers()), Optional.of(item.getOpenApiMethodFilters()), springDocConfigProperties, springDocProviders @@ -173,6 +174,7 @@ private OpenApiResource buildWebMvcOpenApiResource(GroupedOpenApi item) { operationParser, Optional.of(item.getOperationCustomizers()), Optional.of(item.getOpenApiCustomisers()), + Optional.of(item.getRouterOperationCustomizers()), Optional.of(item.getOpenApiMethodFilters()), springDocConfigProperties, springDocProviders ); diff --git a/springdoc-openapi-webmvc-core/src/main/java/org/springdoc/webmvc/api/OpenApiActuatorResource.java b/springdoc-openapi-webmvc-core/src/main/java/org/springdoc/webmvc/api/OpenApiActuatorResource.java index 69fa04490..dec459d52 100644 --- a/springdoc-openapi-webmvc-core/src/main/java/org/springdoc/webmvc/api/OpenApiActuatorResource.java +++ b/springdoc-openapi-webmvc-core/src/main/java/org/springdoc/webmvc/api/OpenApiActuatorResource.java @@ -38,6 +38,7 @@ import org.springdoc.core.SpringDocProviders; import org.springdoc.core.customizers.OpenApiCustomiser; import org.springdoc.core.customizers.OperationCustomizer; +import org.springdoc.core.customizers.RouterOperationCustomizer; import org.springdoc.core.filters.OpenApiMethodFilter; import org.springframework.beans.factory.ObjectFactory; @@ -68,6 +69,7 @@ public class OpenApiActuatorResource extends OpenApiResource { * @param operationParser the operation parser * @param operationCustomizers the operation customizers * @param openApiCustomisers the open api customisers + * @param routerOperationCustomizers the router operation customizers * @param methodFilters the method filters * @param springDocConfigProperties the spring doc config properties * @param springDocProviders the spring doc providers @@ -77,10 +79,11 @@ public OpenApiActuatorResource(ObjectFactory openAPIBuilderObjec OperationService operationParser, Optional> operationCustomizers, Optional> openApiCustomisers, + Optional> routerOperationCustomizers, Optional> methodFilters, SpringDocConfigProperties springDocConfigProperties, SpringDocProviders springDocProviders) { - super(openAPIBuilderObjectFactory, requestBuilder, responseBuilder, operationParser, operationCustomizers, openApiCustomisers, methodFilters, springDocConfigProperties, springDocProviders); + super(openAPIBuilderObjectFactory, requestBuilder, responseBuilder, operationParser, operationCustomizers, openApiCustomisers, routerOperationCustomizers, methodFilters, springDocConfigProperties, springDocProviders); } /** @@ -93,6 +96,7 @@ public OpenApiActuatorResource(ObjectFactory openAPIBuilderObjec * @param operationParser the operation parser * @param operationCustomizers the operation customizers * @param openApiCustomisers the open api customisers + * @param routerOperationCustomizers the router operation customizers * @param methodFilters the method filters * @param springDocConfigProperties the spring doc config properties * @param springDocProviders the spring doc providers @@ -100,9 +104,9 @@ public OpenApiActuatorResource(ObjectFactory openAPIBuilderObjec public OpenApiActuatorResource(String groupName, ObjectFactory openAPIBuilderObjectFactory, AbstractRequestService requestBuilder, GenericResponseService responseBuilder, OperationService operationParser, Optional> operationCustomizers, - Optional> openApiCustomisers, Optional> methodFilters, SpringDocConfigProperties springDocConfigProperties, + Optional> openApiCustomisers, Optional> routerOperationCustomizers, Optional> methodFilters, SpringDocConfigProperties springDocConfigProperties, SpringDocProviders springDocProviders) { - super(groupName, openAPIBuilderObjectFactory, requestBuilder, responseBuilder, operationParser, operationCustomizers, openApiCustomisers,methodFilters, + super(groupName, openAPIBuilderObjectFactory, requestBuilder, responseBuilder, operationParser, operationCustomizers, openApiCustomisers, routerOperationCustomizers, methodFilters, springDocConfigProperties, springDocProviders); } diff --git a/springdoc-openapi-webmvc-core/src/main/java/org/springdoc/webmvc/api/OpenApiResource.java b/springdoc-openapi-webmvc-core/src/main/java/org/springdoc/webmvc/api/OpenApiResource.java index 218ddd73a..ea0c40b56 100644 --- a/springdoc-openapi-webmvc-core/src/main/java/org/springdoc/webmvc/api/OpenApiResource.java +++ b/springdoc-openapi-webmvc-core/src/main/java/org/springdoc/webmvc/api/OpenApiResource.java @@ -47,6 +47,7 @@ import org.springdoc.core.SpringDocProviders; import org.springdoc.core.customizers.OpenApiCustomiser; import org.springdoc.core.customizers.OperationCustomizer; +import org.springdoc.core.customizers.RouterOperationCustomizer; import org.springdoc.core.filters.OpenApiMethodFilter; import org.springdoc.core.fn.RouterOperation; import org.springdoc.core.providers.ActuatorProvider; @@ -81,6 +82,7 @@ public abstract class OpenApiResource extends AbstractOpenApiResource { * @param operationParser the operation parser * @param operationCustomizers the operation customizers * @param openApiCustomisers the open api customisers + * @param routerOperationCustomizers the router operation customisers * @param methodFilters the method filters * @param springDocConfigProperties the spring doc config properties * @param springDocProviders the spring doc providers @@ -89,10 +91,11 @@ public OpenApiResource(String groupName, ObjectFactory openAPIBu GenericResponseService responseBuilder, OperationService operationParser, Optional> operationCustomizers, Optional> openApiCustomisers, + Optional> routerOperationCustomizers, Optional> methodFilters, SpringDocConfigProperties springDocConfigProperties, SpringDocProviders springDocProviders) { - super(groupName, openAPIBuilderObjectFactory, requestBuilder, responseBuilder, operationParser, operationCustomizers, openApiCustomisers, methodFilters, springDocConfigProperties, springDocProviders); + super(groupName, openAPIBuilderObjectFactory, requestBuilder, responseBuilder, operationParser, operationCustomizers, openApiCustomisers, routerOperationCustomizers, methodFilters, springDocConfigProperties, springDocProviders); } /** @@ -104,6 +107,7 @@ public OpenApiResource(String groupName, ObjectFactory openAPIBu * @param operationParser the operation parser * @param operationCustomizers the operation customizers * @param openApiCustomisers the open api customisers + * @param routerOperationCustomizers the router operation customisers * @param methodFilters the method filters * @param springDocConfigProperties the spring doc config properties * @param springDocProviders the spring doc providers @@ -112,10 +116,11 @@ public OpenApiResource(ObjectFactory openAPIBuilderObjectFactory GenericResponseService responseBuilder, OperationService operationParser, Optional> operationCustomizers, Optional> openApiCustomisers, + Optional> routerOperationCustomizers, Optional> methodFilters, SpringDocConfigProperties springDocConfigProperties, SpringDocProviders springDocProviders) { - super(DEFAULT_GROUP_NAME, openAPIBuilderObjectFactory, requestBuilder, responseBuilder, operationParser, operationCustomizers, openApiCustomisers, methodFilters, springDocConfigProperties, springDocProviders); + super(DEFAULT_GROUP_NAME, openAPIBuilderObjectFactory, requestBuilder, responseBuilder, operationParser, operationCustomizers, openApiCustomisers, routerOperationCustomizers, methodFilters, springDocConfigProperties, springDocProviders); } /** @@ -219,13 +224,14 @@ protected void calculatePath(Map restControllers, Map requestMethods = requestMappingInfo.getMethodsCondition().getMethods(); // default allowed requestmethods if (requestMethods.isEmpty()) requestMethods = this.getDefaultAllowedHttpMethods(); - calculatePath(handlerMethod, operationPath, requestMethods, consumes, produces , headers, locale, openAPI); + calculatePath(handlerMethod, operationPath, requestMethods, consumes, produces , headers, params, locale, openAPI); } } } diff --git a/springdoc-openapi-webmvc-core/src/main/java/org/springdoc/webmvc/api/OpenApiWebMvcResource.java b/springdoc-openapi-webmvc-core/src/main/java/org/springdoc/webmvc/api/OpenApiWebMvcResource.java index 88efe8203..543e67c5c 100644 --- a/springdoc-openapi-webmvc-core/src/main/java/org/springdoc/webmvc/api/OpenApiWebMvcResource.java +++ b/springdoc-openapi-webmvc-core/src/main/java/org/springdoc/webmvc/api/OpenApiWebMvcResource.java @@ -39,6 +39,7 @@ import org.springdoc.core.SpringDocProviders; import org.springdoc.core.customizers.OpenApiCustomiser; import org.springdoc.core.customizers.OperationCustomizer; +import org.springdoc.core.customizers.RouterOperationCustomizer; import org.springdoc.core.filters.OpenApiMethodFilter; import org.springdoc.core.providers.SpringWebProvider; @@ -70,12 +71,13 @@ public class OpenApiWebMvcResource extends OpenApiResource { * @param operationParser the operation parser * @param operationCustomizers the operation customizers * @param openApiCustomisers the open api customisers + * @param routerOperationCustomizers the router operation customizers * @param methodFilters the method filters * @param springDocConfigProperties the spring doc config properties * @param springDocProviders the spring doc providers */ - public OpenApiWebMvcResource(String groupName, ObjectFactory openAPIBuilderObjectFactory, AbstractRequestService requestBuilder, GenericResponseService responseBuilder, OperationService operationParser, Optional> operationCustomizers, Optional> openApiCustomisers, Optional> methodFilters, SpringDocConfigProperties springDocConfigProperties, SpringDocProviders springDocProviders) { - super(groupName, openAPIBuilderObjectFactory, requestBuilder, responseBuilder, operationParser, operationCustomizers, openApiCustomisers, methodFilters, springDocConfigProperties, springDocProviders); + public OpenApiWebMvcResource(String groupName, ObjectFactory openAPIBuilderObjectFactory, AbstractRequestService requestBuilder, GenericResponseService responseBuilder, OperationService operationParser, Optional> operationCustomizers, Optional> openApiCustomisers, Optional> routerOperationCustomizers, Optional> methodFilters, SpringDocConfigProperties springDocConfigProperties, SpringDocProviders springDocProviders) { + super(groupName, openAPIBuilderObjectFactory, requestBuilder, responseBuilder, operationParser, operationCustomizers, openApiCustomisers, routerOperationCustomizers ,methodFilters, springDocConfigProperties, springDocProviders); } /** @@ -87,13 +89,14 @@ public OpenApiWebMvcResource(String groupName, ObjectFactory ope * @param operationParser the operation parser * @param operationCustomizers the operation customizers * @param openApiCustomisers the open api customisers + * @param routerOperationCustomizers the router operation customizers * @param methodFilters the method filters * @param springDocConfigProperties the spring doc config properties * @param springDocProviders the spring doc providers */ @Autowired - public OpenApiWebMvcResource(ObjectFactory openAPIBuilderObjectFactory, AbstractRequestService requestBuilder, GenericResponseService responseBuilder, OperationService operationParser, Optional> operationCustomizers, Optional> openApiCustomisers, Optional> methodFilters, SpringDocConfigProperties springDocConfigProperties, SpringDocProviders springDocProviders) { - super(openAPIBuilderObjectFactory, requestBuilder, responseBuilder, operationParser, operationCustomizers, openApiCustomisers, methodFilters, springDocConfigProperties, springDocProviders); + public OpenApiWebMvcResource(ObjectFactory openAPIBuilderObjectFactory, AbstractRequestService requestBuilder, GenericResponseService responseBuilder, OperationService operationParser, Optional> operationCustomizers, Optional> openApiCustomisers, Optional> routerOperationCustomizers, Optional> methodFilters, SpringDocConfigProperties springDocConfigProperties, SpringDocProviders springDocProviders) { + super(openAPIBuilderObjectFactory, requestBuilder, responseBuilder, operationParser, operationCustomizers, openApiCustomisers, routerOperationCustomizers, methodFilters, springDocConfigProperties, springDocProviders); } /** diff --git a/springdoc-openapi-webmvc-core/src/main/java/org/springdoc/webmvc/core/SpringDocWebMvcConfiguration.java b/springdoc-openapi-webmvc-core/src/main/java/org/springdoc/webmvc/core/SpringDocWebMvcConfiguration.java index 4121bc358..f32bd618e 100644 --- a/springdoc-openapi-webmvc-core/src/main/java/org/springdoc/webmvc/core/SpringDocWebMvcConfiguration.java +++ b/springdoc-openapi-webmvc-core/src/main/java/org/springdoc/webmvc/core/SpringDocWebMvcConfiguration.java @@ -38,6 +38,7 @@ import org.springdoc.core.customizers.OpenApiCustomiser; import org.springdoc.core.customizers.OperationCustomizer; import org.springdoc.core.customizers.ParameterCustomizer; +import org.springdoc.core.customizers.RouterOperationCustomizer; import org.springdoc.core.filters.OpenApiMethodFilter; import org.springdoc.core.providers.ActuatorProvider; import org.springdoc.core.providers.SpringWebProvider; @@ -94,6 +95,7 @@ public class SpringDocWebMvcConfiguration { * @param springDocConfigProperties the spring doc config properties * @param operationCustomizers the operation customizers * @param openApiCustomisers the open api customisers + * @param routerOperationCustomizers the router operation customisers * @param methodFilters the method filters * @param springDocProviders the spring doc providers * @return the open api resource @@ -107,11 +109,12 @@ OpenApiWebMvcResource openApiResource(ObjectFactory openAPIBuild SpringDocConfigProperties springDocConfigProperties, Optional> operationCustomizers, Optional> openApiCustomisers, + Optional> routerOperationCustomizers, Optional> methodFilters, SpringDocProviders springDocProviders) { return new OpenApiWebMvcResource(openAPIBuilderObjectFactory, requestBuilder, responseBuilder, operationParser, operationCustomizers, - openApiCustomisers, methodFilters, springDocConfigProperties, springDocProviders); + openApiCustomisers, routerOperationCustomizers, methodFilters, springDocConfigProperties, springDocProviders); } /** @@ -228,6 +231,7 @@ ActuatorProvider actuatorProvider(ServerProperties serverProperties, * @param springDocConfigProperties the spring doc config properties * @param operationCustomizers the operation customizers * @param openApiCustomisers the open api customisers + * @param routerOperationCustomizers the router operation customisers * @param methodFilters the method filters * @param springDocProviders the spring doc providers * @return the open api actuator resource @@ -242,12 +246,13 @@ OpenApiActuatorResource openApiActuatorResource(ObjectFactory op SpringDocConfigProperties springDocConfigProperties, Optional> operationCustomizers, Optional> openApiCustomisers, + Optional> routerOperationCustomizers, Optional> methodFilters, SpringDocProviders springDocProviders) { return new OpenApiActuatorResource(openAPIBuilderObjectFactory, requestBuilder, responseBuilder, operationParser, - operationCustomizers, openApiCustomisers, methodFilters, + operationCustomizers, openApiCustomisers, routerOperationCustomizers, methodFilters, springDocConfigProperties, springDocProviders); } } diff --git a/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v30/app94/SpringDocApp94Test.java b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v30/app94/SpringDocApp94Test.java index 35944807e..36f2743bc 100644 --- a/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v30/app94/SpringDocApp94Test.java +++ b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v30/app94/SpringDocApp94Test.java @@ -40,6 +40,7 @@ import org.springdoc.core.customizers.OpenApiBuilderCustomizer; import org.springdoc.core.customizers.OpenApiCustomiser; import org.springdoc.core.customizers.OperationCustomizer; +import org.springdoc.core.customizers.RouterOperationCustomizer; import org.springdoc.core.filters.OpenApiMethodFilter; import org.springdoc.webmvc.api.OpenApiWebMvcResource; import test.org.springdoc.api.v30.AbstractSpringDocV30Test; @@ -95,9 +96,9 @@ public RequestMappingHandlerMapping defaultTestHandlerMapping(GreetingController @Bean(name = "openApiResource") public OpenApiWebMvcResource openApiResource(ObjectFactory openAPIBuilderObjectFactory, AbstractRequestService requestBuilder, GenericResponseService responseBuilder, OperationService operationParser,Optional> operationCustomizers, SpringDocConfigProperties springDocConfigProperties, - Optional> openApiCustomisers, Optional> methodFilters, SpringDocProviders springDocProviders) { + Optional> openApiCustomisers, Optional> routerOperationCustomizers, Optional> methodFilters, SpringDocProviders springDocProviders) { return new OpenApiWebMvcResource(DEFAULT_GROUP_NAME, openAPIBuilderObjectFactory, requestBuilder, responseBuilder, operationParser, - operationCustomizers, openApiCustomisers, methodFilters, springDocConfigProperties, springDocProviders); + operationCustomizers, openApiCustomisers, routerOperationCustomizers, methodFilters, springDocConfigProperties, springDocProviders); } @Override diff --git a/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v31/app5/DuplicatedPathController.java b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v31/app5/DuplicatedPathController.java new file mode 100644 index 000000000..c51726bf9 --- /dev/null +++ b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v31/app5/DuplicatedPathController.java @@ -0,0 +1,19 @@ +package test.org.springdoc.api.v31.app5; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class DuplicatedPathController { + + @GetMapping("/duplicated") + public String duplicated1() { + return "globalBeanFiltered"; + } + + @GetMapping(value = "/duplicated", params = "filter=params") + public String duplicated2() { + return "beanFiltered"; + } + +} diff --git a/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v31/app5/SpringDocApp5Test.java b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v31/app5/SpringDocApp5Test.java new file mode 100644 index 000000000..617429e9a --- /dev/null +++ b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v31/app5/SpringDocApp5Test.java @@ -0,0 +1,48 @@ +package test.org.springdoc.api.v31.app5; + +import org.junit.jupiter.api.Test; +import org.springdoc.core.Constants; +import org.springdoc.core.customizers.RouterOperationCustomizer; +import test.org.springdoc.api.v31.AbstractSpringDocV31Test; + +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.test.web.servlet.MvcResult; + +import static org.hamcrest.Matchers.is; +import static org.skyscreamer.jsonassert.JSONAssert.assertEquals; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +class SpringDocApp5Test extends AbstractSpringDocV31Test { + + @SpringBootApplication + @ComponentScan + static class SpringDocTestApp { + + @Bean + public RouterOperationCustomizer addRouterOperationCustomizer() { + return (routerOperation, handlerMethod) -> { + if (routerOperation.getParams().length > 0) { + routerOperation.setPath(routerOperation.getPath() + "?" + String.join("&", routerOperation.getParams())); + } + return routerOperation; + }; + } + } + + @Test + public void testAddRouterOperationCustomizerBean() throws Exception { + className = getClass().getSimpleName(); + String testNumber = className.replaceAll("[^0-9]", ""); + MvcResult mockMvcResult = + mockMvc.perform(get(Constants.DEFAULT_API_DOCS_URL)).andExpect(status().isOk()) + .andExpect(jsonPath("$.openapi", is("3.1.0"))).andReturn(); + String result = mockMvcResult.getResponse().getContentAsString(); + String expected = getContent("results/3.1.0/app" + testNumber + ".json"); + assertEquals(expected, result, true); + } + +} diff --git a/springdoc-openapi-webmvc-core/src/test/resources/results/3.1.0/app5.json b/springdoc-openapi-webmvc-core/src/test/resources/results/3.1.0/app5.json new file mode 100644 index 000000000..48c1095d8 --- /dev/null +++ b/springdoc-openapi-webmvc-core/src/test/resources/results/3.1.0/app5.json @@ -0,0 +1,52 @@ +{ + "openapi": "3.1.0", + "info": { + "title": "OpenAPI definition", + "version": "v0" + }, + "servers": [ + { + "url": "http://localhost", + "description": "Generated server url" + } + ], + "paths": { + "/duplicated": { + "get": { + "tags": [ + "duplicated-path-controller" + ], + "operationId": "duplicated1", + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": {} + } + } + } + } + } + }, + "/duplicated?filter=params": { + "get": { + "tags": [ + "duplicated-path-controller" + ], + "operationId": "duplicated2", + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": {} + } + } + } + } + } + } + }, + "components": {} +}