diff --git a/build.gradle b/build.gradle
index 0c07156..38b013f 100644
--- a/build.gradle
+++ b/build.gradle
@@ -43,6 +43,7 @@ subprojects {
springVersion = "5.1.2.RELEASE"
springBootVersion = "2.1.0.RELEASE"
jacksonVersion = "2.9.6"
+ assertJVersion = "3.11.1"
}
repositories {
diff --git a/graphql-java-spring-webflux/build.gradle b/graphql-java-spring-webflux/build.gradle
index 00601bf..903398e 100644
--- a/graphql-java-spring-webflux/build.gradle
+++ b/graphql-java-spring-webflux/build.gradle
@@ -6,6 +6,7 @@ dependencies {
compile "com.fasterxml.jackson.core:jackson-databind:$jacksonVersion"
compile "com.graphql-java:graphql-java:$graphqlJavaVersion"
+ testCompile("org.assertj:assertj-core:$assertJVersion")
testCompile group: 'junit', name: 'junit', version: '4.12'
testCompile "org.springframework:spring-test:$springVersion"
testCompile group: 'javax.servlet', name: 'javax.servlet-api', version: '4.0.1'
diff --git a/graphql-java-spring-webflux/src/main/java/graphql/spring/web/reactive/ExecutionInputCustomizer.java b/graphql-java-spring-webflux/src/main/java/graphql/spring/web/reactive/ExecutionInputCustomizer.java
new file mode 100644
index 0000000..09712df
--- /dev/null
+++ b/graphql-java-spring-webflux/src/main/java/graphql/spring/web/reactive/ExecutionInputCustomizer.java
@@ -0,0 +1,19 @@
+package graphql.spring.web.reactive;
+
+import graphql.ExecutionInput;
+import graphql.PublicApi;
+import org.springframework.web.server.ServerWebExchange;
+import reactor.core.publisher.Mono;
+
+/**
+ * Lets you customize the #ExecutionInput before the query is executed.
+ * You can for example set a context object or define a root value.
+ *
+ * This is only used if you use the default {@link GraphQLInvocation}.
+ */
+@PublicApi
+public interface ExecutionInputCustomizer {
+
+ Mono customizeExecutionInput(ExecutionInput executionInput, ServerWebExchange webRequest);
+
+}
diff --git a/graphql-java-spring-webflux/src/main/java/graphql/spring/web/reactive/GraphQLInvocation.java b/graphql-java-spring-webflux/src/main/java/graphql/spring/web/reactive/GraphQLInvocation.java
index 0e69810..78664b9 100644
--- a/graphql-java-spring-webflux/src/main/java/graphql/spring/web/reactive/GraphQLInvocation.java
+++ b/graphql-java-spring-webflux/src/main/java/graphql/spring/web/reactive/GraphQLInvocation.java
@@ -8,6 +8,6 @@
@PublicApi
public interface GraphQLInvocation {
- Mono invoke(GraphQLInvocationData invocationData, ServerWebExchange webRequest);
+ Mono invoke(GraphQLInvocationData invocationData, ServerWebExchange serverWebExchange);
}
diff --git a/graphql-java-spring-webflux/src/main/java/graphql/spring/web/reactive/components/DefaultExecutionInputCustomizer.java b/graphql-java-spring-webflux/src/main/java/graphql/spring/web/reactive/components/DefaultExecutionInputCustomizer.java
new file mode 100644
index 0000000..05732b9
--- /dev/null
+++ b/graphql-java-spring-webflux/src/main/java/graphql/spring/web/reactive/components/DefaultExecutionInputCustomizer.java
@@ -0,0 +1,18 @@
+package graphql.spring.web.reactive.components;
+
+import graphql.ExecutionInput;
+import graphql.Internal;
+import graphql.spring.web.reactive.ExecutionInputCustomizer;
+import org.springframework.stereotype.Component;
+import org.springframework.web.server.ServerWebExchange;
+import reactor.core.publisher.Mono;
+
+@Component
+@Internal
+public class DefaultExecutionInputCustomizer implements ExecutionInputCustomizer {
+
+ @Override
+ public Mono customizeExecutionInput(ExecutionInput executionInput, ServerWebExchange webRequest) {
+ return Mono.just(executionInput);
+ }
+}
diff --git a/graphql-java-spring-webflux/src/main/java/graphql/spring/web/reactive/components/DefaultGraphQLInvocation.java b/graphql-java-spring-webflux/src/main/java/graphql/spring/web/reactive/components/DefaultGraphQLInvocation.java
index 5d0c5a0..196c835 100644
--- a/graphql-java-spring-webflux/src/main/java/graphql/spring/web/reactive/components/DefaultGraphQLInvocation.java
+++ b/graphql-java-spring-webflux/src/main/java/graphql/spring/web/reactive/components/DefaultGraphQLInvocation.java
@@ -4,6 +4,7 @@
import graphql.ExecutionResult;
import graphql.GraphQL;
import graphql.Internal;
+import graphql.spring.web.reactive.ExecutionInputCustomizer;
import graphql.spring.web.reactive.GraphQLInvocation;
import graphql.spring.web.reactive.GraphQLInvocationData;
import org.springframework.beans.factory.annotation.Autowired;
@@ -16,7 +17,10 @@
public class DefaultGraphQLInvocation implements GraphQLInvocation {
@Autowired
- private GraphQL graphQL;
+ GraphQL graphQL;
+
+ @Autowired
+ ExecutionInputCustomizer executionInputCustomizer;
@Override
public Mono invoke(GraphQLInvocationData invocationData, ServerWebExchange serverWebExchange) {
@@ -25,7 +29,8 @@ public Mono invoke(GraphQLInvocationData invocationData, Server
.operationName(invocationData.getOperationName())
.variables(invocationData.getVariables())
.build();
- return Mono.fromCompletionStage(graphQL.executeAsync(executionInput));
+ Mono customizedExecutionInputMono = executionInputCustomizer.customizeExecutionInput(executionInput, serverWebExchange);
+ return customizedExecutionInputMono.flatMap(customizedExecutionInput -> Mono.fromCompletionStage(graphQL.executeAsync(customizedExecutionInput)));
}
}
diff --git a/graphql-java-spring-webflux/src/test/java/graphql/spring/web/reactive/components/DefaultGraphQLInvocationTest.java b/graphql-java-spring-webflux/src/test/java/graphql/spring/web/reactive/components/DefaultGraphQLInvocationTest.java
new file mode 100644
index 0000000..2e3d9ef
--- /dev/null
+++ b/graphql-java-spring-webflux/src/test/java/graphql/spring/web/reactive/components/DefaultGraphQLInvocationTest.java
@@ -0,0 +1,62 @@
+package graphql.spring.web.reactive.components;
+
+import graphql.ExecutionInput;
+import graphql.ExecutionResult;
+import graphql.GraphQL;
+import graphql.spring.web.reactive.ExecutionInputCustomizer;
+import graphql.spring.web.reactive.GraphQLInvocationData;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.springframework.web.server.ServerWebExchange;
+import reactor.core.publisher.Mono;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import static java.util.concurrent.CompletableFuture.completedFuture;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class DefaultGraphQLInvocationTest {
+
+
+ @Test
+ public void testCustomizerIsCalled() {
+
+ String query = "query myQuery {foo}";
+ String operationName = "myQuery";
+ Map variables = new LinkedHashMap<>();
+
+ DefaultGraphQLInvocation defaultGraphQLInvocation = new DefaultGraphQLInvocation();
+ ExecutionInputCustomizer executionInputCustomizer = mock(ExecutionInputCustomizer.class);
+ defaultGraphQLInvocation.executionInputCustomizer = executionInputCustomizer;
+ GraphQL graphQL = mock(GraphQL.class);
+ defaultGraphQLInvocation.graphQL = graphQL;
+ ExecutionResult executionResult = mock(ExecutionResult.class);
+ when(graphQL.executeAsync(any(ExecutionInput.class))).thenReturn(completedFuture(executionResult));
+
+ GraphQLInvocationData graphQLInvocationData = new GraphQLInvocationData(query, operationName, variables);
+ ServerWebExchange serverWebExchange = mock(ServerWebExchange.class);
+
+ ArgumentCaptor captor1 = ArgumentCaptor.forClass(ExecutionInput.class);
+ ArgumentCaptor captor2 = ArgumentCaptor.forClass(ServerWebExchange.class);
+ ExecutionInput executionInputResult = mock(ExecutionInput.class);
+ when(executionInputCustomizer.customizeExecutionInput(captor1.capture(), captor2.capture())).thenReturn(Mono.just(executionInputResult));
+
+ Mono invoke = defaultGraphQLInvocation.invoke(graphQLInvocationData, serverWebExchange);
+
+ assertThat(captor1.getValue().getQuery()).isEqualTo(query);
+ assertThat(captor1.getValue().getOperationName()).isEqualTo(operationName);
+ assertThat(captor1.getValue().getVariables()).isSameAs(variables);
+
+ invoke.block();
+
+ verify(graphQL).executeAsync(executionInputResult);
+
+ }
+
+
+}
\ No newline at end of file
diff --git a/graphql-java-spring-webmvc/build.gradle b/graphql-java-spring-webmvc/build.gradle
index b207610..c977326 100644
--- a/graphql-java-spring-webmvc/build.gradle
+++ b/graphql-java-spring-webmvc/build.gradle
@@ -6,6 +6,7 @@ dependencies {
compile "com.fasterxml.jackson.core:jackson-databind:$jacksonVersion"
compile "com.graphql-java:graphql-java:$graphqlJavaVersion"
+ testCompile("org.assertj:assertj-core:$assertJVersion")
testCompile group: 'junit', name: 'junit', version: '4.12'
testCompile "org.springframework:spring-test:$springVersion"
testCompile group: 'javax.servlet', name: 'javax.servlet-api', version: '4.0.1'
diff --git a/graphql-java-spring-webmvc/src/main/java/graphql/spring/web/servlet/ExecutionInputCustomizer.java b/graphql-java-spring-webmvc/src/main/java/graphql/spring/web/servlet/ExecutionInputCustomizer.java
new file mode 100644
index 0000000..91e0cb7
--- /dev/null
+++ b/graphql-java-spring-webmvc/src/main/java/graphql/spring/web/servlet/ExecutionInputCustomizer.java
@@ -0,0 +1,20 @@
+package graphql.spring.web.servlet;
+
+import graphql.ExecutionInput;
+import graphql.PublicApi;
+import org.springframework.web.context.request.WebRequest;
+
+import java.util.concurrent.CompletableFuture;
+
+/**
+ * Lets you customize the #ExecutionInput before the query is executed.
+ * You can for example set a context object or define a root value.
+ *
+ * This is only used if you use the default {@link GraphQLInvocation}.
+ */
+@PublicApi
+public interface ExecutionInputCustomizer {
+
+ CompletableFuture customizeExecutionInput(ExecutionInput executionInput, WebRequest webRequest);
+
+}
diff --git a/graphql-java-spring-webmvc/src/main/java/graphql/spring/web/servlet/components/DefaultExecutionInputCustomizer.java b/graphql-java-spring-webmvc/src/main/java/graphql/spring/web/servlet/components/DefaultExecutionInputCustomizer.java
new file mode 100644
index 0000000..cf75683
--- /dev/null
+++ b/graphql-java-spring-webmvc/src/main/java/graphql/spring/web/servlet/components/DefaultExecutionInputCustomizer.java
@@ -0,0 +1,19 @@
+package graphql.spring.web.servlet.components;
+
+import graphql.ExecutionInput;
+import graphql.Internal;
+import graphql.spring.web.servlet.ExecutionInputCustomizer;
+import org.springframework.stereotype.Component;
+import org.springframework.web.context.request.WebRequest;
+
+import java.util.concurrent.CompletableFuture;
+
+@Component
+@Internal
+public class DefaultExecutionInputCustomizer implements ExecutionInputCustomizer {
+
+ @Override
+ public CompletableFuture customizeExecutionInput(ExecutionInput executionInput, WebRequest webRequest) {
+ return CompletableFuture.completedFuture(executionInput);
+ }
+}
diff --git a/graphql-java-spring-webmvc/src/main/java/graphql/spring/web/servlet/components/DefaultGraphQLInvocation.java b/graphql-java-spring-webmvc/src/main/java/graphql/spring/web/servlet/components/DefaultGraphQLInvocation.java
index 4a3ef15..9e5dfe7 100644
--- a/graphql-java-spring-webmvc/src/main/java/graphql/spring/web/servlet/components/DefaultGraphQLInvocation.java
+++ b/graphql-java-spring-webmvc/src/main/java/graphql/spring/web/servlet/components/DefaultGraphQLInvocation.java
@@ -4,6 +4,7 @@
import graphql.ExecutionResult;
import graphql.GraphQL;
import graphql.Internal;
+import graphql.spring.web.servlet.ExecutionInputCustomizer;
import graphql.spring.web.servlet.GraphQLInvocation;
import graphql.spring.web.servlet.GraphQLInvocationData;
import org.springframework.beans.factory.annotation.Autowired;
@@ -17,7 +18,10 @@
public class DefaultGraphQLInvocation implements GraphQLInvocation {
@Autowired
- private GraphQL graphQL;
+ GraphQL graphQL;
+
+ @Autowired
+ ExecutionInputCustomizer executionInputCustomizer;
@Override
public CompletableFuture invoke(GraphQLInvocationData invocationData, WebRequest webRequest) {
@@ -26,7 +30,8 @@ public CompletableFuture invoke(GraphQLInvocationData invocatio
.operationName(invocationData.getOperationName())
.variables(invocationData.getVariables())
.build();
- return graphQL.executeAsync(executionInput);
+ CompletableFuture customizedExecutionInput = executionInputCustomizer.customizeExecutionInput(executionInput, webRequest);
+ return customizedExecutionInput.thenCompose(graphQL::executeAsync);
}
}
diff --git a/graphql-java-spring-webmvc/src/test/java/graphql/spring/web/servlet/components/DefaultGraphQLInvocationTest.java b/graphql-java-spring-webmvc/src/test/java/graphql/spring/web/servlet/components/DefaultGraphQLInvocationTest.java
new file mode 100644
index 0000000..6d2a117
--- /dev/null
+++ b/graphql-java-spring-webmvc/src/test/java/graphql/spring/web/servlet/components/DefaultGraphQLInvocationTest.java
@@ -0,0 +1,59 @@
+package graphql.spring.web.servlet.components;
+
+import graphql.ExecutionInput;
+import graphql.ExecutionResult;
+import graphql.GraphQL;
+import graphql.spring.web.servlet.ExecutionInputCustomizer;
+import graphql.spring.web.servlet.GraphQLInvocationData;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.springframework.web.context.request.WebRequest;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+
+import static java.util.concurrent.CompletableFuture.completedFuture;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class DefaultGraphQLInvocationTest {
+
+
+ @Test
+ public void testCustomizerIsCalled() {
+
+ String query = "query myQuery {foo}";
+ String operationName = "myQuery";
+ Map variables = new LinkedHashMap<>();
+
+ DefaultGraphQLInvocation defaultGraphQLInvocation = new DefaultGraphQLInvocation();
+ ExecutionInputCustomizer executionInputCustomizer = mock(ExecutionInputCustomizer.class);
+ defaultGraphQLInvocation.executionInputCustomizer = executionInputCustomizer;
+ GraphQL graphQL = mock(GraphQL.class);
+ defaultGraphQLInvocation.graphQL = graphQL;
+ ExecutionResult executionResult = mock(ExecutionResult.class);
+ when(graphQL.executeAsync(any(ExecutionInput.class))).thenReturn(completedFuture(executionResult));
+
+ GraphQLInvocationData graphQLInvocationData = new GraphQLInvocationData(query, operationName, variables);
+ WebRequest webRequest = mock(WebRequest.class);
+
+ ArgumentCaptor captor1 = ArgumentCaptor.forClass(ExecutionInput.class);
+ ArgumentCaptor captor2 = ArgumentCaptor.forClass(WebRequest.class);
+ ExecutionInput executionInputResult = mock(ExecutionInput.class);
+ when(executionInputCustomizer.customizeExecutionInput(captor1.capture(), captor2.capture())).thenReturn(completedFuture(executionInputResult));
+
+ CompletableFuture invoke = defaultGraphQLInvocation.invoke(graphQLInvocationData, webRequest);
+
+ assertThat(captor1.getValue().getQuery()).isEqualTo(query);
+ assertThat(captor1.getValue().getOperationName()).isEqualTo(operationName);
+ assertThat(captor1.getValue().getVariables()).isSameAs(variables);
+
+ verify(graphQL).executeAsync(executionInputResult);
+
+ }
+
+}
\ No newline at end of file