Skip to content

Commit 1d61661

Browse files
committed
Merge branch 'bugfix/T-2509-fix-array-index-out-of-bounds-exception-in-swagger-ui-config-parameters' of https://github.com/dgswan/springdoc-openapi into dgswan-bugfix/T-2509-fix-array-index-out-of-bounds-exception-in-swagger-ui-config-parameters
2 parents d0d4fd2 + 570e367 commit 1d61661

File tree

2 files changed

+104
-6
lines changed

2 files changed

+104
-6
lines changed

Diff for: springdoc-openapi-starter-common/src/main/java/org/springdoc/ui/AbstractSwaggerWelcome.java

+14-6
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@
3333
import org.springframework.util.CollectionUtils;
3434
import org.springframework.web.util.UriComponentsBuilder;
3535

36+
import java.util.Objects;
37+
3638
import static org.springdoc.core.utils.Constants.SWAGGER_UI_OAUTH_REDIRECT_URL;
3739
import static org.springdoc.core.utils.Constants.SWAGGER_UI_URL;
3840
import static org.springframework.util.AntPathMatcher.DEFAULT_PATH_SEPARATOR;
@@ -134,12 +136,18 @@ else if (swaggerUiConfigParameters.isValidUrl(swaggerUiUrl))
134136
else
135137
swaggerUiConfigParameters.addUrl(apiDocsUrl);
136138
if (!CollectionUtils.isEmpty(swaggerUiConfig.getUrls())) {
137-
swaggerUiConfig.cloneUrls().forEach(swaggerUrl -> {
138-
swaggerUiConfigParameters.getUrls().remove(swaggerUrl);
139-
if (!swaggerUiConfigParameters.isValidUrl(swaggerUrl.getUrl()))
140-
swaggerUrl.setUrl(buildUrlWithContextPath(swaggerUrl.getUrl()));
141-
swaggerUiConfigParameters.getUrls().add(swaggerUrl);
142-
});
139+
swaggerUiConfig.cloneUrls()
140+
.stream()
141+
.filter(swaggerUrl -> !swaggerUiConfigParameters.isValidUrl(swaggerUrl.getUrl()))
142+
.forEach(swaggerUrl -> {
143+
final var url = buildUrlWithContextPath(swaggerUrl.getUrl());
144+
if (!Objects.equals(url, swaggerUrl.getUrl())) {
145+
swaggerUiConfigParameters.getUrls()
146+
.stream()
147+
.filter(swaggerUrl::equals)
148+
.forEach(subUrl -> subUrl.setUrl(url));
149+
}
150+
});
143151
}
144152
}
145153
calculateOauth2RedirectUrl(uriComponentsBuilder);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/*
2+
*
3+
* * Copyright 2019-2020 the original author or authors.
4+
* *
5+
* * Licensed under the Apache License, Version 2.0 (the "License");
6+
* * you may not use this file except in compliance with the License.
7+
* * You may obtain a copy of the License at
8+
* *
9+
* * https://www.apache.org/licenses/LICENSE-2.0
10+
* *
11+
* * Unless required by applicable law or agreed to in writing, software
12+
* * distributed under the License is distributed on an "AS IS" BASIS,
13+
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* * See the License for the specific language governing permissions and
15+
* * limitations under the License.
16+
*
17+
*/
18+
19+
package test.org.springdoc.ui.app8;
20+
21+
import org.junit.jupiter.api.Test;
22+
import org.springframework.boot.autoconfigure.SpringBootApplication;
23+
import org.springframework.test.context.TestPropertySource;
24+
import test.org.springdoc.ui.AbstractSpringDocTest;
25+
26+
import java.util.concurrent.CompletableFuture;
27+
import java.util.stream.Stream;
28+
29+
import static java.util.concurrent.CompletableFuture.allOf;
30+
import static java.util.concurrent.CompletableFuture.runAsync;
31+
import static org.hamcrest.CoreMatchers.equalTo;
32+
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
33+
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
34+
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
35+
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
36+
37+
/**
38+
* The test to make sure no exceptions are thrown when several parallel requests
39+
* are sent to get Swagger config.
40+
*
41+
* @author Dmitry Lebedko ([email protected])
42+
*/
43+
@TestPropertySource(properties = {
44+
"spring.mvc.servlet.path=/servlet-path",
45+
"springdoc.swagger-ui.urls[0].name=first-user-list",
46+
"springdoc.swagger-ui.urls[0].url=/api-docs.yaml",
47+
"springdoc.swagger-ui.urls[1].name=second-user-list",
48+
"springdoc.swagger-ui.urls[1].url=/api-docs.yaml",
49+
"springdoc.swagger-ui.urls[2].name=third-user-list",
50+
"springdoc.swagger-ui.urls[2].url=/api-docs.yaml"
51+
})
52+
public class SpringDocApp8MultipleUrlsSeveralParallelRequestsTest extends AbstractSpringDocTest {
53+
54+
private static final int PARALLEL_REQUEST_NUMBER = 100;
55+
56+
/**
57+
* Sends {@link SpringDocApp8MultipleUrlsSeveralParallelRequestsTest#PARALLEL_REQUEST_NUMBER} requests
58+
* simultaneously to make sure no exceptions are thrown when getting Swagger config.
59+
*/
60+
@Test
61+
public void swagger_config_for_multiple_groups_and_many_parallel_requests() {
62+
assertDoesNotThrow(() -> {
63+
allOf(Stream.generate(() -> runAsync(() -> {
64+
try {
65+
mockMvc.perform(get("/v3/api-docs/swagger-config"))
66+
.andExpect(status().isOk())
67+
.andExpect(jsonPath("configUrl", equalTo("/servlet-path/v3/api-docs/swagger-config")))
68+
.andExpect(jsonPath("url").doesNotExist())
69+
.andExpect(jsonPath("urls.length()", equalTo(3)))
70+
.andExpect(jsonPath("urls[0].url", equalTo("/servlet-path/api-docs.yaml")))
71+
.andExpect(jsonPath("urls[0].name", equalTo("first-user-list")))
72+
.andExpect(jsonPath("urls[1].url", equalTo("/servlet-path/api-docs.yaml")))
73+
.andExpect(jsonPath("urls[1].name", equalTo("second-user-list")))
74+
.andExpect(jsonPath("urls[2].url", equalTo("/servlet-path/api-docs.yaml")))
75+
.andExpect(jsonPath("urls[2].name", equalTo("third-user-list")));
76+
} catch (Exception e) {
77+
throw new RuntimeException(e);
78+
}
79+
}))
80+
.limit(PARALLEL_REQUEST_NUMBER)
81+
.toArray(CompletableFuture[]::new))
82+
.join();
83+
}, "Swagger config is supposed to be delivered successfully " +
84+
"no matter how many parallel requests are sent");
85+
}
86+
87+
@SpringBootApplication
88+
static class SpringDocTestApp {}
89+
90+
}

0 commit comments

Comments
 (0)