Skip to content

Commit a0be739

Browse files
author
bnasslahsen
committed
Add Support of Actuator endpoints using webflux. Fixes #713
1 parent 66baa2c commit a0be739

File tree

19 files changed

+533
-75
lines changed

19 files changed

+533
-75
lines changed

springdoc-openapi-common/src/main/java/org/springdoc/api/AbstractOpenApiResource.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
import org.slf4j.Logger;
6060
import org.slf4j.LoggerFactory;
6161
import org.springdoc.core.AbstractRequestBuilder;
62+
import org.springdoc.core.ActuatorProvider;
6263
import org.springdoc.core.GenericParameterBuilder;
6364
import org.springdoc.core.GenericResponseBuilder;
6465
import org.springdoc.core.MethodAttributes;
@@ -97,6 +98,8 @@ public abstract class AbstractOpenApiResource extends SpecFilter {
9798

9899
protected final SpringDocConfigProperties springDocConfigProperties;
99100

101+
protected final Optional<ActuatorProvider> actuatorProvider;
102+
100103
private final AbstractRequestBuilder requestBuilder;
101104

102105
private final GenericResponseBuilder responseBuilder;
@@ -116,7 +119,8 @@ protected AbstractOpenApiResource(String groupName, OpenAPIBuilder openAPIBuilde
116119
GenericResponseBuilder responseBuilder, OperationBuilder operationParser,
117120
Optional<List<OperationCustomizer>> operationCustomizers,
118121
Optional<List<OpenApiCustomiser>> openApiCustomisers,
119-
SpringDocConfigProperties springDocConfigProperties) {
122+
SpringDocConfigProperties springDocConfigProperties,
123+
Optional<ActuatorProvider> actuatorProvider) {
120124
super();
121125
this.groupName = Objects.requireNonNull(groupName, "groupName");
122126
this.openAPIBuilder = openAPIBuilder;
@@ -128,6 +132,7 @@ protected AbstractOpenApiResource(String groupName, OpenAPIBuilder openAPIBuilde
128132
if (operationCustomizers.isPresent())
129133
operationCustomizers.get().removeIf(Objects::isNull);
130134
this.operationCustomizers = operationCustomizers;
135+
this.actuatorProvider = actuatorProvider;
131136
}
132137

133138
public static void addRestControllers(Class<?>... classes) {
Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -18,33 +18,21 @@
1818
*
1919
*/
2020

21-
package org.springdoc.webmvc.api;
21+
package org.springdoc.core;
2222

2323
import java.util.Map;
2424

2525
import io.swagger.v3.oas.models.ExternalDocumentation;
2626
import io.swagger.v3.oas.models.tags.Tag;
27-
import org.springdoc.core.Constants;
2827

29-
import org.springframework.boot.actuate.endpoint.web.servlet.WebMvcEndpointHandlerMapping;
3028
import org.springframework.util.AntPathMatcher;
31-
import org.springframework.web.method.HandlerMethod;
32-
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
3329

3430

35-
public class ActuatorProvider {
31+
public interface ActuatorProvider {
3632

37-
private final WebMvcEndpointHandlerMapping webMvcEndpointHandlerMapping;
33+
Map getMethods();
3834

39-
public ActuatorProvider(WebMvcEndpointHandlerMapping webMvcEndpointHandlerMapping) {
40-
this.webMvcEndpointHandlerMapping = webMvcEndpointHandlerMapping;
41-
}
42-
43-
public Map<RequestMappingInfo, HandlerMethod> getMethods() {
44-
return webMvcEndpointHandlerMapping.getHandlerMethods();
45-
}
46-
47-
public Tag getTag() {
35+
default Tag getTag() {
4836
Tag actuatorTag = new Tag();
4937
actuatorTag.setName(Constants.SPRINGDOC_ACTUATOR_TAG);
5038
actuatorTag.setDescription(Constants.SPRINGDOC_ACTUATOR_DESCRIPTION);
@@ -56,7 +44,7 @@ public Tag getTag() {
5644
return actuatorTag;
5745
}
5846

59-
public boolean isRestController(String operationPath) {
47+
default boolean isRestController(String operationPath) {
6048
return operationPath.startsWith(AntPathMatcher.DEFAULT_PATH_SEPARATOR);
6149
}
6250

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
*
3+
* *
4+
* * *
5+
* * * * Copyright 2019-2020 the original author or authors.
6+
* * * *
7+
* * * * Licensed under the Apache License, Version 2.0 (the "License");
8+
* * * * you may not use this file except in compliance with the License.
9+
* * * * You may obtain a copy of the License at
10+
* * * *
11+
* * * * https://www.apache.org/licenses/LICENSE-2.0
12+
* * * *
13+
* * * * Unless required by applicable law or agreed to in writing, software
14+
* * * * distributed under the License is distributed on an "AS IS" BASIS,
15+
* * * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* * * * See the License for the specific language governing permissions and
17+
* * * * limitations under the License.
18+
* * *
19+
* *
20+
*
21+
*
22+
*/
23+
24+
package org.springdoc.core.customizers;
25+
26+
import io.swagger.v3.oas.models.Operation;
27+
import org.springdoc.core.ActuatorProvider;
28+
29+
import org.springframework.web.method.HandlerMethod;
30+
31+
public class ActuatorOperationCustomizer implements OperationCustomizer {
32+
33+
private final ActuatorProvider actuatorProvider;
34+
private int methodCount;
35+
36+
public ActuatorOperationCustomizer(ActuatorProvider actuatorProvider) {
37+
this.actuatorProvider = actuatorProvider;
38+
}
39+
40+
@Override
41+
public Operation customize(Operation operation, HandlerMethod handlerMethod) {
42+
if (operation.getTags() != null && operation.getTags().contains(actuatorProvider.getTag().getName())) {
43+
operation.setSummary(handlerMethod.toString());
44+
operation.setOperationId(operation.getOperationId() + "_" + methodCount++);
45+
}
46+
return operation;
47+
}
48+
}

springdoc-openapi-webflux-core/pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,12 @@
1818
<groupId>org.springframework</groupId>
1919
<artifactId>spring-webflux</artifactId>
2020
</dependency>
21+
<!-- Actuator dependencies -->
22+
<dependency>
23+
<groupId>org.springframework.boot</groupId>
24+
<artifactId>spring-boot-starter-actuator</artifactId>
25+
<optional>true</optional>
26+
</dependency>
2127
<dependency>
2228
<groupId>org.hibernate.validator</groupId>
2329
<artifactId>hibernate-validator</artifactId>

springdoc-openapi-webflux-core/src/main/java/org/springdoc/webflux/api/MultipleOpenApiResource.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import com.fasterxml.jackson.core.JsonProcessingException;
2929
import io.swagger.v3.oas.annotations.Operation;
3030
import org.springdoc.core.AbstractRequestBuilder;
31+
import org.springdoc.core.ActuatorProvider;
3132
import org.springdoc.core.GenericResponseBuilder;
3233
import org.springdoc.core.GroupedOpenApi;
3334
import org.springdoc.core.OpenAPIBuilder;
@@ -70,11 +71,13 @@ public class MultipleOpenApiResource implements InitializingBean {
7071

7172
private Map<String, OpenApiResource> groupedOpenApiResources;
7273

74+
private Optional<ActuatorProvider> actuatorProvider;
75+
7376
public MultipleOpenApiResource(List<GroupedOpenApi> groupedOpenApis,
7477
ObjectFactory<OpenAPIBuilder> defaultOpenAPIBuilder, AbstractRequestBuilder requestBuilder,
7578
GenericResponseBuilder responseBuilder, OperationBuilder operationParser,
7679
RequestMappingInfoHandlerMapping requestMappingHandlerMapping,
77-
SpringDocConfigProperties springDocConfigProperties) {
80+
SpringDocConfigProperties springDocConfigProperties, Optional<ActuatorProvider> actuatorProvider) {
7881

7982
this.groupedOpenApis = groupedOpenApis;
8083
this.defaultOpenAPIBuilder = defaultOpenAPIBuilder;
@@ -83,6 +86,7 @@ public MultipleOpenApiResource(List<GroupedOpenApi> groupedOpenApis,
8386
this.operationParser = operationParser;
8487
this.requestMappingHandlerMapping = requestMappingHandlerMapping;
8588
this.springDocConfigProperties = springDocConfigProperties;
89+
this.actuatorProvider = actuatorProvider;
8690
}
8791

8892
@Override
@@ -99,7 +103,9 @@ public void afterPropertiesSet() throws Exception {
99103
operationParser,
100104
requestMappingHandlerMapping,
101105
Optional.of(item.getOperationCustomizers()),
102-
Optional.of(item.getOpenApiCustomisers()), springDocConfigProperties
106+
Optional.of(item.getOpenApiCustomisers()),
107+
springDocConfigProperties,
108+
actuatorProvider
103109
);
104110
}
105111
));

springdoc-openapi-webflux-core/src/main/java/org/springdoc/webflux/api/OpenApiResource.java

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
package org.springdoc.webflux.api;
2222

23+
import java.util.HashSet;
2324
import java.util.LinkedHashMap;
2425
import java.util.List;
2526
import java.util.Map;
@@ -34,6 +35,7 @@
3435
import io.swagger.v3.oas.models.OpenAPI;
3536
import org.springdoc.api.AbstractOpenApiResource;
3637
import org.springdoc.core.AbstractRequestBuilder;
38+
import org.springdoc.core.ActuatorProvider;
3739
import org.springdoc.core.GenericResponseBuilder;
3840
import org.springdoc.core.OpenAPIBuilder;
3941
import org.springdoc.core.OperationBuilder;
@@ -73,8 +75,10 @@ public OpenApiResource(String groupName, OpenAPIBuilder openAPIBuilder, Abstract
7375
GenericResponseBuilder responseBuilder, OperationBuilder operationParser,
7476
RequestMappingInfoHandlerMapping requestMappingHandlerMapping,
7577
Optional<List<OperationCustomizer>> operationCustomizers,
76-
Optional<List<OpenApiCustomiser>> openApiCustomisers, SpringDocConfigProperties springDocConfigProperties) {
77-
super(groupName, openAPIBuilder, requestBuilder, responseBuilder, operationParser, operationCustomizers, openApiCustomisers, springDocConfigProperties);
78+
Optional<List<OpenApiCustomiser>> openApiCustomisers,
79+
SpringDocConfigProperties springDocConfigProperties,
80+
Optional<ActuatorProvider> actuatorProvider) {
81+
super(groupName, openAPIBuilder, requestBuilder, responseBuilder, operationParser, operationCustomizers, openApiCustomisers, springDocConfigProperties,actuatorProvider);
7882
this.requestMappingHandlerMapping = requestMappingHandlerMapping;
7983
}
8084

@@ -83,8 +87,10 @@ public OpenApiResource(OpenAPIBuilder openAPIBuilder, AbstractRequestBuilder req
8387
GenericResponseBuilder responseBuilder, OperationBuilder operationParser,
8488
RequestMappingInfoHandlerMapping requestMappingHandlerMapping,
8589
Optional<List<OperationCustomizer>> operationCustomizers,
86-
Optional<List<OpenApiCustomiser>> openApiCustomisers, SpringDocConfigProperties springDocConfigProperties) {
87-
super(DEFAULT_GROUP_NAME, openAPIBuilder, requestBuilder, responseBuilder, operationParser, operationCustomizers, openApiCustomisers, springDocConfigProperties);
90+
Optional<List<OpenApiCustomiser>> openApiCustomisers,
91+
SpringDocConfigProperties springDocConfigProperties,
92+
Optional<ActuatorProvider> actuatorProvider) {
93+
super(DEFAULT_GROUP_NAME, openAPIBuilder, requestBuilder, responseBuilder, operationParser, operationCustomizers, openApiCustomisers, springDocConfigProperties,actuatorProvider);
8894
this.requestMappingHandlerMapping = requestMappingHandlerMapping;
8995
}
9096

@@ -115,6 +121,16 @@ public Mono<String> openapiYaml(ServerHttpRequest serverHttpRequest,
115121
@Override
116122
protected void getPaths(Map<String, Object> restControllers) {
117123
Map<RequestMappingInfo, HandlerMethod> map = requestMappingHandlerMapping.getHandlerMethods();
124+
calculatePath(restControllers, map);
125+
if (actuatorProvider.isPresent()) {
126+
map = actuatorProvider.get().getMethods();
127+
this.openAPIBuilder.addTag(new HashSet<>(map.values()), actuatorProvider.get().getTag());
128+
calculatePath(restControllers, map);
129+
}
130+
getWebFluxRouterFunctionPaths();
131+
}
132+
133+
protected void calculatePath(Map<String, Object> restControllers, Map<RequestMappingInfo, HandlerMethod> map) {
118134
for (Map.Entry<RequestMappingInfo, HandlerMethod> entry : map.entrySet()) {
119135
RequestMappingInfo requestMappingInfo = entry.getKey();
120136
HandlerMethod handlerMethod = entry.getValue();
@@ -125,7 +141,8 @@ protected void getPaths(Map<String, Object> restControllers) {
125141
Map<String, String> regexMap = new LinkedHashMap<>();
126142
operationPath = PathUtils.parsePath(operationPath, regexMap);
127143
if (operationPath.startsWith(DEFAULT_PATH_SEPARATOR)
128-
&& restControllers.containsKey(handlerMethod.getBean().toString()) && isPackageToScan(handlerMethod.getBeanType().getPackage()) && isPathToMatch(operationPath)) {
144+
&& (restControllers.containsKey(handlerMethod.getBean().toString()) || actuatorProvider.isPresent())
145+
&& isPackageToScan(handlerMethod.getBeanType().getPackage()) && isPathToMatch(operationPath)) {
129146
Set<RequestMethod> requestMethods = requestMappingInfo.getMethodsCondition().getMethods();
130147
// default allowed requestmethods
131148
if (requestMethods.isEmpty())
@@ -134,7 +151,6 @@ protected void getPaths(Map<String, Object> restControllers) {
134151
}
135152
}
136153
}
137-
getWebFluxRouterFunctionPaths();
138154
}
139155

140156
protected void getWebFluxRouterFunctionPaths() {

springdoc-openapi-webflux-core/src/main/java/org/springdoc/webflux/core/MultipleOpenApiWebFluxConfiguration.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,10 @@
2121
package org.springdoc.webflux.core;
2222

2323
import java.util.List;
24+
import java.util.Optional;
2425

2526
import org.springdoc.core.AbstractRequestBuilder;
27+
import org.springdoc.core.ActuatorProvider;
2628
import org.springdoc.core.GenericResponseBuilder;
2729
import org.springdoc.core.GroupedOpenApi;
2830
import org.springdoc.core.MultipleOpenApiSupportCondition;
@@ -56,10 +58,14 @@ public class MultipleOpenApiWebFluxConfiguration {
5658
MultipleOpenApiResource multipleOpenApiResource(List<GroupedOpenApi> groupedOpenApis,
5759
ObjectFactory<OpenAPIBuilder> defaultOpenAPIBuilder, AbstractRequestBuilder requestBuilder,
5860
GenericResponseBuilder responseBuilder, OperationBuilder operationParser,
59-
RequestMappingInfoHandlerMapping requestMappingHandlerMapping, SpringDocConfigProperties springDocConfigProperties) {
61+
RequestMappingInfoHandlerMapping requestMappingHandlerMapping,
62+
SpringDocConfigProperties springDocConfigProperties,
63+
Optional<ActuatorProvider> actuatorProvider) {
6064
return new MultipleOpenApiResource(groupedOpenApis,
6165
defaultOpenAPIBuilder, requestBuilder,
6266
responseBuilder, operationParser,
63-
requestMappingHandlerMapping, springDocConfigProperties);
67+
requestMappingHandlerMapping,
68+
springDocConfigProperties,
69+
actuatorProvider);
6470
}
6571
}

springdoc-openapi-webflux-core/src/main/java/org/springdoc/webflux/core/SpringDocWebFluxConfiguration.java

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import java.util.Optional;
2525

2626
import org.springdoc.core.AbstractRequestBuilder;
27+
import org.springdoc.core.ActuatorProvider;
2728
import org.springdoc.core.GenericParameterBuilder;
2829
import org.springdoc.core.GenericResponseBuilder;
2930
import org.springdoc.core.OpenAPIBuilder;
@@ -32,12 +33,17 @@
3233
import org.springdoc.core.RequestBodyBuilder;
3334
import org.springdoc.core.ReturnTypeParser;
3435
import org.springdoc.core.SpringDocConfigProperties;
36+
import org.springdoc.core.customizers.ActuatorOperationCustomizer;
3537
import org.springdoc.core.customizers.OpenApiCustomiser;
3638
import org.springdoc.core.customizers.OperationCustomizer;
3739
import org.springdoc.core.customizers.ParameterCustomizer;
3840
import org.springdoc.webflux.api.OpenApiResource;
3941
import org.springdoc.webflux.core.converters.WebFluxSupportConverter;
4042

43+
import org.springframework.boot.actuate.autoconfigure.web.server.ConditionalOnManagementPort;
44+
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementPortType;
45+
import org.springframework.boot.actuate.endpoint.web.reactive.WebFluxEndpointHandlerMapping;
46+
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
4147
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
4248
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
4349
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
@@ -48,6 +54,7 @@
4854
import org.springframework.web.reactive.result.method.RequestMappingInfoHandlerMapping;
4955

5056
import static org.springdoc.core.Constants.SPRINGDOC_ENABLED;
57+
import static org.springdoc.core.Constants.SPRINGDOC_SHOW_ACTUATOR;
5158

5259
@Configuration
5360
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
@@ -61,11 +68,13 @@ OpenApiResource openApiResource(OpenAPIBuilder openAPIBuilder, AbstractRequestBu
6168
GenericResponseBuilder responseBuilder, OperationBuilder operationParser,
6269
RequestMappingInfoHandlerMapping requestMappingHandlerMapping,
6370
Optional<List<OperationCustomizer>> operationCustomizers,
64-
Optional<List<OpenApiCustomiser>> openApiCustomisers, SpringDocConfigProperties springDocConfigProperties) {
71+
Optional<List<OpenApiCustomiser>> openApiCustomisers,
72+
SpringDocConfigProperties springDocConfigProperties,
73+
Optional<ActuatorProvider> actuatorProvider) {
6574
return new OpenApiResource(openAPIBuilder, requestBuilder,
6675
responseBuilder, operationParser,
6776
requestMappingHandlerMapping,operationCustomizers,
68-
openApiCustomisers, springDocConfigProperties);
77+
openApiCustomisers, springDocConfigProperties,actuatorProvider);
6978
}
7079

7180
@Bean
@@ -90,4 +99,22 @@ GenericResponseBuilder responseBuilder(OperationBuilder operationBuilder, List<R
9099
WebFluxSupportConverter webFluxSupportConverter() {
91100
return new WebFluxSupportConverter();
92101
}
102+
103+
@ConditionalOnProperty(SPRINGDOC_SHOW_ACTUATOR)
104+
@ConditionalOnClass(WebFluxEndpointHandlerMapping.class)
105+
@ConditionalOnManagementPort(ManagementPortType.SAME)
106+
static class SpringDocWebFluxActuatorConfiguration {
107+
108+
@Bean
109+
@ConditionalOnMissingBean
110+
ActuatorProvider actuatorProvider(WebFluxEndpointHandlerMapping webFluxEndpointHandlerMapping) {
111+
return new WebFluxActuatorProvider(webFluxEndpointHandlerMapping);
112+
}
113+
114+
@Bean
115+
@Lazy(false)
116+
OperationCustomizer actuatorCustomizer(ActuatorProvider actuatorProvider) {
117+
return new ActuatorOperationCustomizer(actuatorProvider);
118+
}
119+
}
93120
}

0 commit comments

Comments
 (0)