Skip to content

Commit 60a1ae7

Browse files
author
bnasslahsen
committed
Make springdoc cache configurable. Fixes #331
1 parent 7b0a004 commit 60a1ae7

File tree

16 files changed

+242
-49
lines changed

16 files changed

+242
-49
lines changed

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

+6-4
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,7 @@
2929
import java.util.stream.Collectors;
3030
import java.util.stream.Stream;
3131

32-
import static org.springdoc.core.Constants.SPRINGDOC_PACKAGES_TO_SCAN;
33-
import static org.springdoc.core.Constants.SPRINGDOC_PATHS_TO_MATCH;
32+
import static org.springdoc.core.Constants.*;
3433

3534
public abstract class AbstractOpenApiResource {
3635

@@ -46,6 +45,8 @@ public abstract class AbstractOpenApiResource {
4645
private List<String> packagesToScan;
4746
@Value(SPRINGDOC_PATHS_TO_MATCH)
4847
private List<String> pathsToMatch;
48+
@Value(SPRINGDOC_CACHE_DISABLED_VALUE)
49+
private boolean cacheDisabled;
4950

5051
protected AbstractOpenApiResource(OpenAPIBuilder openAPIBuilder, AbstractRequestBuilder requestBuilder,
5152
AbstractResponseBuilder responseBuilder, OperationBuilder operationParser,
@@ -60,7 +61,7 @@ protected AbstractOpenApiResource(OpenAPIBuilder openAPIBuilder, AbstractRequest
6061

6162
protected AbstractOpenApiResource(OpenAPIBuilder openAPIBuilder, AbstractRequestBuilder requestBuilder,
6263
AbstractResponseBuilder responseBuilder, OperationBuilder operationParser,
63-
Optional<List<OpenApiCustomiser>> openApiCustomisers, List<String> pathsToMatch, List<String> packagesToScan) {
64+
Optional<List<OpenApiCustomiser>> openApiCustomisers, List<String> pathsToMatch, List<String> packagesToScan,boolean cacheDisabled) {
6465
super();
6566
this.openAPIBuilder = openAPIBuilder;
6667
this.requestBuilder = requestBuilder;
@@ -69,11 +70,12 @@ protected AbstractOpenApiResource(OpenAPIBuilder openAPIBuilder, AbstractRequest
6970
this.openApiCustomisers = openApiCustomisers;
7071
this.pathsToMatch = pathsToMatch;
7172
this.packagesToScan = packagesToScan;
73+
this.cacheDisabled=cacheDisabled;
7274
}
7375

7476
protected synchronized OpenAPI getOpenApi() {
7577
OpenAPI openApi;
76-
if (!computeDone) {
78+
if (!computeDone || cacheDisabled) {
7779
Instant start = Instant.now();
7880
openAPIBuilder.build();
7981
Map<String, Object> restControllersMap = openAPIBuilder.getRestControllersMap();

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

+4-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,11 @@ public final class Constants {
1111
public static final String SWAGGER_CONFIG_URL = API_DOCS_URL + DEFAULT_PATH_SEPARATOR + SWAGGGER_CONFIG_FILE;
1212
public static final String DEFAULT_API_DOCS_URL_YAML = API_DOCS_URL + ".yaml";
1313
public static final String SPRINGDOC_ENABLED = "springdoc.api-docs.enabled";
14+
public static final String SPRINGDOC_CACHE_DISABLED = "springdoc.cache.disabled";
15+
public static final String SPRINGDOC_CACHE_DISABLED_VALUE= "${" + SPRINGDOC_CACHE_DISABLED + ":false}";
1416
public static final String SPRINGDOC_SWAGGER_UI_ENABLED = "springdoc.swagger-ui.enabled";
17+
public static final String SPRINGDOC_SWAGGER_UI_CONFIG_URL ="springdoc.swagger-ui.configUrl";
18+
public static final String SPRINGDOC_SWAGGER_UI_CONFIG_URL_VALUE ="${" + SPRINGDOC_SWAGGER_UI_CONFIG_URL + ":#{null}}";
1519
public static final String SPRINGDOC_SHOW_ACTUATOR = "springdoc.show-actuator";
1620
public static final String SPRINGDOC_SHOW_ACTUATOR_VALUE = "${" + SPRINGDOC_SHOW_ACTUATOR + ":false}";
1721
public static final String SPRINGDOC_PACKAGES_TO_SCAN = "${springdoc.packagesToScan:#{null}}";
@@ -34,7 +38,6 @@ public final class Constants {
3438
public static final String HEAD_METHOD = "head";
3539
public static final String OPTIONS_METHOD = "options";
3640
public static final String QUERY_PARAM = "query";
37-
3841
public static final String DEFAULT_DESCRIPTION = "default response";
3942
public static final String DEFAULT_TITLE = "OpenAPI definition";
4043
public static final String DEFAULT_VERSION = "v0";

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

+11-3
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ public class OpenAPIBuilder {
3737

3838
private static final Logger LOGGER = LoggerFactory.getLogger(OpenAPIBuilder.class);
3939
private final OpenAPI openAPI;
40+
private boolean isServersPresent = false;
4041
private final ApplicationContext context;
4142
private final SecurityParser securityParser;
4243
private final Map<HandlerMethod, String> springdocTags = new HashMap<>();
@@ -50,6 +51,8 @@ public class OpenAPIBuilder {
5051
this.openAPI.setComponents(new Components());
5152
if (this.openAPI.getPaths() == null)
5253
this.openAPI.setPaths(new Paths());
54+
if (!CollectionUtils.isEmpty(this.openAPI.getServers()))
55+
this.isServersPresent = true;
5356
} else {
5457
this.openAPI = new OpenAPI();
5558
this.openAPI.setComponents(new Components());
@@ -93,9 +96,11 @@ else if (openAPI.getInfo() == null) {
9396
openAPI.setInfo(infos);
9497
}
9598
// default server value
96-
if (CollectionUtils.isEmpty(openAPI.getServers())) {
99+
if (CollectionUtils.isEmpty(openAPI.getServers()) || !isServersPresent) {
97100
Server server = new Server().url(serverBaseUrl).description(DEFAULT_SERVER_DESCRIPTION);
98-
openAPI.addServersItem(server);
101+
List servers = new ArrayList();
102+
servers.add(server);
103+
openAPI.setServers(servers);
99104
}
100105
// add security schemes
101106
this.calculateSecuritySchemes(openAPI.getComponents());
@@ -195,7 +200,10 @@ private void buildOpenAPIWithOpenAPIDefinition(OpenAPI openAPI, OpenAPIDefinitio
195200
// OpenApiDefinition tags
196201
AnnotationsUtils.getTags(apiDef.tags(), false).ifPresent(tags -> openAPI.setTags(new ArrayList<>(tags)));
197202
// OpenApiDefinition servers
198-
openAPI.setServers(AnnotationsUtils.getServers(apiDef.servers()).orElse(null));
203+
if(AnnotationsUtils.getServers(apiDef.servers()).isPresent()){
204+
openAPI.setServers(AnnotationsUtils.getServers(apiDef.servers()).get());
205+
this.isServersPresent=true;
206+
}
199207
// OpenApiDefinition extensions
200208
if (apiDef.extensions().length > 0) {
201209
openAPI.setExtensions(AnnotationsUtils.getExtensions(apiDef.extensions()));

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

+33-2
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@
66
import org.springdoc.core.converters.ObjectNodeConverter;
77
import org.springdoc.core.converters.PropertyCustomizingConverter;
88
import org.springdoc.core.customizers.PropertyCustomizer;
9+
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
910
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
10-
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
11-
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
11+
import org.springframework.boot.autoconfigure.condition.*;
1212
import org.springframework.context.ApplicationContext;
1313
import org.springframework.context.annotation.Bean;
1414
import org.springframework.context.annotation.Configuration;
@@ -18,6 +18,7 @@
1818
import java.util.List;
1919
import java.util.Optional;
2020

21+
import static org.springdoc.core.Constants.SPRINGDOC_CACHE_DISABLED;
2122
import static org.springdoc.core.Constants.SPRINGDOC_ENABLED;
2223

2324
@Configuration
@@ -78,4 +79,34 @@ public SecurityParser securityParser(PropertyResolverUtils propertyResolverUtils
7879
return new SecurityParser(propertyResolverUtils);
7980
}
8081

82+
static class ConditionOnCacheOrGroupedOpenApi extends AnyNestedCondition {
83+
84+
ConditionOnCacheOrGroupedOpenApi() {
85+
super(ConfigurationPhase.REGISTER_BEAN);
86+
}
87+
88+
@Bean
89+
@ConditionalOnBean(GroupedOpenApi.class)
90+
public BeanFactoryPostProcessor beanFactoryPostProcessor1() {
91+
return getBeanFactoryPostProcessor();
92+
}
93+
94+
@Bean
95+
@ConditionalOnProperty(name = SPRINGDOC_CACHE_DISABLED)
96+
@ConditionalOnMissingBean(GroupedOpenApi.class)
97+
public BeanFactoryPostProcessor beanFactoryPostProcessor2() {
98+
return getBeanFactoryPostProcessor();
99+
}
100+
101+
private BeanFactoryPostProcessor getBeanFactoryPostProcessor() {
102+
return beanFactory -> {
103+
for (String beanName : beanFactory.getBeanNamesForType(OpenAPIBuilder.class)) {
104+
beanFactory.getBeanDefinition(beanName).setScope("prototype");
105+
}
106+
for (String beanName : beanFactory.getBeanNamesForType(OpenAPI.class)) {
107+
beanFactory.getBeanDefinition(beanName).setScope("prototype");
108+
}
109+
};
110+
}
111+
}
81112
}

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

+1-2
Original file line numberDiff line numberDiff line change
@@ -134,8 +134,7 @@ public static void setSwaggerUrls(List<SwaggerUrl> swaggerUrls) {
134134
public static void addUrl(String url) {
135135
swaggerUrls.forEach(elt ->
136136
{
137-
if (StringUtils.isEmpty(elt.url))
138-
elt.setUrl(url + DEFAULT_PATH_SEPARATOR + elt.getName());
137+
elt.setUrl(url + DEFAULT_PATH_SEPARATOR + elt.getName());
139138
});
140139
}
141140

springdoc-openapi-ui/src/main/java/org/springdoc/ui/SwaggerWelcome.java

+4-1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ class SwaggerWelcome implements InitializingBean {
3737
@Value(MVC_SERVLET_PATH)
3838
private String mvcServletPath;
3939

40+
@Value(SPRINGDOC_SWAGGER_UI_CONFIG_URL_VALUE)
41+
private String originConfigUrl;
42+
4043
@Autowired
4144
private SwaggerUiConfigProperties swaggerUiConfig;
4245

@@ -77,7 +80,7 @@ private String buildUrl(final HttpServletRequest request, final String docsUrl)
7780
}
7881

7982
private void buildConfigUrl(HttpServletRequest request) {
80-
if (StringUtils.isEmpty(swaggerUiConfig.getConfigUrl())) {
83+
if (StringUtils.isEmpty(originConfigUrl)) {
8184
String url = buildUrl(request, apiDocsUrl);
8285
String swaggerConfigUrl = url + DEFAULT_PATH_SEPARATOR + SWAGGGER_CONFIG_FILE;
8386
swaggerUiConfig.setConfigUrl(swaggerConfigUrl);

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

+4-1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ public class MultipleOpenApiResource implements InitializingBean {
3232
private final OperationBuilder operationParser;
3333
private final RequestMappingInfoHandlerMapping requestMappingHandlerMapping;
3434
private Map<String, OpenApiResource> groupedOpenApiResources;
35+
@Value(SPRINGDOC_CACHE_DISABLED_VALUE)
36+
private boolean cacheDisabled;
3537

3638
public MultipleOpenApiResource(List<GroupedOpenApi> groupedOpenApis,
3739
ObjectFactory<OpenAPIBuilder> defaultOpenAPIBuilder, AbstractRequestBuilder requestBuilder,
@@ -56,7 +58,8 @@ public void afterPropertiesSet() throws Exception {
5658
responseBuilder,
5759
operationParser,
5860
requestMappingHandlerMapping,
59-
Optional.of(item.getOpenApiCustomisers()), item.getPathsToMatch(), item.getPackagesToScan()
61+
Optional.of(item.getOpenApiCustomisers()), item.getPathsToMatch(), item.getPackagesToScan(),
62+
cacheDisabled
6063
)
6164
));
6265
}

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,8 @@ public OpenApiResource(OpenAPIBuilder openAPIBuilder, AbstractRequestBuilder req
4545
public OpenApiResource(OpenAPIBuilder openAPIBuilder, AbstractRequestBuilder requestBuilder,
4646
AbstractResponseBuilder responseBuilder, OperationBuilder operationParser,
4747
RequestMappingInfoHandlerMapping requestMappingHandlerMapping,
48-
Optional<List<OpenApiCustomiser>> openApiCustomisers, List<String> pathsToMatch, List<String> packagesToScan) {
49-
super(openAPIBuilder, requestBuilder, responseBuilder, operationParser, openApiCustomisers, pathsToMatch, packagesToScan);
48+
Optional<List<OpenApiCustomiser>> openApiCustomisers, List<String> pathsToMatch, List<String> packagesToScan, boolean cacheDisabled) {
49+
super(openAPIBuilder, requestBuilder, responseBuilder, operationParser, openApiCustomisers, pathsToMatch, packagesToScan, cacheDisabled);
5050
this.requestMappingHandlerMapping = requestMappingHandlerMapping;
5151
}
5252

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

-14
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
package org.springdoc.core;
22

3-
import io.swagger.v3.oas.models.OpenAPI;
43
import org.springdoc.api.MultipleOpenApiResource;
54
import org.springframework.beans.factory.ObjectFactory;
6-
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
75
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
86
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
97
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
@@ -22,18 +20,6 @@
2220
@ConditionalOnProperty(name = SPRINGDOC_ENABLED, matchIfMissing = true)
2321
public class MultipleOpenApiWebFluxConfiguration {
2422

25-
@Bean
26-
public BeanFactoryPostProcessor beanFactoryPostProcessor() {
27-
return beanFactory -> {
28-
for (String beanName : beanFactory.getBeanNamesForType(OpenAPI.class)) {
29-
beanFactory.getBeanDefinition(beanName).setScope("prototype");
30-
}
31-
for (String beanName : beanFactory.getBeanNamesForType(OpenAPIBuilder.class)) {
32-
beanFactory.getBeanDefinition(beanName).setScope("prototype");
33-
}
34-
};
35-
}
36-
3723
@Bean
3824
public MultipleOpenApiResource multipleOpenApiResource(List<GroupedOpenApi> groupedOpenApis,
3925
ObjectFactory<OpenAPIBuilder> defaultOpenAPIBuilder, AbstractRequestBuilder requestBuilder,

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

+4-1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ public class MultipleOpenApiResource implements InitializingBean {
3434
private Map<String, OpenApiResource> groupedOpenApiResources;
3535
@Value(SPRINGDOC_SHOW_ACTUATOR_VALUE)
3636
private boolean showActuator;
37+
@Value(SPRINGDOC_CACHE_DISABLED_VALUE)
38+
private boolean cacheDisabled;
3739

3840
public MultipleOpenApiResource(List<GroupedOpenApi> groupedOpenApis,
3941
ObjectFactory<OpenAPIBuilder> defaultOpenAPIBuilder, AbstractRequestBuilder requestBuilder,
@@ -61,7 +63,8 @@ public void afterPropertiesSet() throws Exception {
6163
requestMappingHandlerMapping,
6264
servletContextProvider,
6365
Optional.of(item.getOpenApiCustomisers()), item.getPathsToMatch(), item.getPackagesToScan(),
64-
showActuator
66+
showActuator,
67+
cacheDisabled
6568
)
6669
));
6770
}

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,8 @@ public OpenApiResource(OpenAPIBuilder openAPIBuilder, AbstractRequestBuilder req
5454
AbstractResponseBuilder responseBuilder, OperationBuilder operationParser,
5555
RequestMappingInfoHandlerMapping requestMappingHandlerMapping, Optional<ActuatorProvider> servletContextProvider,
5656
Optional<List<OpenApiCustomiser>> openApiCustomisers, List<String> pathsToMatch, List<String> packagesToScan,
57-
boolean showActuator) {
58-
super(openAPIBuilder, requestBuilder, responseBuilder, operationParser, openApiCustomisers, pathsToMatch, packagesToScan);
57+
boolean showActuator,boolean cacheDisabled) {
58+
super(openAPIBuilder, requestBuilder, responseBuilder, operationParser, openApiCustomisers, pathsToMatch, packagesToScan,cacheDisabled);
5959
this.requestMappingHandlerMapping = requestMappingHandlerMapping;
6060
this.servletContextProvider = servletContextProvider;
6161
this.showActuator = showActuator;

springdoc-openapi-webmvc-core/src/main/java/org/springdoc/core/MultipleOpenApiSupportConfiguration.java

-14
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
package org.springdoc.core;
22

3-
import io.swagger.v3.oas.models.OpenAPI;
43
import org.springdoc.api.ActuatorProvider;
54
import org.springdoc.api.MultipleOpenApiResource;
65
import org.springframework.beans.factory.ObjectFactory;
7-
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
86
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
97
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
108
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
@@ -24,18 +22,6 @@
2422
@ConditionalOnProperty(name = SPRINGDOC_ENABLED, matchIfMissing = true)
2523
public class MultipleOpenApiSupportConfiguration {
2624

27-
@Bean
28-
public BeanFactoryPostProcessor beanFactoryPostProcessor() {
29-
return beanFactory -> {
30-
for (String beanName : beanFactory.getBeanNamesForType(OpenAPI.class)) {
31-
beanFactory.getBeanDefinition(beanName).setScope("prototype");
32-
}
33-
for (String beanName : beanFactory.getBeanNamesForType(OpenAPIBuilder.class)) {
34-
beanFactory.getBeanDefinition(beanName).setScope("prototype");
35-
}
36-
};
37-
}
38-
3925
@Bean
4026
public MultipleOpenApiResource multipleOpenApiResource(List<GroupedOpenApi> groupedOpenApis,
4127
ObjectFactory<OpenAPIBuilder> defaultOpenAPIBuilder, AbstractRequestBuilder requestBuilder,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package test.org.springdoc.api.app41;
2+
3+
import org.junit.Test;
4+
import org.springdoc.core.Constants;
5+
import org.springframework.test.web.servlet.MvcResult;
6+
import test.org.springdoc.api.AbstractSpringDocTest;
7+
8+
import java.nio.file.Files;
9+
import java.nio.file.Path;
10+
import java.nio.file.Paths;
11+
12+
import static org.hamcrest.Matchers.is;
13+
import static org.skyscreamer.jsonassert.JSONAssert.assertEquals;
14+
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
15+
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
16+
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
17+
18+
public class SpringDocApp411Test extends AbstractSpringDocTest {
19+
20+
@Test
21+
public void testApp() throws Exception {
22+
String className = getClass().getSimpleName();
23+
String testNumber = className.replaceAll("[^0-9]", "");
24+
MvcResult mockMvcResult = mockMvc.perform(get(Constants.DEFAULT_API_DOCS_URL)).andExpect(status().isOk())
25+
.andExpect(jsonPath("$.openapi", is("3.0.1"))).andReturn();
26+
// Test result consistency
27+
mockMvcResult = mockMvc.perform(get(Constants.DEFAULT_API_DOCS_URL)).andExpect(status().isOk())
28+
.andExpect(jsonPath("$.openapi", is("3.0.1"))).andReturn();
29+
String result = mockMvcResult.getResponse().getContentAsString();
30+
Path path = Paths.get(getClass().getClassLoader().getResource("results/app41.json").toURI());
31+
byte[] fileBytes = Files.readAllBytes(path);
32+
String expected = new String(fileBytes);
33+
assertEquals(expected, result, false);
34+
}
35+
36+
}

springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/app41/SpringDocApp41Test.java

+15-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
11
package test.org.springdoc.api.app41;
22

33
import org.junit.Test;
4+
import org.junit.runner.RunWith;
45
import org.springdoc.core.Constants;
6+
import org.springframework.beans.factory.annotation.Autowired;
7+
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
8+
import org.springframework.boot.test.context.SpringBootTest;
9+
import org.springframework.test.context.ActiveProfiles;
10+
import org.springframework.test.context.junit4.SpringRunner;
11+
import org.springframework.test.web.servlet.MockMvc;
512
import org.springframework.test.web.servlet.MvcResult;
6-
import test.org.springdoc.api.AbstractSpringDocTest;
713

814
import java.nio.file.Files;
915
import java.nio.file.Path;
@@ -15,7 +21,14 @@
1521
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
1622
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
1723

18-
public class SpringDocApp41Test extends AbstractSpringDocTest {
24+
@RunWith(SpringRunner.class)
25+
@ActiveProfiles("test")
26+
@SpringBootTest(properties = "springdoc.cache.disabled=true")
27+
@AutoConfigureMockMvc
28+
public class SpringDocApp41Test {
29+
30+
@Autowired
31+
protected MockMvc mockMvc;
1932

2033
@Test
2134
public void testApp() throws Exception {

0 commit comments

Comments
 (0)