Skip to content

Commit

Permalink
WebInterceptor contract revision
Browse files Browse the repository at this point in the history
WebInterceptor now uses delegation, forming a chain of interceptors
followed by a GraphQLService at the end to invoke graphql.GraphQL.

Closes spring-iogh-49
  • Loading branch information
rstoyanchev committed Apr 30, 2021
1 parent e5a92a3 commit f93c46e
Show file tree
Hide file tree
Showing 29 changed files with 402 additions and 407 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.graphql.boot;

import graphql.GraphQL;

import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.graphql.GraphQLService;
import org.springframework.graphql.support.ExecutionGraphQLService;
import org.springframework.graphql.support.GraphQLSource;

@Configuration
@ConditionalOnClass(GraphQL.class)
@ConditionalOnMissingBean(GraphQLService.class)
@AutoConfigureAfter(GraphQLAutoConfiguration.class)
public class GraphQLServiceAutoConfiguration {

@Bean
@ConditionalOnMissingBean
public GraphQLService graphQLService(GraphQLSource graphQLSource) {
return new ExecutionGraphQLService(graphQLSource);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.graphql.GraphQLService;
import org.springframework.graphql.support.GraphQLSource;
import org.springframework.graphql.web.DefaultWebGraphQLService;
import org.springframework.graphql.web.WebGraphQLService;
import org.springframework.graphql.web.WebGraphQLHandler;
import org.springframework.graphql.web.WebInterceptor;
import org.springframework.graphql.web.webflux.GraphQLHttpHandler;
import org.springframework.graphql.web.webflux.GraphQLWebSocketHandler;
Expand Down Expand Up @@ -64,16 +64,14 @@ public class WebFluxGraphQLAutoConfiguration {

@Bean
@ConditionalOnMissingBean
public WebGraphQLService webGraphQLService(GraphQLSource graphQLSource, ObjectProvider<WebInterceptor> interceptors) {
DefaultWebGraphQLService handler = new DefaultWebGraphQLService(graphQLSource);
handler.setInterceptors(interceptors.orderedStream().collect(Collectors.toList()));
return handler;
public WebGraphQLHandler webGraphQLHandler(ObjectProvider<WebInterceptor> interceptors, GraphQLService service) {
return WebInterceptor.createHandler(interceptors.orderedStream().collect(Collectors.toList()), service);
}

@Bean
@ConditionalOnMissingBean
public GraphQLHttpHandler graphQLHandler(WebGraphQLService service) {
return new GraphQLHttpHandler(service);
public GraphQLHttpHandler graphQLHttpHandler(WebGraphQLHandler webGraphQLHandler) {
return new GraphQLHttpHandler(webGraphQLHandler);
}

@Bean
Expand All @@ -99,22 +97,22 @@ static class WebSocketConfiguration {
@Bean
@ConditionalOnMissingBean
public GraphQLWebSocketHandler graphQLWebSocketHandler(
WebGraphQLService service, GraphQLProperties properties, ServerCodecConfigurer configurer) {
WebGraphQLHandler webGraphQLHandler, GraphQLProperties properties, ServerCodecConfigurer configurer) {

return new GraphQLWebSocketHandler(
service, configurer, properties.getWebsocket().getConnectionInitTimeout());
webGraphQLHandler, configurer, properties.getWebsocket().getConnectionInitTimeout());
}

@Bean
public HandlerMapping graphQLWebSocketEndpoint(
GraphQLWebSocketHandler handler, GraphQLProperties properties) {
GraphQLWebSocketHandler graphQLWebSocketHandler, GraphQLProperties properties) {

String path = properties.getWebsocket().getPath();
if (logger.isInfoEnabled()) {
logger.info("GraphQL endpoint WebSocket " + path);
}
WebSocketHandlerMapping handlerMapping = new WebSocketHandlerMapping();
handlerMapping.setUrlMap(Collections.singletonMap(path, handler));
handlerMapping.setUrlMap(Collections.singletonMap(path, graphQLWebSocketHandler));
handlerMapping.setOrder(-2); // Ahead of HTTP endpoint ("routerFunctionMapping" bean)
return handlerMapping;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,9 @@
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.graphql.GraphQLService;
import org.springframework.graphql.support.GraphQLSource;
import org.springframework.graphql.web.DefaultWebGraphQLService;
import org.springframework.graphql.web.WebGraphQLService;
import org.springframework.graphql.web.WebGraphQLHandler;
import org.springframework.graphql.web.WebInterceptor;
import org.springframework.graphql.web.webmvc.GraphQLHttpHandler;
import org.springframework.graphql.web.webmvc.GraphQLWebSocketHandler;
Expand Down Expand Up @@ -71,16 +71,14 @@ public class WebMvcGraphQLAutoConfiguration {

@Bean
@ConditionalOnMissingBean
public WebGraphQLService webGraphQLService(GraphQLSource graphQLSource, ObjectProvider<WebInterceptor> interceptors) {
DefaultWebGraphQLService handler = new DefaultWebGraphQLService(graphQLSource);
handler.setInterceptors(interceptors.orderedStream().collect(Collectors.toList()));
return handler;
public WebGraphQLHandler webGraphQLHandler(ObjectProvider<WebInterceptor> interceptors, GraphQLService service) {
return WebInterceptor.createHandler(interceptors.orderedStream().collect(Collectors.toList()), service);
}

@Bean
@ConditionalOnMissingBean
public GraphQLHttpHandler graphQLHandler(WebGraphQLService service) {
return new GraphQLHttpHandler(service);
public GraphQLHttpHandler graphQLHttpHandler(WebGraphQLHandler webGraphQLHandler) {
return new GraphQLHttpHandler(webGraphQLHandler);
}

@Bean
Expand Down Expand Up @@ -108,15 +106,15 @@ static class WebSocketConfiguration {
@Bean
@ConditionalOnMissingBean
public GraphQLWebSocketHandler graphQLWebSocketHandler(
WebGraphQLService service, GraphQLProperties properties, HttpMessageConverters converters) {
WebGraphQLHandler webGraphQLHandler, GraphQLProperties properties, HttpMessageConverters converters) {

HttpMessageConverter<?> converter = converters.getConverters().stream()
.filter(candidate -> candidate.canRead(Map.class, MediaType.APPLICATION_JSON))
.findFirst()
.orElseThrow(() -> new IllegalStateException("No JSON converter"));

return new GraphQLWebSocketHandler(
service, converter, properties.getWebsocket().getConnectionInitTimeout());
webGraphQLHandler, converter, properties.getWebsocket().getConnectionInitTimeout());
}

@Bean
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.graphql.boot.actuate.metrics.GraphQLMetricsAutoConfiguration,\
org.springframework.graphql.boot.GraphQLAutoConfiguration,\
org.springframework.graphql.boot.GraphQLServiceAutoConfiguration,\
org.springframework.graphql.boot.WebFluxGraphQLAutoConfiguration,\
org.springframework.graphql.boot.WebMvcGraphQLAutoConfiguration
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
import java.util.function.Consumer;

import org.junit.jupiter.api.Test;
import reactor.core.publisher.Mono;

import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration;
Expand All @@ -32,7 +31,6 @@
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.graphql.web.WebInterceptor;
import org.springframework.graphql.web.WebOutput;
import org.springframework.http.MediaType;
import org.springframework.test.web.reactive.server.WebTestClient;

Expand All @@ -43,7 +41,8 @@ class WebFluxApplicationContextTests {
private static final AutoConfigurations AUTO_CONFIGURATIONS = AutoConfigurations.of(
HttpHandlerAutoConfiguration.class, WebFluxAutoConfiguration.class,
CodecsAutoConfiguration.class, JacksonAutoConfiguration.class,
GraphQLAutoConfiguration.class, WebFluxGraphQLAutoConfiguration.class);
GraphQLAutoConfiguration.class, GraphQLServiceAutoConfiguration.class,
WebFluxGraphQLAutoConfiguration.class);

private static final String BASE_URL = "https://spring.example.org/graphql";

Expand Down Expand Up @@ -139,12 +138,8 @@ static class CustomWebInterceptor {

@Bean
public WebInterceptor customWebInterceptor() {
return new WebInterceptor() {
@Override
public Mono<WebOutput> postHandle(WebOutput output) {
return Mono.just(output.transform(builder -> builder.responseHeader("X-Custom-Header", "42")));
}
};
return (input, next) -> next.handle(input).map(output ->
output.transform(builder -> builder.responseHeader("X-Custom-Header", "42")));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@


import org.junit.jupiter.api.Test;
import reactor.core.publisher.Mono;

import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration;
Expand All @@ -29,7 +28,6 @@
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.graphql.web.WebInterceptor;
import org.springframework.graphql.web.WebOutput;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
Expand All @@ -39,7 +37,8 @@
import static graphql.schema.idl.TypeRuntimeWiring.newTypeWiring;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.asyncDispatch;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
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.status;

Expand All @@ -48,7 +47,8 @@ class WebMvcApplicationContextTests {
public static final AutoConfigurations AUTO_CONFIGURATIONS = AutoConfigurations.of(
DispatcherServletAutoConfiguration.class, WebMvcAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class, JacksonAutoConfiguration.class,
GraphQLAutoConfiguration.class, WebMvcGraphQLAutoConfiguration.class);
GraphQLAutoConfiguration.class, GraphQLServiceAutoConfiguration.class,
WebMvcGraphQLAutoConfiguration.class);

@Test
void endpointHandlesGraphQLQuery() {
Expand Down Expand Up @@ -135,13 +135,8 @@ static class CustomWebInterceptor {

@Bean
public WebInterceptor customWebInterceptor() {
return new WebInterceptor() {
@Override
public Mono<WebOutput> postHandle(WebOutput output) {
return Mono.just(output.transform(builder ->
builder.responseHeader("X-Custom-Header", "42")));
}
};
return (input, next) -> next.handle(input).map(output ->
output.transform(builder -> builder.responseHeader("X-Custom-Header", "42")));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.graphql.web.WebGraphQLService;
import org.springframework.graphql.web.WebGraphQLHandler;
import org.springframework.graphql.test.tester.GraphQLTester;

/**
Expand All @@ -34,9 +34,9 @@ public class QueryTests {


@BeforeEach
public void setUp(@Autowired WebGraphQLService service) {
public void setUp(@Autowired WebGraphQLHandler handler) {
this.graphQLTester = GraphQLTester.create(webInput ->
service.execute(webInput).contextWrite(context -> context.put("name", "James")));
handler.handle(webInput).contextWrite(context -> context.put("name", "James")));
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.graphql.web.WebGraphQLService;
import org.springframework.graphql.web.WebGraphQLHandler;
import org.springframework.graphql.test.tester.GraphQLTester;

/**
Expand All @@ -36,9 +36,9 @@ public class SubscriptionTests {


@BeforeEach
public void setUp(@Autowired WebGraphQLService service) {
public void setUp(@Autowired WebGraphQLHandler handler) {
this.graphQLTester = GraphQLTester.create(webInput ->
service.execute(webInput).contextWrite(context -> context.put("name", "James")));
handler.handle(webInput).contextWrite(context -> context.put("name", "James")));
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@

import org.springframework.core.ParameterizedTypeReference;
import org.springframework.graphql.RequestInput;
import org.springframework.graphql.web.WebGraphQLService;
import org.springframework.graphql.web.WebGraphQLHandler;
import org.springframework.graphql.web.WebInput;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
Expand Down Expand Up @@ -83,9 +83,9 @@ class DefaultGraphQLTester implements GraphQLTester {
this.requestStrategy = new WebTestClientRequestStrategy(client, this.jsonPathConfig);
}

DefaultGraphQLTester(WebGraphQLService service) {
DefaultGraphQLTester(WebGraphQLHandler handler) {
this.jsonPathConfig = initJsonPathConfig();
this.requestStrategy = new DirectRequestStrategy(service, this.jsonPathConfig);
this.requestStrategy = new DirectRequestStrategy(handler, this.jsonPathConfig);
}

private Configuration initJsonPathConfig() {
Expand Down Expand Up @@ -182,12 +182,12 @@ private static class DirectRequestStrategy implements RequestStrategy {
private static final Duration DEFAULT_TIMEOUT = Duration.ofSeconds(5);


private final WebGraphQLService graphQLService;
private final WebGraphQLHandler graphQLHandler;

private final Configuration jsonPathConfig;

public DirectRequestStrategy(WebGraphQLService service, Configuration jsonPathConfig) {
this.graphQLService = service;
public DirectRequestStrategy(WebGraphQLHandler handler, Configuration jsonPathConfig) {
this.graphQLHandler = handler;
this.jsonPathConfig = jsonPathConfig;
}

Expand All @@ -213,7 +213,7 @@ public SubscriptionSpec executeSubscription(RequestInput input) {

private ExecutionResult executeInternal(RequestInput input) {
WebInput webInput = new WebInput(DEFAULT_URL, DEFAULT_HEADERS, input.toMap(), null);
ExecutionResult result = this.graphQLService.execute(webInput).block(DEFAULT_TIMEOUT);
ExecutionResult result = this.graphQLHandler.handle(webInput).block(DEFAULT_TIMEOUT);
Assert.notNull(result, "Expected ExecutionResult");
return result;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,14 @@
import reactor.core.publisher.Flux;

import org.springframework.core.ParameterizedTypeReference;
import org.springframework.graphql.web.WebGraphQLService;
import org.springframework.graphql.web.WebGraphQLHandler;
import org.springframework.lang.Nullable;
import org.springframework.test.web.reactive.server.WebTestClient;

/**
* Main entry point for testing GraphQL with requests performed via
* {@link WebTestClient} as an HTTP client or via any {@link WebGraphQLService}.
* Main entry point for testing GraphQL with requests performed either as an
* HTTP client via {@link WebTestClient} or directly via a
* {@link WebGraphQLHandler}.
*
*
* <p>GraphQL requests to Spring MVC without an HTTP server:
Expand Down Expand Up @@ -75,16 +76,16 @@
* }
* </pre>
*
* <p>GraphQL requests to any {@link WebGraphQLService}:
* <p>GraphQL requests to any {@link WebGraphQLHandler}:
* <pre class="code">
* &#064;SpringBootTest
* public class MyTests {
*
* private GraphQLTester graphQLTester;
*
* &#064;BeforeEach
* public void setUp(&#064;Autowired WebGraphQLService service) {
* this.graphQLTester = GraphQLTester.create(service);
* public void setUp(&#064;Autowired WebGraphQLHandler handler) {
* this.graphQLTester = GraphQLTester.create(handler);
* }
* </pre>
*/
Expand Down Expand Up @@ -112,13 +113,13 @@ static GraphQLTester create(WebTestClient client) {
}

/**
* Create a {@code GraphQLTester} that performs GraphQL requests through the
* given {@link WebGraphQLService}.
* @param service the handler to execute requests with
* Create a {@code GraphQLTester} that performs GraphQL requests through
* the given {@link WebGraphQLHandler}.
* @param handler the handler to execute requests with
* @return the created {@code GraphQLTester} instance
*/
static GraphQLTester create(WebGraphQLService service) {
return new DefaultGraphQLTester(service);
static GraphQLTester create(WebGraphQLHandler handler) {
return new DefaultGraphQLTester(handler);
}


Expand Down
Loading

0 comments on commit f93c46e

Please sign in to comment.