Skip to content

Commit df545f1

Browse files
author
bnasslahsen
committed
Added support for auto fill clientId and clientSecret for webflux. fixes #333
1 parent a8b0279 commit df545f1

File tree

8 files changed

+282
-4
lines changed

8 files changed

+282
-4
lines changed

Diff for: springdoc-openapi-webflux-ui/src/main/java/org/springdoc/ui/SwaggerConfig.java

+20-1
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,30 @@
1818

1919
package org.springdoc.ui;
2020

21+
import com.fasterxml.jackson.databind.ObjectMapper;
22+
import org.springdoc.core.SpringDocConfiguration;
23+
import org.springdoc.core.SwaggerUiOAuthProperties;
24+
25+
import org.springframework.beans.factory.annotation.Autowired;
2126
import org.springframework.beans.factory.annotation.Value;
27+
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
28+
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
29+
import org.springframework.context.annotation.Bean;
2230
import org.springframework.context.annotation.Configuration;
2331
import org.springframework.web.reactive.config.ResourceHandlerRegistry;
2432
import org.springframework.web.reactive.config.WebFluxConfigurer;
2533

2634
import static org.springdoc.core.Constants.CLASSPATH_RESOURCE_LOCATION;
2735
import static org.springdoc.core.Constants.DEFAULT_WEB_JARS_PREFIX_URL;
36+
import static org.springdoc.core.Constants.SPRINGDOC_SWAGGER_UI_ENABLED;
2837
import static org.springdoc.core.Constants.SWAGGER_UI_PATH;
2938
import static org.springdoc.core.Constants.WEB_JARS_PREFIX_URL;
3039
import static org.springframework.util.AntPathMatcher.DEFAULT_PATH_SEPARATOR;
3140

3241

