Support for invocation of interceptors
This commit is contained in:
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package org.springframework.boot.graphql;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import graphql.GraphQL;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
@@ -39,7 +41,7 @@ public class WebFluxGraphQLAutoConfiguration {
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public WebFluxGraphQLHandler graphQLHandler(GraphQL.Builder graphQLBuilder) {
|
||||
return new WebFluxGraphQLHandler(graphQLBuilder);
|
||||
return new WebFluxGraphQLHandler(graphQLBuilder, Collections.emptyList());
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package org.springframework.boot.graphql;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import graphql.GraphQL;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
@@ -42,7 +44,7 @@ public class WebMvcGraphQLAutoConfiguration {
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public WebMvcGraphQLHandler graphQLHandler(GraphQL.Builder graphQLBuilder) {
|
||||
return new WebMvcGraphQLHandler(graphQLBuilder);
|
||||
return new WebMvcGraphQLHandler(graphQLBuilder, Collections.emptyList());
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
*/
|
||||
package org.springframework.graphql;
|
||||
|
||||
import graphql.ExecutionInput;
|
||||
import graphql.ExecutionResult;
|
||||
import java.util.List;
|
||||
|
||||
import graphql.GraphQL;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
@@ -25,45 +25,24 @@ import org.springframework.web.reactive.function.server.ServerRequest;
|
||||
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||
|
||||
/**
|
||||
* GraphQL handler to be exposed as a WebFlux.fn endpoint via
|
||||
* GraphQL handler to expose as a WebFlux.fn endpoint via
|
||||
* {@link org.springframework.web.reactive.function.server.RouterFunctions}.
|
||||
*/
|
||||
public class WebFluxGraphQLHandler implements HandlerFunction<ServerResponse> {
|
||||
public class WebFluxGraphQLHandler extends WebHandlerSupport implements HandlerFunction<ServerResponse> {
|
||||
|
||||
private final GraphQL graphQL;
|
||||
|
||||
public WebFluxGraphQLHandler(GraphQL.Builder graphQLBuilder) {
|
||||
this.graphQL = graphQLBuilder.build();
|
||||
public WebFluxGraphQLHandler(GraphQL.Builder builder, List<WebInterceptor> interceptors) {
|
||||
super(builder, interceptors);
|
||||
}
|
||||
|
||||
|
||||
public Mono<ServerResponse> handle(ServerRequest request) {
|
||||
return request.bodyToMono(WebInput.MAP_PARAMETERIZED_TYPE_REF)
|
||||
.flatMap(body -> {
|
||||
WebInput webInput = new WebInput(
|
||||
request.uri(), request.headers().asHttpHeaders(), body);
|
||||
|
||||
ExecutionInput executionInput = ExecutionInput.newExecutionInput()
|
||||
.query(webInput.getQuery())
|
||||
.operationName(webInput.getOperationName())
|
||||
.variables(webInput.getVariables())
|
||||
.build();
|
||||
|
||||
// Invoke GraphQLInterceptor's preHandle here
|
||||
return extendInput(executionInput, webInput);
|
||||
WebInput webInput = new WebInput(request.uri(), request.headers().asHttpHeaders(), body);
|
||||
return executeQuery(webInput);
|
||||
})
|
||||
.flatMap(executionInput -> {
|
||||
// Invoke handleResult here
|
||||
return execute(executionInput);
|
||||
})
|
||||
.flatMap(result -> ServerResponse.ok().bodyValue(result.toSpecification()));
|
||||
}
|
||||
|
||||
protected Mono<ExecutionInput> extendInput(ExecutionInput executionInput, WebInput webInput) {
|
||||
return Mono.just(executionInput);
|
||||
}
|
||||
|
||||
protected Mono<ExecutionResult> execute(ExecutionInput input) {
|
||||
return Mono.fromFuture(graphQL.executeAsync(input));
|
||||
.flatMap(output -> ServerResponse.ok().bodyValue(output.toSpecification()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright 2020-2020 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;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import graphql.ExecutionInput;
|
||||
import graphql.ExecutionResult;
|
||||
import graphql.GraphQL;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* Base class for GraphQL over HTTP handlers.
|
||||
*/
|
||||
public abstract class WebHandlerSupport {
|
||||
|
||||
private final GraphQL graphQL;
|
||||
|
||||
private final List<WebInterceptor> interceptors;
|
||||
|
||||
|
||||
public WebHandlerSupport(GraphQL.Builder builder, List<WebInterceptor> interceptors) {
|
||||
this.graphQL = builder.build();
|
||||
this.interceptors = (!CollectionUtils.isEmpty(interceptors) ?
|
||||
Collections.unmodifiableList(new ArrayList<>(interceptors)) : Collections.emptyList());
|
||||
}
|
||||
|
||||
|
||||
public GraphQL getGraphQL() {
|
||||
return this.graphQL;
|
||||
}
|
||||
|
||||
public List<WebInterceptor> getInterceptors() {
|
||||
return this.interceptors;
|
||||
}
|
||||
|
||||
|
||||
protected Mono<WebOutput> executeQuery(WebInput webInput) {
|
||||
return createInputChain(webInput).flatMap(executionInput -> {
|
||||
Mono<ExecutionResult> resultMono = Mono.fromFuture(getGraphQL().executeAsync(executionInput));
|
||||
return createOutputChain(resultMono);
|
||||
});
|
||||
}
|
||||
|
||||
protected Mono<ExecutionInput> createInputChain(WebInput webInput) {
|
||||
Mono<ExecutionInput> preHandleMono = Mono.just(webInput.toExecutionInput());
|
||||
for (WebInterceptor interceptor : this.interceptors) {
|
||||
preHandleMono = preHandleMono.flatMap(input -> interceptor.preHandle(input, webInput));
|
||||
}
|
||||
return preHandleMono;
|
||||
}
|
||||
|
||||
protected Mono<WebOutput> createOutputChain(Mono<ExecutionResult> resultMono) {
|
||||
Mono<WebOutput> outputMono = resultMono.map(WebOutput::new);
|
||||
for (WebInterceptor interceptor : this.interceptors) {
|
||||
outputMono = outputMono.flatMap(interceptor::postHandle);
|
||||
}
|
||||
return outputMono;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -17,8 +17,12 @@ package org.springframework.graphql;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import graphql.ExecutionInput;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.lang.Nullable;
|
||||
@@ -50,7 +54,7 @@ public class WebInput {
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
WebInput(URI uri, HttpHeaders headers, Map<String, Object> body) {
|
||||
public WebInput(URI uri, HttpHeaders headers, Map<String, Object> body) {
|
||||
this.uri = UriComponentsBuilder.fromUri(uri).build(true);
|
||||
this.headers = headers;
|
||||
this.query = getAndValidateQuery(body);
|
||||
@@ -71,21 +75,29 @@ public class WebInput {
|
||||
return this.uri;
|
||||
}
|
||||
|
||||
public HttpHeaders getHeaders() {
|
||||
public HttpHeaders headers() {
|
||||
return this.headers;
|
||||
}
|
||||
|
||||
public String getQuery() {
|
||||
public String query() {
|
||||
return this.query;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getOperationName() {
|
||||
public String operationName() {
|
||||
return this.operationName;
|
||||
}
|
||||
|
||||
public Map<String, Object> getVariables() {
|
||||
public Map<String, Object> variables() {
|
||||
return this.variables;
|
||||
}
|
||||
|
||||
public ExecutionInput toExecutionInput() {
|
||||
return ExecutionInput.newExecutionInput()
|
||||
.query(query())
|
||||
.operationName(operationName())
|
||||
.variables(variables())
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,11 +16,11 @@
|
||||
package org.springframework.graphql;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
|
||||
import graphql.ExecutionInput;
|
||||
import graphql.ExecutionResult;
|
||||
import graphql.GraphQL;
|
||||
import reactor.core.publisher.Mono;
|
||||
@@ -32,17 +32,17 @@ import org.springframework.web.servlet.function.ServerRequest;
|
||||
import org.springframework.web.servlet.function.ServerResponse;
|
||||
|
||||
/**
|
||||
* GraphQL handler to be exposed as a WebMvc.fn endpoint via
|
||||
* GraphQL handler to expose as a WebMvc.fn endpoint via
|
||||
* {@link org.springframework.web.servlet.function.RouterFunctions}.
|
||||
*/
|
||||
public class WebMvcGraphQLHandler implements HandlerFunction<ServerResponse> {
|
||||
public class WebMvcGraphQLHandler extends WebHandlerSupport implements HandlerFunction<ServerResponse> {
|
||||
|
||||
private final GraphQL graphQL;
|
||||
|
||||
public WebMvcGraphQLHandler(GraphQL.Builder graphQL) {
|
||||
this.graphQL = graphQL.build();
|
||||
public WebMvcGraphQLHandler(GraphQL.Builder builder, List<WebInterceptor> interceptors) {
|
||||
super(builder, interceptors);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
@@ -50,38 +50,18 @@ public class WebMvcGraphQLHandler implements HandlerFunction<ServerResponse> {
|
||||
* e.g. {@link HttpMediaTypeNotSupportedException}.
|
||||
*/
|
||||
public ServerResponse handle(ServerRequest request) throws ServletException {
|
||||
WebInput webInput = createWebInput(request);
|
||||
|
||||
ExecutionInput executionInput = ExecutionInput.newExecutionInput()
|
||||
.query(webInput.getQuery())
|
||||
.operationName(webInput.getOperationName())
|
||||
.variables(webInput.getVariables())
|
||||
.build();
|
||||
|
||||
Mono<Map<String, Object>> body = extendInput(executionInput, webInput)
|
||||
.flatMap(this::execute)
|
||||
.map(ExecutionResult::toSpecification);
|
||||
|
||||
return ServerResponse.ok().body(body);
|
||||
WebInput webInput = new WebInput(request.uri(), request.headers().asHttpHeaders(), readBody(request));
|
||||
Mono<WebOutput> outputMono = executeQuery(webInput);
|
||||
return ServerResponse.ok().body(outputMono.map(ExecutionResult::toSpecification));
|
||||
}
|
||||
|
||||
private static WebInput createWebInput(ServerRequest request) throws ServletException {
|
||||
Map<String, Object> body;
|
||||
private static Map<String, Object> readBody(ServerRequest request) throws ServletException {
|
||||
try {
|
||||
body = request.body(WebInput.MAP_PARAMETERIZED_TYPE_REF);
|
||||
return request.body(WebInput.MAP_PARAMETERIZED_TYPE_REF);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new ServerWebInputException("I/O error while reading request body", null, ex);
|
||||
}
|
||||
return new WebInput(request.uri(), request.headers().asHttpHeaders(), body);
|
||||
}
|
||||
|
||||
protected Mono<ExecutionInput> extendInput(ExecutionInput executionInput, WebInput webInput) {
|
||||
return Mono.just(executionInput);
|
||||
}
|
||||
|
||||
protected Mono<ExecutionResult> execute(ExecutionInput input) {
|
||||
return Mono.fromFuture(this.graphQL.executeAsync(input));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -26,12 +26,16 @@ import graphql.GraphQLError;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
|
||||
/**
|
||||
* Simple wrapper around a GraphQL {@link ExecutionResult} that allows
|
||||
* {@link #transform(Consumer) transformation} via a {@link Builder Builder}.
|
||||
*/
|
||||
public class WebOutput implements ExecutionResult {
|
||||
|
||||
private final ExecutionResult executionResult;
|
||||
|
||||
|
||||
WebOutput(ExecutionResult executionResult) {
|
||||
public WebOutput(ExecutionResult executionResult) {
|
||||
this.executionResult = executionResult;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user