Auto-configure graphiql endpoint
Spring GraphQL ships with a static version of the graphiql IDE for exploring and querying GraphQL endpoints. See https://github.com/graphql/graphiql for more information. This commit auto-configures the GraphiQL handler for both MVC and WebFlux and points GraphiQL to the GraphQL HTTP endpoint exposed by the application. This feature is disabled by default and can be switched on with "spring.graphql.graphiql.enabled=true". See gh-29140
This commit is contained in:
@@ -47,20 +47,21 @@ import static org.hamcrest.Matchers.containsString;
|
||||
*/
|
||||
class GraphQlWebFluxAutoConfigurationTests {
|
||||
|
||||
private static final String BASE_URL = "https://spring.example.org/graphql";
|
||||
private static final String BASE_URL = "https://spring.example.org/";
|
||||
|
||||
private final ReactiveWebApplicationContextRunner contextRunner = new ReactiveWebApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(HttpHandlerAutoConfiguration.class, WebFluxAutoConfiguration.class,
|
||||
CodecsAutoConfiguration.class, JacksonAutoConfiguration.class, GraphQlAutoConfiguration.class,
|
||||
GraphQlWebFluxAutoConfiguration.class))
|
||||
.withUserConfiguration(DataFetchersConfiguration.class, CustomWebInterceptor.class).withPropertyValues(
|
||||
"spring.main.web-application-type=reactive", "spring.graphql.schema.printer.enabled=true");
|
||||
.withUserConfiguration(DataFetchersConfiguration.class, CustomWebInterceptor.class)
|
||||
.withPropertyValues("spring.main.web-application-type=reactive", "spring.graphql.graphiql.enabled=true",
|
||||
"spring.graphql.schema.printer.enabled=true");
|
||||
|
||||
@Test
|
||||
void simpleQueryShouldWork() {
|
||||
testWithWebClient((client) -> {
|
||||
String query = "{ bookById(id: \\\"book-1\\\"){ id name pageCount author } }";
|
||||
client.post().uri("").bodyValue("{ \"query\": \"" + query + "\"}").exchange().expectStatus().isOk()
|
||||
client.post().uri("/graphql").bodyValue("{ \"query\": \"" + query + "\"}").exchange().expectStatus().isOk()
|
||||
.expectBody().jsonPath("data.bookById.name").isEqualTo("GraphQL for beginners");
|
||||
});
|
||||
}
|
||||
@@ -69,19 +70,21 @@ class GraphQlWebFluxAutoConfigurationTests {
|
||||
void httpGetQueryShouldBeSupported() {
|
||||
testWithWebClient((client) -> {
|
||||
String query = "{ bookById(id: \\\"book-1\\\"){ id name pageCount author } }";
|
||||
client.get().uri("?query={query}", "{ \"query\": \"" + query + "\"}").exchange().expectStatus()
|
||||
client.get().uri("/graphql?query={query}", "{ \"query\": \"" + query + "\"}").exchange().expectStatus()
|
||||
.isEqualTo(HttpStatus.METHOD_NOT_ALLOWED).expectHeader().valueEquals("Allow", "POST");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRejectMissingQuery() {
|
||||
testWithWebClient((client) -> client.post().uri("").bodyValue("{}").exchange().expectStatus().isBadRequest());
|
||||
testWithWebClient(
|
||||
(client) -> client.post().uri("/graphql").bodyValue("{}").exchange().expectStatus().isBadRequest());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRejectQueryWithInvalidJson() {
|
||||
testWithWebClient((client) -> client.post().uri("").bodyValue(":)").exchange().expectStatus().isBadRequest());
|
||||
testWithWebClient(
|
||||
(client) -> client.post().uri("/graphql").bodyValue(":)").exchange().expectStatus().isBadRequest());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -89,18 +92,28 @@ class GraphQlWebFluxAutoConfigurationTests {
|
||||
testWithWebClient((client) -> {
|
||||
String query = "{ bookById(id: \\\"book-1\\\"){ id name pageCount author } }";
|
||||
|
||||
client.post().uri("").bodyValue("{ \"query\": \"" + query + "\"}").exchange().expectStatus().isOk()
|
||||
client.post().uri("/graphql").bodyValue("{ \"query\": \"" + query + "\"}").exchange().expectStatus().isOk()
|
||||
.expectHeader().valueEquals("X-Custom-Header", "42");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldExposeSchemaEndpoint() {
|
||||
testWithWebClient((client) -> client.get().uri("/schema").accept(MediaType.ALL).exchange()
|
||||
testWithWebClient((client) -> client.get().uri("/graphql/schema").accept(MediaType.ALL).exchange()
|
||||
.expectStatus().isOk().expectHeader().contentType(MediaType.TEXT_PLAIN).expectBody(String.class)
|
||||
.value(containsString("type Book")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldExposeGraphiqlEndpoint() {
|
||||
testWithWebClient((client) -> {
|
||||
client.get().uri("/graphiql").exchange().expectStatus().is3xxRedirection().expectHeader()
|
||||
.location("https://spring.example.org/graphiql?path=/graphql");
|
||||
client.get().uri("/graphiql?path=/graphql").accept(MediaType.ALL).exchange().expectStatus().isOk()
|
||||
.expectHeader().contentType(MediaType.TEXT_HTML);
|
||||
});
|
||||
}
|
||||
|
||||
private void testWithWebClient(Consumer<WebTestClient> consumer) {
|
||||
this.contextRunner.run((context) -> {
|
||||
WebTestClient client = WebTestClient.bindToApplicationContext(context).configureClient()
|
||||
|
||||
@@ -43,6 +43,7 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilder
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
/**
|
||||
@@ -57,8 +58,9 @@ class GraphQlWebMvcAutoConfigurationTests {
|
||||
AutoConfigurations.of(DispatcherServletAutoConfiguration.class, WebMvcAutoConfiguration.class,
|
||||
HttpMessageConvertersAutoConfiguration.class, JacksonAutoConfiguration.class,
|
||||
GraphQlAutoConfiguration.class, GraphQlWebMvcAutoConfiguration.class))
|
||||
.withUserConfiguration(DataFetchersConfiguration.class, CustomWebInterceptor.class).withPropertyValues(
|
||||
"spring.main.web-application-type=servlet", "spring.graphql.schema.printer.enabled=true");
|
||||
.withUserConfiguration(DataFetchersConfiguration.class, CustomWebInterceptor.class)
|
||||
.withPropertyValues("spring.main.web-application-type=servlet", "spring.graphql.graphiql.enabled=true",
|
||||
"spring.graphql.schema.printer.enabled=true");
|
||||
|
||||
@Test
|
||||
void simpleQueryShouldWork() {
|
||||
@@ -107,6 +109,16 @@ class GraphQlWebMvcAutoConfigurationTests {
|
||||
.andExpect(content().string(Matchers.containsString("type Book"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldExposeGraphiqlEndpoint() {
|
||||
testWith((mockMvc) -> {
|
||||
mockMvc.perform(get("/graphiql")).andExpect(status().is3xxRedirection())
|
||||
.andExpect(redirectedUrl("http://localhost/graphiql?path=/graphql"));
|
||||
mockMvc.perform(get("/graphiql?path=/graphql")).andExpect(status().isOk())
|
||||
.andExpect(content().contentType(MediaType.TEXT_HTML));
|
||||
});
|
||||
}
|
||||
|
||||
private void testWith(MockMvcConsumer mockMvcConsumer) {
|
||||
this.contextRunner.run((context) -> {
|
||||
MediaType mediaType = MediaType.APPLICATION_JSON;
|
||||
|
||||
Reference in New Issue
Block a user