Skip to content

Commit 06fc757

Browse files
committed
make validatorUrl configurable and fix overwrites for url (validatorUrl) in case configUrl is used
1 parent 6003432 commit 06fc757

File tree

11 files changed

+279
-36
lines changed

11 files changed

+279
-36
lines changed

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ public final class Constants {
1818
public static final String SPRINGDOC_ACTUATOR_TAG = "Actuator";
1919
public static final String DEFAULT_WEB_JARS_PREFIX_URL = "/webjars";
2020
public static final String WEB_JARS_PREFIX_URL = "${springdoc.webjars.prefix:#{T(org.springdoc.core.Constants).DEFAULT_WEB_JARS_PREFIX_URL}}";
21-
public static final String SWAGGER_UI_URL = "/swagger-ui/index.html?url=";
22-
public static final String DEFAULT_VALIDATOR_URL = "&validatorUrl=";
21+
public static final String SWAGGER_UI_URL = "/swagger-ui/index.html";
22+
public static final String DEFAULT_VALIDATOR_URL = "";
2323
public static final String APPLICATION_OPENAPI_YAML = "application/vnd.oai.openapi";
2424
public static final String DEFAULT_SWAGGER_UI_PATH = DEFAULT_PATH_SEPARATOR + "swagger-ui.html";
2525
public static final String SWAGGER_UI_PATH = "${springdoc.swagger-ui.path:#{T(org.springdoc.core.Constants).DEFAULT_SWAGGER_UI_PATH}}";

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

+20-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@
1818
@Configuration
1919
@ConfigurationProperties(prefix = "springdoc.swagger-ui")
2020
public class SwaggerUiConfigProperties {
21+
22+
public static final String CONFIG_URL_PROPERTY = "configUrl";
23+
public static final String VALIDATOR_URL_PROPERTY = "validatorUrl";
24+
public static final String URL_PROPERTY = "url";
25+
2126
/**
2227
* The path for the Swagger UI pages to load. Will redirect to the springdoc.webjars.prefix property.
2328
*/
@@ -31,6 +36,11 @@ public class SwaggerUiConfigProperties {
3136
* URL to fetch external configuration document from.
3237
*/
3338
private String configUrl;
39+
40+
/**
41+
* URL to validate specs against.
42+
*/
43+
private String validatorUrl;
3444
/**
3545
* If set, enables filtering. The top bar will show an edit box that
3646
* could be used to filter the tagged operations that are shown.
@@ -104,7 +114,8 @@ public class SwaggerUiConfigProperties {
104114
public Map<String, String> getConfigParameters() {
105115
final Map<String, String> params = new TreeMap<>();
106116
put("layout", layout, params);
107-
put("configUrl", configUrl, params);
117+
put(CONFIG_URL_PROPERTY, configUrl, params);
118+
put(VALIDATOR_URL_PROPERTY, validatorUrl, params);
108119
put("filter", filter, params);
109120
put("deepLinking", this.deepLinking, params);
110121
put("displayOperationId", displayOperationId, params);
@@ -142,6 +153,14 @@ protected void put(final String name, final String value, final Map<String, Stri
142153
}
143154
}
144155

156+
public String getValidatorUrl() {
157+
return validatorUrl;
158+
}
159+
160+
public void setValidatorUrl(String validatorUrl) {
161+
this.validatorUrl = validatorUrl;
162+
}
163+
145164
public String getPath() {
146165
return path;
147166
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package org.springdoc.core;
2+
3+
import org.springframework.web.util.UriComponentsBuilder;
4+
5+
import java.util.Map;
6+
7+
import static org.springdoc.core.Constants.DEFAULT_VALIDATOR_URL;
8+
import static org.springdoc.core.SwaggerUiConfigProperties.*;
9+
10+
final public class SwaggerUiQueryParamsAppender {
11+
12+
private SwaggerUiQueryParamsAppender() {
13+
}
14+
15+
/**
16+
* Appends swagger-ui query-params to the provided builder.
17+
* <p>
18+
* Since url is derived from springdoc-endpoint it can be provided independently of swaggerUiParams.
19+
* If configUrl is used, config has to be provided manually including `url`, `validatorUrl`
20+
*
21+
* @param uriBuilder the UriComponentsBuilder to which queryParams get appended
22+
* @param swaggerUiParams a map of the swaggerUiParams to use
23+
* @param url the url to use as queryParameter (gets derived from springdoc properties
24+
* @return UriComponentsBuilder with appended swagger-ui query-parameters
25+
*/
26+
public static UriComponentsBuilder appendSwaggerUiQueryParams(final UriComponentsBuilder uriBuilder,
27+
final Map<String, String> swaggerUiParams,
28+
final String url) {
29+
if (swaggerUiParams.get(CONFIG_URL_PROPERTY) == null) {
30+
uriBuilder.queryParam(URL_PROPERTY, url);
31+
32+
if (swaggerUiParams.get(VALIDATOR_URL_PROPERTY) == null) {
33+
uriBuilder.queryParam(VALIDATOR_URL_PROPERTY, DEFAULT_VALIDATOR_URL);
34+
}
35+
}
36+
return appendQueryParameter(uriBuilder, swaggerUiParams);
37+
}
38+
39+
private static UriComponentsBuilder appendQueryParameter(UriComponentsBuilder uriBuilder,
40+
Map<String, String> swaggerUiParams) {
41+
42+
return swaggerUiParams.entrySet().stream()
43+
.reduce(uriBuilder,
44+
(b, e) -> b.queryParam(e.getKey(), e.getValue()),
45+
(left, right) -> left);
46+
}
47+
48+
49+
}

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

+12-17
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import java.util.Map;
1414

1515
import static org.springdoc.core.Constants.*;
16+
import static org.springdoc.core.SwaggerUiQueryParamsAppender.appendSwaggerUiQueryParams;
1617
import static org.springframework.util.AntPathMatcher.DEFAULT_PATH_SEPARATOR;
1718
import static org.springframework.web.servlet.view.UrlBasedViewResolver.REDIRECT_URL_PREFIX;
1819

@@ -47,25 +48,19 @@ public String redirectToUi(HttpServletRequest request) {
4748
sbUrl.append(mvcServletPath);
4849
sbUrl.append(uiRootPath);
4950
sbUrl.append(SWAGGER_UI_URL);
50-
if (contextPath.endsWith(DEFAULT_PATH_SEPARATOR)) {
51-
contextPath = contextPath.substring(0, contextPath.length() - 1);
52-
sbUrl.append(contextPath).append(apiDocsUrl);
53-
} else {
54-
sbUrl.append(contextPath).append(apiDocsUrl);
55-
}
56-
sbUrl.append(DEFAULT_VALIDATOR_URL);
5751

58-
final Map<String, String> params = swaggerUiConfig.getConfigParameters();
52+
UriComponentsBuilder uriBuilder = UriComponentsBuilder.fromUriString(sbUrl.toString());
53+
Map<String, String> swaggerUiParams = swaggerUiConfig.getConfigParameters();
54+
String url = buildUrl(contextPath, apiDocsUrl);
5955

60-
final UriComponentsBuilder builder = params
61-
.entrySet()
62-
.stream()
63-
.reduce(
64-
UriComponentsBuilder
65-
.fromUriString(sbUrl.toString()),
66-
(b, e) -> b.queryParam(e.getKey(), e.getValue()),
67-
(left, right) -> left);
56+
return appendSwaggerUiQueryParams(uriBuilder, swaggerUiParams, url)
57+
.build().encode().toString();
58+
}
6859

69-
return builder.build().encode().toString();
60+
private String buildUrl(final String contextPath, final String docsUrl) {
61+
if (contextPath.endsWith(DEFAULT_PATH_SEPARATOR)) {
62+
return contextPath.substring(0, contextPath.length() - 1) + docsUrl;
63+
}
64+
return contextPath + docsUrl;
7065
}
7166
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package test.org.springdoc.ui.app1;
2+
3+
import org.junit.Test;
4+
import org.springdoc.core.SwaggerUiConfigProperties;
5+
import org.springframework.beans.factory.annotation.Autowired;
6+
import org.springframework.boot.test.context.SpringBootTest;
7+
import org.springframework.test.web.servlet.MvcResult;
8+
import test.org.springdoc.ui.AbstractSpringDocTest;
9+
10+
import static org.junit.Assert.assertEquals;
11+
import static org.junit.Assert.assertTrue;
12+
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
13+
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
14+
15+
@SpringBootTest(properties = {
16+
"springdoc.swagger-ui.configUrl=/foo/bar",
17+
"springdoc.swagger-ui.url=/batz" // ignored since configUrl is configured
18+
})
19+
public class SpringDocApp1RedirectConfigUrlTest extends AbstractSpringDocTest {
20+
21+
@Test
22+
public void shouldRedirectWithConfigUrlIgnoringQueryParams() throws Exception {
23+
MvcResult mvcResult = mockMvc.perform(get("/swagger-ui.html"))
24+
.andExpect(status().isFound()).andReturn();
25+
26+
String locationHeader = mvcResult.getResponse().getHeader("Location");
27+
assertEquals("/swagger-ui/index.html?configUrl=/foo/bar", locationHeader);
28+
}
29+
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package test.org.springdoc.ui.app1;
2+
3+
import org.junit.Test;
4+
import org.springframework.test.web.servlet.MvcResult;
5+
import test.org.springdoc.ui.AbstractSpringDocTest;
6+
7+
import static org.junit.Assert.assertEquals;
8+
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
9+
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
10+
11+
public class SpringDocApp1RedirectDefaultTest extends AbstractSpringDocTest {
12+
13+
@Test
14+
public void shouldRedirectWithDefaultQueryParams() throws Exception {
15+
MvcResult mvcResult = mockMvc.perform(get("/swagger-ui.html"))
16+
.andExpect(status().isFound()).andReturn();
17+
18+
String locationHeader = mvcResult.getResponse().getHeader("Location");
19+
assertEquals("/swagger-ui/index.html?url=/v3/api-docs&validatorUrl=", locationHeader);
20+
}
21+
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package test.org.springdoc.ui.app1;
2+
3+
import org.junit.Test;
4+
import org.junit.runner.RunWith;
5+
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
6+
import org.springframework.boot.test.context.SpringBootTest;
7+
import org.springframework.test.context.ActiveProfiles;
8+
import org.springframework.test.context.TestPropertySource;
9+
import org.springframework.test.context.junit4.SpringRunner;
10+
import org.springframework.test.web.servlet.MvcResult;
11+
import test.org.springdoc.ui.AbstractSpringDocTest;
12+
13+
import static org.junit.Assert.assertEquals;
14+
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
15+
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
16+
17+
@SpringBootTest(properties = {
18+
"springdoc.swagger-ui.validatorUrl=/foo/validate",
19+
"springdoc.api-docs.path=/baf/batz"
20+
})
21+
public class SpringDocApp1RedirectWithConfigTest extends AbstractSpringDocTest {
22+
23+
@Test
24+
public void shouldRedirectWithConfiguredParams() throws Exception {
25+
MvcResult mvcResult = mockMvc.perform(get("/swagger-ui.html"))
26+
.andExpect(status().isFound()).andReturn();
27+
28+
String locationHeader = mvcResult.getResponse().getHeader("Location");
29+
assertEquals("/swagger-ui/index.html?url=/baf/batz&validatorUrl=/foo/validate", locationHeader);
30+
}
31+
32+
}

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

+5-16
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import java.util.Map;
1616

1717
import static org.springdoc.core.Constants.*;
18+
import static org.springdoc.core.SwaggerUiQueryParamsAppender.appendSwaggerUiQueryParams;
1819
import static org.springframework.web.reactive.function.server.RequestPredicates.GET;
1920
import static org.springframework.web.reactive.function.server.RouterFunctions.route;
2021

@@ -38,23 +39,11 @@ public class SwaggerWelcome {
3839
@Bean
3940
@ConditionalOnProperty(name = SPRINGDOC_SWAGGER_UI_ENABLED, matchIfMissing = true)
4041
RouterFunction<ServerResponse> routerFunction() {
41-
String url = webJarsPrefixUrl +
42-
SWAGGER_UI_URL +
43-
apiDocsUrl +
44-
DEFAULT_VALIDATOR_URL;
45-
46-
final Map<String, String> params = swaggerUiConfig.getConfigParameters();
47-
48-
49-
final UriComponentsBuilder builder = params
50-
.entrySet()
51-
.stream()
52-
.reduce(
53-
UriComponentsBuilder
54-
.fromUriString(url),
55-
(b, e) -> b.queryParam(e.getKey(), e.getValue()),
56-
(left, right) -> left);
42+
String baseUrl = webJarsPrefixUrl + SWAGGER_UI_URL;
5743

44+
final Map<String, String> swaggerUiParams = swaggerUiConfig.getConfigParameters();
45+
final UriComponentsBuilder uriBuilder = UriComponentsBuilder.fromUriString(baseUrl);
46+
final UriComponentsBuilder builder = appendSwaggerUiQueryParams(uriBuilder, swaggerUiParams, apiDocsUrl);
5847

5948
return route(GET(uiPath),
6049
req -> ServerResponse.temporaryRedirect(URI.create(builder.build().encode().toString())).build());
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package test.org.springdoc.ui.app1;
2+
3+
import org.hamcrest.Matchers;
4+
import org.junit.Test;
5+
import org.junit.runner.RunWith;
6+
import org.springdoc.core.SwaggerUiConfigProperties;
7+
import org.springdoc.ui.SwaggerWelcome;
8+
import org.springframework.beans.factory.annotation.Autowired;
9+
import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest;
10+
import org.springframework.test.context.ActiveProfiles;
11+
import org.springframework.test.context.ContextConfiguration;
12+
import org.springframework.test.context.junit4.SpringRunner;
13+
import org.springframework.test.web.reactive.server.WebTestClient;
14+
15+
16+
@RunWith(SpringRunner.class)
17+
@WebFluxTest(properties = {
18+
"springdoc.swagger-ui.configUrl=/foo/bar",
19+
"springdoc.swagger-ui.url=/batz" // ignored since configUrl is configured
20+
})
21+
@ActiveProfiles("test")
22+
@ContextConfiguration(classes = {SwaggerWelcome.class, SwaggerUiConfigProperties.class})
23+
public class SpringDocApp1RedirectConfigUrlTest {
24+
25+
@Autowired
26+
private WebTestClient webTestClient;
27+
28+
@Test
29+
public void shouldRedirectWithConfigUrlIgnoringQueryParams() throws Exception {
30+
31+
WebTestClient.ResponseSpec responseSpec = webTestClient.get().uri("/swagger-ui.html").exchange()
32+
.expectStatus().isTemporaryRedirect();
33+
responseSpec.expectHeader()
34+
.value("Location", Matchers.is("/webjars/swagger-ui/index.html?configUrl=/foo/bar"));
35+
36+
}
37+
38+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package test.org.springdoc.ui.app1;
2+
3+
import org.hamcrest.Matchers;
4+
import org.junit.Test;
5+
import org.junit.runner.RunWith;
6+
import org.springdoc.core.SwaggerUiConfigProperties;
7+
import org.springdoc.ui.SwaggerWelcome;
8+
import org.springframework.beans.factory.annotation.Autowired;
9+
import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest;
10+
import org.springframework.test.context.ActiveProfiles;
11+
import org.springframework.test.context.ContextConfiguration;
12+
import org.springframework.test.context.junit4.SpringRunner;
13+
import org.springframework.test.web.reactive.server.WebTestClient;
14+
15+
16+
@RunWith(SpringRunner.class)
17+
@WebFluxTest()
18+
@ActiveProfiles("test")
19+
@ContextConfiguration(classes = {SwaggerWelcome.class, SwaggerUiConfigProperties.class})
20+
public class SpringDocApp1RedirectDefaultTest {
21+
22+
@Autowired
23+
private WebTestClient webTestClient;
24+
25+
@Test
26+
public void shouldRedirectWithDefaultQueryParams() throws Exception {
27+
WebTestClient.ResponseSpec responseSpec = webTestClient.get().uri("/swagger-ui.html").exchange()
28+
.expectStatus().isTemporaryRedirect();
29+
responseSpec.expectHeader()
30+
.value("Location", Matchers.is("/webjars/swagger-ui/index.html?url=/v3/api-docs&validatorUrl="));
31+
32+
}
33+
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package test.org.springdoc.ui.app1;
2+
3+
import org.hamcrest.Matchers;
4+
import org.junit.Test;
5+
import org.junit.runner.RunWith;
6+
import org.springdoc.core.SwaggerUiConfigProperties;
7+
import org.springdoc.ui.SwaggerWelcome;
8+
import org.springframework.beans.factory.annotation.Autowired;
9+
import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest;
10+
import org.springframework.test.context.ActiveProfiles;
11+
import org.springframework.test.context.ContextConfiguration;
12+
import org.springframework.test.context.junit4.SpringRunner;
13+
import org.springframework.test.web.reactive.server.WebTestClient;
14+
15+
@RunWith(SpringRunner.class)
16+
@WebFluxTest(properties = {
17+
"springdoc.swagger-ui.validatorUrl=/foo/validate",
18+
"springdoc.api-docs.path=/baf/batz"
19+
})
20+
@ActiveProfiles("test")
21+
@ContextConfiguration(classes = {SwaggerWelcome.class, SwaggerUiConfigProperties.class})
22+
public class SpringDocApp1RedirectWithConfigTest {
23+
@Autowired
24+
private WebTestClient webTestClient;
25+
26+
@Test
27+
public void shouldRedirectWithConfiguredParams() throws Exception {
28+
WebTestClient.ResponseSpec responseSpec = webTestClient.get().uri("/swagger-ui.html").exchange()
29+
.expectStatus().isTemporaryRedirect();
30+
31+
responseSpec.expectHeader()
32+
.value("Location", Matchers.is("/webjars/swagger-ui/index.html?url=/baf/batz&validatorUrl=/foo/validate"));
33+
}
34+
35+
}

0 commit comments

Comments
 (0)