Skip to content
This repository was archived by the owner on Dec 19, 2023. It is now read-only.

Commit 1cea297

Browse files
authored
Merge pull request #521 from BlasiusSecundus/feature/fix-playground-webflux
Feature/fix playground webflux
2 parents 4118504 + 6be4a07 commit 1cea297

25 files changed

+571
-76
lines changed

example-graphql-subscription/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ apply plugin: "org.springframework.boot"
33
dependencies {
44
implementation(project(":graphql-spring-boot-starter"))
55
implementation(project(":graphiql-spring-boot-starter"))
6+
implementation(project(":playground-spring-boot-starter"))
67
implementation "com.graphql-java-kickstart:graphql-java-tools:$LIB_GRAPHQL_JAVA_TOOLS_VER"
78

89
implementation "io.reactivex.rxjava2:rxjava"

example-webflux/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ dependencies {
2323
implementation(project(":graphql-kickstart-spring-boot-starter-webflux"))
2424
implementation(project(":graphql-kickstart-spring-boot-starter-tools"))
2525
implementation(project(":voyager-spring-boot-starter"))
26+
implementation(project(":playground-spring-boot-starter"))
2627

2728
implementation("org.springframework.boot:spring-boot-starter-webflux:$LIB_SPRING_BOOT_VER")
2829
implementation("org.springframework.boot:spring-boot-starter-actuator:$LIB_SPRING_BOOT_VER")

playground-spring-boot-autoconfigure/build.gradle

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,17 @@ dependencies{
2020
annotationProcessor "org.springframework.boot:spring-boot-configuration-processor"
2121

2222
implementation "org.springframework.boot:spring-boot-autoconfigure"
23-
implementation "org.springframework.boot:spring-boot-starter-web"
23+
compileOnly "org.springframework.boot:spring-boot-starter-web"
24+
compileOnly "org.springframework.boot:spring-boot-starter-webflux"
25+
compileOnly "org.springframework.boot:spring-boot-starter-security"
2426
implementation "org.springframework.boot:spring-boot-starter-thymeleaf"
2527
implementation "org.springframework.boot:spring-boot-starter-validation"
2628

2729
testImplementation "org.springframework.boot:spring-boot-starter-web"
30+
testImplementation "org.springframework.boot:spring-boot-starter-webflux"
2831
testImplementation "org.springframework.boot:spring-boot-starter-test"
2932
testImplementation "org.springframework.boot:spring-boot-starter-security"
33+
testImplementation "org.springframework.security:spring-security-test"
3034
testImplementation "org.jsoup:jsoup:$LIB_JSOUP_VER"
3135
}
3236

playground-spring-boot-autoconfigure/src/main/java/graphql/kickstart/playground/boot/PlaygroundAutoConfiguration.java

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,20 @@
11
package graphql.kickstart.playground.boot;
22

33
import com.fasterxml.jackson.databind.ObjectMapper;
4-
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
54
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
65
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
76
import org.springframework.boot.context.properties.EnableConfigurationProperties;
87
import org.springframework.context.annotation.Bean;
98
import org.springframework.context.annotation.Configuration;
10-
import org.springframework.web.servlet.DispatcherServlet;
119

1210
@Configuration
1311
@ConditionalOnWebApplication
14-
@ConditionalOnClass(DispatcherServlet.class)
1512
@EnableConfigurationProperties(PlaygroundPropertiesConfiguration.class)
1613
public class PlaygroundAutoConfiguration {
1714

1815
@Bean
1916
@ConditionalOnProperty(value = "graphql.playground.enabled", havingValue = "true", matchIfMissing = true)
20-
PlaygroundController playgroundController(final PlaygroundPropertiesConfiguration playgroundPropertiesConfiguration,
17+
public PlaygroundController playgroundController(final PlaygroundPropertiesConfiguration playgroundPropertiesConfiguration,
2118
final ObjectMapper objectMapper) {
2219
return new PlaygroundController(playgroundPropertiesConfiguration, objectMapper);
2320
}

playground-spring-boot-autoconfigure/src/main/java/graphql/kickstart/playground/boot/PlaygroundController.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@
55
import org.springframework.stereotype.Controller;
66
import org.springframework.ui.Model;
77
import org.springframework.web.bind.annotation.GetMapping;
8+
import org.springframework.web.bind.annotation.RequestAttribute;
89

9-
import javax.servlet.http.HttpServletRequest;
1010
import java.nio.file.Paths;
1111

12+
import static java.util.Objects.nonNull;
13+
1214
@Controller
1315
@RequiredArgsConstructor
1416
public class PlaygroundController {
@@ -23,21 +25,24 @@ public class PlaygroundController {
2325
private static final String FAVICON_URL_ATTRIBUTE_NAME = "faviconUrl";
2426
private static final String SCRIPT_URL_ATTRIBUTE_NAME = "scriptUrl";
2527
private static final String LOGO_URL_ATTRIBUTE_NAME = "logoUrl";
28+
private static final String _CSRF = "_csrf";
2629

2730
private final PlaygroundPropertiesConfiguration propertiesConfiguration;
2831

2932
private final ObjectMapper objectMapper;
3033

3134
@GetMapping("${graphql.playground.mapping:/playground}")
32-
public String playground(final Model model, final HttpServletRequest request) {
35+
public String playground(final Model model, final @RequestAttribute(value = _CSRF, required = false) Object csrf) {
3336
if (propertiesConfiguration.getPlayground().getCdn().isEnabled()) {
3437
setCdnUrls(model);
3538
} else {
3639
setLocalAssetUrls(model);
3740
}
3841
model.addAttribute("pageTitle", propertiesConfiguration.getPlayground().getPageTitle());
3942
model.addAttribute("properties", objectMapper.valueToTree(propertiesConfiguration.getPlayground()));
40-
model.addAttribute("_csrf", request.getAttribute("_csrf"));
43+
if (nonNull(csrf)) {
44+
model.addAttribute(_CSRF, csrf);
45+
}
4146
return "playground";
4247
}
4348

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package graphql.kickstart.playground.boot;
2+
3+
import lombok.RequiredArgsConstructor;
4+
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
5+
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
6+
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
7+
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
8+
import org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration;
9+
import org.springframework.context.ApplicationContext;
10+
import org.springframework.context.annotation.Bean;
11+
import org.springframework.context.annotation.Configuration;
12+
import org.springframework.context.annotation.Import;
13+
import org.springframework.core.io.ClassPathResource;
14+
import org.springframework.web.reactive.config.ViewResolverRegistry;
15+
import org.springframework.web.reactive.config.WebFluxConfigurer;
16+
import org.springframework.web.reactive.function.server.RouterFunction;
17+
import org.springframework.web.reactive.function.server.RouterFunctions;
18+
import org.springframework.web.reactive.function.server.ServerResponse;
19+
import org.thymeleaf.spring5.SpringWebFluxTemplateEngine;
20+
import org.thymeleaf.spring5.view.reactive.ThymeleafReactiveViewResolver;
21+
import org.thymeleaf.templatemode.TemplateMode;
22+
import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver;
23+
24+
import java.nio.charset.StandardCharsets;
25+
26+
@Configuration
27+
@Import(PlaygroundWebFluxControllerAdvice.class)
28+
@ConditionalOnClass(WebFluxConfigurer.class)
29+
@ConditionalOnProperty(value = "graphql.playground.enabled", havingValue = "true", matchIfMissing = true)
30+
@RequiredArgsConstructor
31+
public class PlaygroundWebFluxAutoConfiguration implements WebFluxConfigurer {
32+
33+
private final ApplicationContext applicationContext;
34+
35+
@Bean
36+
public RouterFunction<ServerResponse> playgroundStaticFilesRouter() {
37+
return RouterFunctions.resources("/vendor/playground/**", new ClassPathResource("static/vendor/playground/"));
38+
}
39+
40+
@Override
41+
public void configureViewResolvers(final ViewResolverRegistry registry) {
42+
final ClassLoaderTemplateResolver templateResolver = new ClassLoaderTemplateResolver();
43+
templateResolver.setPrefix("templates/");
44+
templateResolver.setSuffix(".html");
45+
templateResolver.setTemplateMode(TemplateMode.HTML);
46+
templateResolver.setCharacterEncoding(StandardCharsets.UTF_8.displayName());
47+
templateResolver.setOrder(1);
48+
templateResolver.setCheckExistence(true);
49+
final SpringWebFluxTemplateEngine springWebFluxTemplateEngine = new SpringWebFluxTemplateEngine();
50+
springWebFluxTemplateEngine.setTemplateResolver(templateResolver);
51+
final ThymeleafReactiveViewResolver thymeleafReactiveViewResolver = new ThymeleafReactiveViewResolver();
52+
thymeleafReactiveViewResolver.setDefaultCharset(StandardCharsets.UTF_8);
53+
thymeleafReactiveViewResolver.setApplicationContext(applicationContext);
54+
thymeleafReactiveViewResolver.setTemplateEngine(springWebFluxTemplateEngine);
55+
thymeleafReactiveViewResolver.setViewNames(new String[] {"playground"});
56+
registry.viewResolver(thymeleafReactiveViewResolver);
57+
}
58+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package graphql.kickstart.playground.boot;
2+
3+
import lombok.NoArgsConstructor;
4+
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
5+
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
6+
import org.springframework.security.web.reactive.result.view.CsrfRequestDataValueProcessor;
7+
import org.springframework.security.web.server.csrf.CsrfToken;
8+
import org.springframework.web.bind.annotation.ControllerAdvice;
9+
import org.springframework.web.bind.annotation.ModelAttribute;
10+
import org.springframework.web.server.ServerWebExchange;
11+
import reactor.core.publisher.Mono;
12+
13+
@NoArgsConstructor
14+
@ControllerAdvice
15+
@ConditionalOnClass({ CsrfToken.class, CsrfRequestDataValueProcessor.class })
16+
@ConditionalOnBean(CsrfRequestDataValueProcessor.class)
17+
public class PlaygroundWebFluxControllerAdvice {
18+
19+
@ModelAttribute(CsrfRequestDataValueProcessor.DEFAULT_CSRF_ATTR_NAME)
20+
public Mono<CsrfToken> getCsrfToken(final ServerWebExchange exchange) {
21+
return exchange.getAttributeOrDefault(CsrfToken.class.getName(), Mono.empty());
22+
}
23+
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
org.springframework.boot.autoconfigure.EnableAutoConfiguration=graphql.kickstart.playground.boot.PlaygroundAutoConfiguration
1+
org.springframework.boot.autoconfigure.EnableAutoConfiguration=graphql.kickstart.playground.boot.PlaygroundAutoConfiguration,graphql.kickstart.playground.boot.PlaygroundWebFluxAutoConfiguration

playground-spring-boot-autoconfigure/src/test/java/graphql/kickstart/playground/boot/PlaygroundCdnCustomVersionTest.java

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,15 @@
1111
@SpringBootTest(classes = PlaygroundTestConfig.class)
1212
@AutoConfigureMockMvc
1313
@TestPropertySource("classpath:application-playground-cdn-custom-version-test.properties")
14-
class PlaygroundCdnCustomVersionTest extends PlaygroundResourcesTestBase {
14+
public class PlaygroundCdnCustomVersionTest extends PlaygroundResourcesTestBase {
1515

16-
@Test
17-
void shouldLoadSpecifiedVersionFromCdn() throws Exception {
18-
String LOGO_CDN_PATH = "https://cdn.jsdelivr.net/npm/graphql-playground-react@1.7.10/build/logo.png";
19-
String FAVICON_CDN_PATH = "https://cdn.jsdelivr.net/npm/graphql-playground-react@1.7.10/build/favicon.png";
20-
String SCRIPT_CDN_PATH = "https://cdn.jsdelivr.net/npm/graphql-playground-react@1.7.10/build/static/js/middleware.js";
21-
String CSS_CDN_PATH = "https://cdn.jsdelivr.net/npm/graphql-playground-react@1.7.10/build/static/css/index.css";
22-
testPlaygroundResources(CSS_CDN_PATH, SCRIPT_CDN_PATH, FAVICON_CDN_PATH, LOGO_CDN_PATH);
23-
}
16+
@Test
17+
public void shouldLoadSpecifiedVersionFromCdn() throws Exception {
18+
testPlaygroundResources(
19+
PlaygroundTestHelper.CUSTOM_VERSION_CSS_CDN_PATH,
20+
PlaygroundTestHelper.CUSTOM_VERSION_SCRIPT_CDN_PATH,
21+
PlaygroundTestHelper.CUSTOM_VERSION_FAVICON_CDN_PATH,
22+
PlaygroundTestHelper.CUSTOM_VERSION_LOGO_CDN_PATH
23+
);
24+
}
2425
}

playground-spring-boot-autoconfigure/src/test/java/graphql/kickstart/playground/boot/PlaygroundCdnTest.java

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,15 @@
1111
@SpringBootTest(classes = PlaygroundTestConfig.class)
1212
@AutoConfigureMockMvc
1313
@TestPropertySource("classpath:application-playground-cdn-test.properties")
14-
class PlaygroundCdnTest extends PlaygroundResourcesTestBase {
14+
public class PlaygroundCdnTest extends PlaygroundResourcesTestBase {
1515

16-
@Test
17-
void shouldLoadLatestVersionFromCdn() throws Exception {
18-
String LOGO_CDN_PATH = "https://cdn.jsdelivr.net/npm/graphql-playground-react@latest/build/logo.png";
19-
String FAVICON_CDN_PATH = "https://cdn.jsdelivr.net/npm/graphql-playground-react@latest/build/favicon.png";
20-
String SCRIPT_CDN_PATH = "https://cdn.jsdelivr.net/npm/graphql-playground-react@latest/build/static/js/middleware.js";
21-
String CSS_CDN_PATH = "https://cdn.jsdelivr.net/npm/graphql-playground-react@latest/build/static/css/index.css";
22-
testPlaygroundResources(CSS_CDN_PATH, SCRIPT_CDN_PATH, FAVICON_CDN_PATH, LOGO_CDN_PATH);
23-
}
16+
@Test
17+
public void shouldLoadLatestVersionFromCdn() throws Exception {
18+
testPlaygroundResources(
19+
PlaygroundTestHelper.DEFAULT_CSS_CDN_PATH,
20+
PlaygroundTestHelper.DEFAULT_SCRIPT_CDN_PATH,
21+
PlaygroundTestHelper.DEFAULT_FAVICON_CDN_PATH,
22+
PlaygroundTestHelper.DEFAULT_LOGO_CDN_PATH
23+
);
24+
}
2425
}

playground-spring-boot-autoconfigure/src/test/java/graphql/kickstart/playground/boot/PlaygroundCustomMappingTest.java

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -21,19 +21,17 @@
2121
@SpringBootTest(classes = PlaygroundTestConfig.class)
2222
@AutoConfigureMockMvc
2323
@TestPropertySource("classpath:application-playground-mapping-test.properties")
24-
class PlaygroundCustomMappingTest {
24+
public class PlaygroundCustomMappingTest {
2525

26-
private static final String CONFIGURED_MAPPING = "/test-mapping";
26+
@Autowired
27+
private MockMvc mockMvc;
2728

28-
@Autowired
29-
private MockMvc mockMvc;
30-
31-
@Test
32-
void shouldUseTheConfiguredRequestMapping() throws Exception {
33-
mockMvc.perform(get(CONFIGURED_MAPPING))
34-
.andExpect(status().isOk())
35-
.andExpect(content().contentTypeCompatibleWith(MediaType.TEXT_HTML))
36-
.andExpect(content().encoding(StandardCharsets.UTF_8.name()))
37-
.andExpect(content().string(not(isEmptyString())));
38-
}
29+
@Test
30+
public void shouldUseTheConfiguredRequestMapping() throws Exception {
31+
mockMvc.perform(get(PlaygroundTestHelper.CUSTOM_MAPPING))
32+
.andExpect(status().isOk())
33+
.andExpect(content().contentTypeCompatibleWith(MediaType.TEXT_HTML))
34+
.andExpect(content().encoding(StandardCharsets.UTF_8.name()))
35+
.andExpect(content().string(not(isEmptyString())));
36+
}
3937
}

playground-spring-boot-autoconfigure/src/test/java/graphql/kickstart/playground/boot/PlaygroundCustomStaticPathTest.java

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,15 @@
1111
@SpringBootTest(classes = PlaygroundTestConfig.class)
1212
@AutoConfigureMockMvc
1313
@TestPropertySource("classpath:application-playground-custom-static-path.properties")
14-
class PlaygroundCustomStaticPathTest extends PlaygroundResourcesTestBase {
14+
public class PlaygroundCustomStaticPathTest extends PlaygroundResourcesTestBase {
1515

16-
private static final String CUSTOM_CSS_URL = "/custom-static-path/static/css/index.css";
17-
private static final String CUSTOM_SCRIPT_URL = "/custom-static-path/static/js/middleware.js";
18-
private static final String CUSTOM_FAVICON_URL = "/custom-static-path/favicon.png";
19-
private static final String CUSTOM_LOGO_URL = "/custom-static-path/logo.png";
20-
21-
@Test
22-
void shouldLoadStaticResourcesFromCustomPath() throws Exception {
23-
testPlaygroundResources(CUSTOM_CSS_URL, CUSTOM_SCRIPT_URL, CUSTOM_FAVICON_URL, CUSTOM_LOGO_URL);
24-
}
16+
@Test
17+
public void shouldLoadStaticResourcesFromCustomPath() throws Exception {
18+
testPlaygroundResources(
19+
PlaygroundTestHelper.CUSTOM_LOCAL_CSS_URL,
20+
PlaygroundTestHelper.CUSTOM_LOCAL_SCRIPT_URL,
21+
PlaygroundTestHelper.CUSTOM_LOCAL_FAVICON_URL,
22+
PlaygroundTestHelper.CUSTOM_LOCAL_LOGO_URL
23+
);
24+
}
2525
}

playground-spring-boot-autoconfigure/src/test/java/graphql/kickstart/playground/boot/PlaygroundCustomTitleTest.java

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -20,22 +20,19 @@
2020
@SpringBootTest(classes = PlaygroundTestConfig.class)
2121
@AutoConfigureMockMvc
2222
@TestPropertySource("classpath:application-playground-custom-title.properties")
23-
class PlaygroundCustomTitleTest {
23+
public class PlaygroundCustomTitleTest {
2424

25-
private static final String CUSTOM_TITLE = "My CustomTest Title";
25+
@Autowired
26+
private MockMvc mockMvc;
2627

27-
@Autowired
28-
private MockMvc mockMvc;
28+
@Test
29+
public void shouldUseTheCustomPageTitle() throws Exception {
30+
final MvcResult mvcResult = mockMvc.perform(get(PlaygroundTestHelper.DEFAULT_PLAYGROUND_ENDPOINT))
31+
.andExpect(status().isOk())
32+
.andExpect(model().attribute(PlaygroundTestHelper.PAGE_TITLE_FIELD_NAME, PlaygroundTestHelper.CUSTOM_TITLE))
33+
.andReturn();
2934

30-
@Test
31-
void shouldUseTheCustomPageTitle() throws Exception {
32-
final MvcResult mvcResult = mockMvc
33-
.perform(get(PlaygroundTestHelper.DEFAULT_PLAYGROUND_ENDPOINT))
34-
.andExpect(status().isOk())
35-
.andExpect(model().attribute(PlaygroundTestHelper.PAGE_TITLE_FIELD_NAME, CUSTOM_TITLE))
36-
.andReturn();
37-
38-
final Document document = Jsoup.parse(mvcResult.getResponse().getContentAsString());
39-
PlaygroundTestHelper.assertTitle(document, CUSTOM_TITLE);
40-
}
35+
final Document document = Jsoup.parse(mvcResult.getResponse().getContentAsString());
36+
PlaygroundTestHelper.assertTitle(document, PlaygroundTestHelper.CUSTOM_TITLE);
37+
}
4138
}

playground-spring-boot-autoconfigure/src/test/java/graphql/kickstart/playground/boot/PlaygroundTestConfig.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,13 @@
33
import com.fasterxml.jackson.databind.ObjectMapper;
44
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
55
import org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration;
6+
import org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration;
67
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
78
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
89
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
910
import org.springframework.test.context.ContextConfiguration;
1011

11-
@EnableAutoConfiguration
12+
@EnableAutoConfiguration(exclude = WebFluxAutoConfiguration.class)
1213
@EnableWebSecurity
1314
@ContextConfiguration(classes = {PlaygroundAutoConfiguration.class, ObjectMapper.class,
1415
ThymeleafAutoConfiguration.class})

0 commit comments

Comments
 (0)