Skip to content

Commit c4ddd4a

Browse files
committed
webflux integration
1 parent 59320da commit c4ddd4a

File tree

95 files changed

+7010
-1014
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

95 files changed

+7010
-1014
lines changed

springdoc-openapi-common/pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,5 +40,11 @@
4040
<groupId>org.apache.commons</groupId>
4141
<artifactId>commons-lang3</artifactId>
4242
</dependency>
43+
<!-- Actuator dependencies -->
44+
<dependency>
45+
<groupId>org.springframework.boot</groupId>
46+
<artifactId>spring-boot-starter-actuator</artifactId>
47+
<optional>true</optional>
48+
</dependency>
4349
</dependencies>
4450
</project>

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ public abstract class AbstractOpenApiResource extends SpecFilter {
135135
/**
136136
* The Actuator provider.
137137
*/
138-
protected final Optional<ActuatorProvider> actuatorProvider;
138+
protected final Optional<ActuatorProvider> optionalActuatorProvider;
139139

140140
/**
141141
* The Request builder.
@@ -170,7 +170,7 @@ public abstract class AbstractOpenApiResource extends SpecFilter {
170170
/**
171171
* The Group name.
172172
*/
173-
private final String groupName;
173+
protected final String groupName;
174174

175175
/**
176176
* Instantiates a new Abstract open api resource.
@@ -204,7 +204,7 @@ protected AbstractOpenApiResource(String groupName, ObjectFactory<OpenAPIService
204204
if (operationCustomizers.isPresent())
205205
operationCustomizers.get().removeIf(Objects::isNull);
206206
this.operationCustomizers = operationCustomizers;
207-
this.actuatorProvider = actuatorProvider;
207+
this.optionalActuatorProvider = actuatorProvider;
208208
if (springDocConfigProperties.isPreLoadingEnabled())
209209
Executors.newSingleThreadExecutor().execute(this::getOpenApi);
210210
}

springdoc-openapi-common/src/main/java/org/springdoc/core/ActuatorProvider.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,4 +71,16 @@ default boolean isRestController(String operationPath, Class<?> controllerClass
7171
&& !AbstractOpenApiResource.isHiddenRestControllers(controllerClass);
7272
}
7373

74+
int getApplicationPort();
75+
76+
int getActuatorPort();
77+
78+
String getActuatorPath();
79+
80+
boolean isUseManagementPort();
81+
82+
String getBasePath();
83+
84+
String getServletContextPath();
85+
7486
}

springdoc-openapi-common/src/main/java/org/springdoc/core/Constants.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,6 @@ public final class Constants {
9595
*/
9696
public static final String SPRINGDOC_SWAGGER_UI_ENABLED = "springdoc.swagger-ui.enabled";
9797

98-
9998
/**
10099
* The constant SPRINGDOC_USE_MANAGEMENT_PORT.
101100
*/
@@ -118,6 +117,8 @@ public final class Constants {
118117
*/
119118
public static final String MVC_SERVLET_PATH = "${spring.mvc.servlet.path" + NULL + "}";
120119

120+
public static final String ACTUATOR_DEFAULT_GROUP = "x-actuator";
121+
121122
/**
122123
* The constant SPRINGDOC_SHOW_ACTUATOR.
123124
*/

springdoc-openapi-common/src/main/java/org/springdoc/core/SpringDocConfiguration.java

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,13 @@
2727

2828
import com.fasterxml.jackson.databind.node.ObjectNode;
2929
import io.swagger.v3.core.converter.ModelConverter;
30+
import io.swagger.v3.oas.annotations.Hidden;
3031
import io.swagger.v3.oas.models.Components;
3132
import io.swagger.v3.oas.models.OpenAPI;
3233
import io.swagger.v3.oas.models.media.ObjectSchema;
3334
import io.swagger.v3.oas.models.media.Schema;
35+
import org.springdoc.api.ErrorMessage;
36+
import org.springdoc.api.OpenApiResourceNotFoundException;
3437
import org.springdoc.core.converters.AdditionalModelsConverter;
3538
import org.springdoc.core.converters.FileSupportConverter;
3639
import org.springdoc.core.converters.ModelConverterRegistrar;
@@ -56,6 +59,12 @@
5659
import org.springframework.context.annotation.Configuration;
5760
import org.springframework.context.annotation.Lazy;
5861
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
62+
import org.springframework.http.HttpStatus;
63+
import org.springframework.http.ResponseEntity;
64+
import org.springframework.web.bind.annotation.ExceptionHandler;
65+
import org.springframework.web.bind.annotation.ResponseStatus;
66+
import org.springframework.web.bind.annotation.RestControllerAdvice;
67+
import org.springframework.web.context.request.WebRequest;
5968
import org.springframework.web.context.request.async.DeferredResult;
6069

6170
import static org.springdoc.core.Constants.SPRINGDOC_DEPRECATING_CONVERTER_ENABLED;
@@ -309,7 +318,7 @@ static BeanFactoryPostProcessor springdocBeanFactoryPostProcessor() {
309318
*
310319
* @return the bean factory post processor
311320
*/
312-
// For spring-boot-1 compatibility
321+
// For spring-boot-1 compatibility
313322
@Bean
314323
@Conditional(CacheOrGroupedOpenApiCondition.class)
315324
@ConditionalOnMissingClass(value = BINDRESULT_CLASS)
@@ -318,4 +327,24 @@ static BeanFactoryPostProcessor springdocBeanFactoryPostProcessor2() {
318327
return SpringdocBeanFactoryConfigurer::initBeanFactoryPostProcessor;
319328
}
320329

330+
/**
331+
* The type Open api resource advice.
332+
*/
333+
@RestControllerAdvice
334+
@Hidden
335+
class OpenApiResourceAdvice {
336+
/**
337+
* Handle no handler found response entity.
338+
*
339+
* @param e the e
340+
* @param request the request
341+
* @return the response entity
342+
*/
343+
@ExceptionHandler(OpenApiResourceNotFoundException.class)
344+
@ResponseStatus(HttpStatus.NOT_FOUND)
345+
public ResponseEntity<ErrorMessage> handleNoHandlerFound(OpenApiResourceNotFoundException e, WebRequest request) {
346+
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(new ErrorMessage(e.getMessage()));
347+
}
348+
}
349+
321350
}
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
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;
25+
26+
import java.util.ArrayList;
27+
import java.util.List;
28+
29+
import io.swagger.v3.oas.models.OpenAPI;
30+
31+
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
32+
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
33+
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties;
34+
import org.springframework.boot.context.properties.bind.BindResult;
35+
import org.springframework.boot.context.properties.bind.Binder;
36+
import org.springframework.context.EnvironmentAware;
37+
import org.springframework.core.env.Environment;
38+
import org.springframework.lang.Nullable;
39+
40+
import static org.springdoc.core.Constants.ACTUATOR_DEFAULT_GROUP;
41+
import static org.springdoc.core.Constants.DEFAULT_GROUP_NAME;
42+
import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_PROTOTYPE;
43+
44+
/**
45+
* The type Springdoc bean factory configurer.
46+
* @author bnasslahsen
47+
*/
48+
public class SpringdocActuatorBeanFactoryConfigurer implements EnvironmentAware, BeanFactoryPostProcessor {
49+
50+
/**
51+
* The Environment.
52+
*/
53+
@Nullable
54+
private Environment environment;
55+
56+
@Override
57+
public void setEnvironment(Environment environment) {
58+
this.environment = environment;
59+
}
60+
61+
private List<GroupedOpenApi> groupedOpenApis;
62+
63+
public SpringdocActuatorBeanFactoryConfigurer(List<GroupedOpenApi> groupedOpenApis) {
64+
this.groupedOpenApis = groupedOpenApis;
65+
}
66+
67+
@Override
68+
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
69+
final BindResult<WebEndpointProperties> result = Binder.get(environment)
70+
.bind("management.endpoints.web", WebEndpointProperties.class);
71+
if (result.isBound()) {
72+
WebEndpointProperties webEndpointProperties = result.get();
73+
74+
List<GroupedOpenApi> newGroups = new ArrayList<>();
75+
GroupedOpenApi actuatorGroup = GroupedOpenApi.builder().group(ACTUATOR_DEFAULT_GROUP)
76+
.pathsToMatch(webEndpointProperties.getBasePath() + "/**")
77+
.pathsToExclude(webEndpointProperties.getBasePath() + "/health/*")
78+
.build();
79+
// Add the actuator group
80+
newGroups.add(actuatorGroup);
81+
82+
if (groupedOpenApis.size() == 0) {
83+
GroupedOpenApi defaultGroup = GroupedOpenApi.builder().group(DEFAULT_GROUP_NAME)
84+
.pathsToMatch("/**")
85+
.pathsToExclude(webEndpointProperties.getBasePath() + "/**")
86+
.build();
87+
// Register the default group
88+
newGroups.add(defaultGroup);
89+
}
90+
91+
newGroups.forEach(elt -> beanFactory.registerSingleton(elt.getGroup(), elt));
92+
}
93+
initBeanFactoryPostProcessor(beanFactory);
94+
}
95+
96+
/**
97+
* Init bean factory post processor.
98+
*
99+
* @param beanFactory the bean factory
100+
*/
101+
public static void initBeanFactoryPostProcessor(ConfigurableListableBeanFactory beanFactory) {
102+
for (String beanName : beanFactory.getBeanNamesForType(OpenAPIService.class))
103+
beanFactory.getBeanDefinition(beanName).setScope(SCOPE_PROTOTYPE);
104+
for (String beanName : beanFactory.getBeanNamesForType(OpenAPI.class))
105+
beanFactory.getBeanDefinition(beanName).setScope(SCOPE_PROTOTYPE);
106+
}
107+
}

springdoc-openapi-common/src/main/java/org/springdoc/ui/AbstractSwaggerWelcome.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,10 +81,12 @@ public void afterPropertiesSet() {
8181
* @param docsUrl the docs url
8282
* @return the string
8383
*/
84-
protected String buildUrl(String contextPath, final String docsUrl) {
84+
protected String buildUrl(String contextPath, String docsUrl) {
8585
if (contextPath.endsWith(DEFAULT_PATH_SEPARATOR)) {
8686
return contextPath.substring(0, contextPath.length() - 1) + docsUrl;
8787
}
88+
if (!docsUrl.startsWith(DEFAULT_PATH_SEPARATOR))
89+
docsUrl = DEFAULT_PATH_SEPARATOR + docsUrl;
8890
return contextPath + docsUrl;
8991
}
9092

@@ -127,7 +129,8 @@ protected UriComponentsBuilder getUriComponentsBuilder(String sbUrl) {
127129
.filter(entry -> !entry.getKey().startsWith(SwaggerUiConfigParameters.URLS_PROPERTY))
128130
.filter(entry -> StringUtils.isNotEmpty((String) entry.getValue()))
129131
.forEach(entry -> uriBuilder.queryParam(entry.getKey(), entry.getValue()));
130-
} else if (swaggerUiConfig.isDisplayQueryParamsWithoutOauth2() && StringUtils.isNotEmpty(swaggerUiConfigParameters.getUrl())) {
132+
}
133+
else if (swaggerUiConfig.isDisplayQueryParamsWithoutOauth2() && StringUtils.isNotEmpty(swaggerUiConfigParameters.getUrl())) {
131134
swaggerUiConfigParameters.getConfigParameters().entrySet().stream()
132135
.filter(entry -> !SwaggerUiConfigParameters.CONFIG_URL_PROPERTY.equals(entry.getKey()))
133136
.filter(entry -> !SwaggerUiConfigParameters.OAUTH2_REDIRECT_URL_PROPERTY.equals(entry.getKey()))

springdoc-openapi-ui/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,5 +38,10 @@
3838
<artifactId>spring-boot-starter-actuator</artifactId>
3939
<optional>true</optional>
4040
</dependency>
41+
<dependency>
42+
<groupId>org.apache.tomcat.embed</groupId>
43+
<artifactId>tomcat-embed-core</artifactId>
44+
<scope>test</scope>
45+
</dependency>
4146
</dependencies>
4247
</project>

springdoc-openapi-ui/src/main/java/org/springdoc/webmvc/ui/SwaggerActuatorWelcome.java renamed to springdoc-openapi-ui/src/main/java/org/springdoc/webmvc/ui/ActuatorSwaggerWelcome.java

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@
55
import javax.servlet.http.HttpServletRequest;
66

77
import io.swagger.v3.oas.annotations.Operation;
8+
import org.apache.commons.lang3.ArrayUtils;
89
import org.apache.commons.lang3.StringUtils;
910
import org.springdoc.core.SpringDocConfigProperties;
1011
import org.springdoc.core.SwaggerUiConfigParameters;
1112
import org.springdoc.core.SwaggerUiConfigProperties;
13+
import org.springdoc.ui.AbstractSwaggerWelcome;
1214

1315
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties;
1416
import org.springframework.boot.actuate.endpoint.web.annotation.ControllerEndpoint;
@@ -30,15 +32,14 @@
3032
* The type Swagger actuator welcome.
3133
*/
3234
@ControllerEndpoint(id = DEFAULT_SWAGGER_UI_ACTUATOR_PATH)
33-
public class SwaggerActuatorWelcome extends SwaggerWelcome {
35+
public class ActuatorSwaggerWelcome extends AbstractSwaggerWelcome {
3436

3537
/**
3638
* The Web endpoint properties.
3739
*/
3840
private WebEndpointProperties webEndpointProperties;
3941

40-
public static final String SWAGGER_CONFIG_ACTUATOR_URL = DEFAULT_PATH_SEPARATOR + SWAGGGER_CONFIG_FILE;
41-
42+
private static final String SWAGGER_CONFIG_ACTUATOR_URL = DEFAULT_PATH_SEPARATOR + SWAGGGER_CONFIG_FILE;
4243

4344
/**
4445
* Instantiates a new Swagger welcome.
@@ -47,7 +48,7 @@ public class SwaggerActuatorWelcome extends SwaggerWelcome {
4748
* @param swaggerUiConfigParameters the swagger ui config parameters
4849
* @param webEndpointProperties the web endpoint properties
4950
*/
50-
public SwaggerActuatorWelcome(SwaggerUiConfigProperties swaggerUiConfig, SpringDocConfigProperties springDocConfigProperties, SwaggerUiConfigParameters swaggerUiConfigParameters, WebEndpointProperties webEndpointProperties) {
51+
public ActuatorSwaggerWelcome(SwaggerUiConfigProperties swaggerUiConfig, SpringDocConfigProperties springDocConfigProperties, SwaggerUiConfigParameters swaggerUiConfigParameters, WebEndpointProperties webEndpointProperties) {
5152
super(swaggerUiConfig, springDocConfigProperties, swaggerUiConfigParameters);
5253
this.webEndpointProperties = webEndpointProperties;
5354
}
@@ -81,14 +82,27 @@ public Map<String, Object> openapiJson(HttpServletRequest request) {
8182
return swaggerUiConfigParameters.getConfigParameters();
8283
}
8384

85+
@Override
86+
protected void calculateUiRootPath(StringBuilder... sbUrls) {
87+
StringBuilder sbUrl = new StringBuilder();
88+
sbUrl.append(webEndpointProperties.getBasePath());
89+
if (ArrayUtils.isNotEmpty(sbUrls))
90+
sbUrl = sbUrls[0];
91+
String swaggerPath = swaggerUiConfigParameters.getPath();
92+
if (swaggerPath.contains(DEFAULT_PATH_SEPARATOR))
93+
sbUrl.append(swaggerPath, 0, swaggerPath.lastIndexOf(DEFAULT_PATH_SEPARATOR));
94+
swaggerUiConfigParameters.setUiRootPath(sbUrl.toString());
95+
}
96+
8497

8598
protected void buildConfigUrl(String contextPath, UriComponentsBuilder uriComponentsBuilder) {
8699
String apiDocsUrl = DEFAULT_API_DOCS_ACTUATOR_URL;
87100
if (StringUtils.isEmpty(swaggerUiConfig.getConfigUrl())) {
88-
String url = webEndpointProperties.getBasePath() + DEFAULT_PATH_SEPARATOR + buildUrl(contextPath, apiDocsUrl);
89-
String swaggerConfigUrl = webEndpointProperties.getBasePath()
101+
String url = buildUrl(contextPath + webEndpointProperties.getBasePath(), apiDocsUrl);
102+
String swaggerConfigUrl = contextPath + webEndpointProperties.getBasePath()
90103
+ DEFAULT_PATH_SEPARATOR + DEFAULT_SWAGGER_UI_ACTUATOR_PATH
91104
+ DEFAULT_PATH_SEPARATOR + SWAGGGER_CONFIG_FILE;
105+
92106
swaggerUiConfigParameters.setConfigUrl(swaggerConfigUrl);
93107
if (CollectionUtils.isEmpty(swaggerUiConfigParameters.getUrls())) {
94108
String swaggerUiUrl = swaggerUiConfig.getUrl();
@@ -103,4 +117,9 @@ protected void buildConfigUrl(String contextPath, UriComponentsBuilder uriCompon
103117
calculateOauth2RedirectUrl(uriComponentsBuilder);
104118
}
105119

120+
@Override
121+
protected void calculateOauth2RedirectUrl(UriComponentsBuilder uriComponentsBuilder) {
122+
if (!swaggerUiConfigParameters.isValidUrl(swaggerUiConfigParameters.getOauth2RedirectUrl()))
123+
swaggerUiConfigParameters.setOauth2RedirectUrl(uriComponentsBuilder.path(swaggerUiConfigParameters.getUiRootPath()).path(swaggerUiConfigParameters.getOauth2RedirectUrl()).build().toString());
124+
}
106125
}

0 commit comments

Comments
 (0)