3342
@Configuration
43+
@ConditionalOnProperty(name = SPRINGDOC_SWAGGER_UI_ENABLED, matchIfMissing = true)
44+
@ConditionalOnBean(SpringDocConfiguration.class)
3445
public class SwaggerConfig implements WebFluxConfigurer {
3546

3647
@Value(SWAGGER_UI_PATH)
@@ -39,6 +50,8 @@ public class SwaggerConfig implements WebFluxConfigurer {
3950
@Value(WEB_JARS_PREFIX_URL)
4051
private String webJarsPrefixUrl;
4152

53+
@Autowired
54+
private SwaggerIndexTransformer swaggerIndexTransformer;
4255

4356
@Override
4457
public void addResourceHandlers(ResourceHandlerRegistry registry) {
@@ -48,7 +61,13 @@ public void addResourceHandlers(ResourceHandlerRegistry registry) {
4861
}
4962
registry.addResourceHandler(uiRootPath + webJarsPrefixUrl+"/**")
5063
.addResourceLocations(CLASSPATH_RESOURCE_LOCATION + DEFAULT_WEB_JARS_PREFIX_URL+DEFAULT_PATH_SEPARATOR)
51-
.resourceChain(false);
64+
.resourceChain(false)
65+
.addTransformer(swaggerIndexTransformer);
5266
}
5367

68+
@Bean
69+
@ConditionalOnProperty(name = SPRINGDOC_SWAGGER_UI_ENABLED, matchIfMissing = true)
70+
public SwaggerIndexTransformer indexPageTransformer(SwaggerUiOAuthProperties swaggerUiOAuthProperties, ObjectMapper objectMapper) {
71+
return new SwaggerIndexTransformer(swaggerUiOAuthProperties, objectMapper);
72+
}
5473
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
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 org.springdoc.ui;
20+
21+
import java.io.ByteArrayOutputStream;
22+
import java.io.IOException;
23+
import java.io.InputStream;
24+
import java.nio.charset.StandardCharsets;
25+
26+
import com.fasterxml.jackson.core.JsonProcessingException;
27+
import com.fasterxml.jackson.databind.ObjectMapper;
28+
import org.slf4j.Logger;
29+
import org.slf4j.LoggerFactory;
30+
import org.springdoc.core.SwaggerUiOAuthProperties;
31+
import reactor.core.publisher.Mono;
32+
33+
import org.springframework.core.io.Resource;
34+
import org.springframework.util.AntPathMatcher;
35+
import org.springframework.util.CollectionUtils;
36+
import org.springframework.web.reactive.resource.ResourceTransformer;
37+
import org.springframework.web.reactive.resource.ResourceTransformerChain;
38+
import org.springframework.web.reactive.resource.TransformedResource;
39+
import org.springframework.web.server.ServerWebExchange;
40+
41+
public class SwaggerIndexTransformer implements ResourceTransformer {
42+
43+
private static final Logger LOGGER = LoggerFactory.getLogger(SwaggerIndexTransformer.class);
44+
45+
private SwaggerUiOAuthProperties swaggerUiOAuthProperties;
46+
47+
private ObjectMapper objectMapper;
48+
49+
public SwaggerIndexTransformer(SwaggerUiOAuthProperties swaggerUiOAuthProperties, ObjectMapper objectMapper) {
50+
this.swaggerUiOAuthProperties = swaggerUiOAuthProperties;
51+
this.objectMapper = objectMapper;
52+
}
53+
54+
@Override
55+
public Mono<Resource> transform(ServerWebExchange serverWebExchange, Resource resource, ResourceTransformerChain resourceTransformerChain) {
56+
final AntPathMatcher antPathMatcher = new AntPathMatcher();
57+
boolean isIndexFound = false;
58+
try {
59+
isIndexFound = antPathMatcher.match("**/swagger-ui/**/index.html", resource.getURL().toString());
60+
if (isIndexFound && !CollectionUtils.isEmpty(swaggerUiOAuthProperties.getConfigParameters())) {
61+
String html = readFullyAsString(resource.getInputStream());
62+
html = addInitOauth(html);
63+
return Mono.just(new TransformedResource(resource, html.getBytes()));
64+
}
65+
else {
66+
return Mono.just(resource);
67+
}
68+
}
69+
catch (Exception e) {
70+
throw new RuntimeException(e);
71+
}
72+
}
73+
74+
private String addInitOauth(String html) throws JsonProcessingException {
75+
StringBuilder stringBuilder = new StringBuilder("window.ui = ui\n");
76+
stringBuilder.append("ui.initOAuth(\n");
77+
String json = objectMapper.writeValueAsString(swaggerUiOAuthProperties.getConfigParameters());
78+
stringBuilder.append(json);
79+
stringBuilder.append(")");
80+
return html.replace("window.ui = ui", stringBuilder.toString());
81+
}
82+
83+
private String readFullyAsString(InputStream inputStream)
84+
throws IOException {
85+
ByteArrayOutputStream baos = new ByteArrayOutputStream();
86+
byte[] buffer = new byte[1024];
87+
int length;
88+
while ((length = inputStream.read(buffer)) != -1) {
89+
baos.write(buffer, 0, length);
90+
}
91+
return baos.toString(StandardCharsets.UTF_8.name());
92+
}
93+
94+
}
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
22
org.springdoc.ui.SwaggerWelcome,\
33
org.springdoc.ui.SwaggerConfig,\
4-
org.springdoc.core.SwaggerUiConfigProperties
4+
org.springdoc.core.SwaggerUiConfigProperties,\
5+
org.springdoc.core.SwaggerUiOAuthProperties

Diff for: springdoc-openapi-webflux-ui/src/test/java/test/org/springdoc/ui/AbstractSpringDocTest.java

+19-2
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,17 @@
1818

1919
package test.org.springdoc.ui;
2020

21+
import java.nio.charset.StandardCharsets;
22+
import java.nio.file.Files;
23+
import java.nio.file.Path;
24+
import java.nio.file.Paths;
25+
26+
import nonapi.io.github.classgraph.utils.FileUtils;
2127
import org.springdoc.core.SpringDocConfigProperties;
2228
import org.springdoc.core.SpringDocConfiguration;
2329
import org.springdoc.core.SpringDocWebFluxConfiguration;
2430
import org.springdoc.core.SwaggerUiConfigProperties;
31+
import org.springdoc.core.SwaggerUiOAuthProperties;
2532
import org.springdoc.ui.SwaggerConfig;
2633
import org.springdoc.ui.SwaggerWelcome;
2734

@@ -34,11 +41,21 @@
3441

3542
@ActiveProfiles("test")
3643
@WebFluxTest
37-
@ContextConfiguration(classes = { SpringDocConfiguration.class, SpringDocConfigProperties.class, SpringDocWebFluxConfiguration.class, SwaggerUiConfigProperties.class, SwaggerConfig.class, SwaggerWelcome.class })
44+
@ContextConfiguration(classes = { SpringDocConfiguration.class, SpringDocConfigProperties.class, SpringDocWebFluxConfiguration.class, SwaggerUiConfigProperties.class, SwaggerConfig.class, SwaggerWelcome.class, SwaggerUiOAuthProperties.class })
3845
public abstract class AbstractSpringDocTest {
3946

40-
4147
@Autowired
4248
protected WebTestClient webTestClient;
4349

50+
protected String getContent(String fileName) {
51+
try {
52+
Path path = Paths.get(FileUtils.class.getClassLoader().getResource(fileName).toURI());
53+
byte[] fileBytes = Files.readAllBytes(path);
54+
return new String(fileBytes, StandardCharsets.UTF_8);
55+
}
56+
catch (Exception e) {
57+
throw new RuntimeException("Failed to read file: " + fileName, e);
58+
}
59+
}
60+
4461
}

Diff for: springdoc-openapi-webflux-ui/src/test/java/test/org/springdoc/ui/app4/SpringDocOauthContextPathTest.java

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
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.app5;
20+
21+
import javax.validation.Valid;
22+
import javax.validation.constraints.Size;
23+
24+
import org.springframework.web.bind.annotation.GetMapping;
25+
import org.springframework.web.bind.annotation.RequestParam;
26+
import org.springframework.web.bind.annotation.RestController;
27+
28+
@RestController
29+
public class HelloController {
30+
31+
@GetMapping(value = "/persons")
32+
public void persons(@Valid @RequestParam @Size(min = 4, max = 6) String name) {
33+
34+
}
35+
36+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
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.app5;
20+
21+
import org.junit.jupiter.api.Test;
22+
import test.org.springdoc.ui.AbstractSpringDocTest;
23+
24+
import org.springframework.boot.autoconfigure.SpringBootApplication;
25+
import org.springframework.test.context.TestPropertySource;
26+
import org.springframework.test.web.reactive.server.EntityExchangeResult;
27+
28+
import static org.junit.jupiter.api.Assertions.assertEquals;
29+
import static org.junit.jupiter.api.Assertions.assertTrue;
30+
31+
@TestPropertySource(properties = "springdoc.swagger-ui.oauth.clientId=myClientId")
32+
public class SpringDocApp5Test extends AbstractSpringDocTest {
33+
34+
@Test
35+
public void transformed_index_with_oauth() throws Exception {
36+
EntityExchangeResult<byte[]> getResult = webTestClient.get().uri("/webjars/swagger-ui/index.html")
37+
.exchange()
38+
.expectStatus().isOk()
39+
.expectBody().returnResult();
40+
String result = new String(getResult.getResponseBody());
41+
assertTrue(result.contains("Swagger UI"));
42+
String expected = getContent("results/index5.html");
43+
assertEquals(expected, result);
44+
}
45+
46+
@SpringBootApplication
47+
static class SpringDocTestApp {}
48+
49+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<!-- HTML for static distribution bundle build -->
2+
<!DOCTYPE html>
3+
<html lang="en">
4+
<head>
5+
<meta charset="UTF-8">
6+
<title>Swagger UI</title>
7+
<link rel="stylesheet" type="text/css" href="./swagger-ui.css" >
8+
<link rel="icon" type="image/png" href="./favicon-32x32.png" sizes="32x32" />
9+
<link rel="icon" type="image/png" href="./favicon-16x16.png" sizes="16x16" />
10+
<style>
11+
html
12+
{
13+
box-sizing: border-box;
14+
overflow: -moz-scrollbars-vertical;
15+
overflow-y: scroll;
16+
}
17+
18+
*,
19+
*:before,
20+
*:after
21+
{
22+
box-sizing: inherit;
23+
}
24+
25+
body
26+
{
27+
margin:0;
28+
background: #fafafa;
29+
}
30+
</style>
31+
</head>
32+
33+
<body>
34+
<div id="swagger-ui"></div>
35+
36+
<script src="./swagger-ui-bundle.js"> </script>
37+
<script src="./swagger-ui-standalone-preset.js"> </script>
38+
<script>
39+
window.onload = function() {
40+
// Begin Swagger UI call region
41+
const ui = SwaggerUIBundle({
42+
url: "https://petstore.swagger.io/v2/swagger.json",
43+
dom_id: '#swagger-ui',
44+
deepLinking: true,
45+
presets: [
46+
SwaggerUIBundle.presets.apis,
47+
SwaggerUIStandalonePreset
48+
],
49+
plugins: [
50+
SwaggerUIBundle.plugins.DownloadUrl
51+
],
52+
layout: "StandaloneLayout"
53+
})
54+
// End Swagger UI call region
55+
56+
window.ui = ui
57+
ui.initOAuth(
58+
{"clientId":"myClientId"})
59+
}
60+
</script>
61+
</body>
62+
</html>

0 commit comments

Comments
 (0)