Skip to content
This repository was archived by the owner on Oct 25, 2021. It is now read-only.

Commit d7659a9

Browse files
authored
Merge pull request #9 from graphql-java/customize-input
add ExecutionInputCustomizer
2 parents 8c56029 + 63b81e4 commit d7659a9

File tree

12 files changed

+215
-5
lines changed

12 files changed

+215
-5
lines changed

build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ subprojects {
4343
springVersion = "5.1.2.RELEASE"
4444
springBootVersion = "2.1.0.RELEASE"
4545
jacksonVersion = "2.9.6"
46+
assertJVersion = "3.11.1"
4647
}
4748

4849
repositories {

graphql-java-spring-webflux/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ dependencies {
66
compile "com.fasterxml.jackson.core:jackson-databind:$jacksonVersion"
77
compile "com.graphql-java:graphql-java:$graphqlJavaVersion"
88

9+
testCompile("org.assertj:assertj-core:$assertJVersion")
910
testCompile group: 'junit', name: 'junit', version: '4.12'
1011
testCompile "org.springframework:spring-test:$springVersion"
1112
testCompile group: 'javax.servlet', name: 'javax.servlet-api', version: '4.0.1'
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package graphql.spring.web.reactive;
2+
3+
import graphql.ExecutionInput;
4+
import graphql.PublicApi;
5+
import org.springframework.web.server.ServerWebExchange;
6+
import reactor.core.publisher.Mono;
7+
8+
/**
9+
* Lets you customize the #ExecutionInput before the query is executed.
10+
* You can for example set a context object or define a root value.
11+
* <p>
12+
* This is only used if you use the default {@link GraphQLInvocation}.
13+
*/
14+
@PublicApi
15+
public interface ExecutionInputCustomizer {
16+
17+
Mono<ExecutionInput> customizeExecutionInput(ExecutionInput executionInput, ServerWebExchange webRequest);
18+
19+
}

graphql-java-spring-webflux/src/main/java/graphql/spring/web/reactive/GraphQLInvocation.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,6 @@
88
@PublicApi
99
public interface GraphQLInvocation {
1010

11-
Mono<ExecutionResult> invoke(GraphQLInvocationData invocationData, ServerWebExchange webRequest);
11+
Mono<ExecutionResult> invoke(GraphQLInvocationData invocationData, ServerWebExchange serverWebExchange);
1212

1313
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package graphql.spring.web.reactive.components;
2+
3+
import graphql.ExecutionInput;
4+
import graphql.Internal;
5+
import graphql.spring.web.reactive.ExecutionInputCustomizer;
6+
import org.springframework.stereotype.Component;
7+
import org.springframework.web.server.ServerWebExchange;
8+
import reactor.core.publisher.Mono;
9+
10+
@Component
11+
@Internal
12+
public class DefaultExecutionInputCustomizer implements ExecutionInputCustomizer {
13+
14+
@Override
15+
public Mono<ExecutionInput> customizeExecutionInput(ExecutionInput executionInput, ServerWebExchange webRequest) {
16+
return Mono.just(executionInput);
17+
}
18+
}

graphql-java-spring-webflux/src/main/java/graphql/spring/web/reactive/components/DefaultGraphQLInvocation.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import graphql.ExecutionResult;
55
import graphql.GraphQL;
66
import graphql.Internal;
7+
import graphql.spring.web.reactive.ExecutionInputCustomizer;
78
import graphql.spring.web.reactive.GraphQLInvocation;
89
import graphql.spring.web.reactive.GraphQLInvocationData;
910
import org.springframework.beans.factory.annotation.Autowired;
@@ -16,7 +17,10 @@
1617
public class DefaultGraphQLInvocation implements GraphQLInvocation {
1718

1819
@Autowired
19-
private GraphQL graphQL;
20+
GraphQL graphQL;
21+
22+
@Autowired
23+
ExecutionInputCustomizer executionInputCustomizer;
2024

2125
@Override
2226
public Mono<ExecutionResult> invoke(GraphQLInvocationData invocationData, ServerWebExchange serverWebExchange) {
@@ -25,7 +29,8 @@ public Mono<ExecutionResult> invoke(GraphQLInvocationData invocationData, Server
2529
.operationName(invocationData.getOperationName())
2630
.variables(invocationData.getVariables())
2731
.build();
28-
return Mono.fromCompletionStage(graphQL.executeAsync(executionInput));
32+
Mono<ExecutionInput> customizedExecutionInputMono = executionInputCustomizer.customizeExecutionInput(executionInput, serverWebExchange);
33+
return customizedExecutionInputMono.flatMap(customizedExecutionInput -> Mono.fromCompletionStage(graphQL.executeAsync(customizedExecutionInput)));
2934
}
3035

3136
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package graphql.spring.web.reactive.components;
2+
3+
import graphql.ExecutionInput;
4+
import graphql.ExecutionResult;
5+
import graphql.GraphQL;
6+
import graphql.spring.web.reactive.ExecutionInputCustomizer;
7+
import graphql.spring.web.reactive.GraphQLInvocationData;
8+
import org.junit.Test;
9+
import org.mockito.ArgumentCaptor;
10+
import org.springframework.web.server.ServerWebExchange;
11+
import reactor.core.publisher.Mono;
12+
13+
import java.util.LinkedHashMap;
14+
import java.util.Map;
15+
16+
import static java.util.concurrent.CompletableFuture.completedFuture;
17+
import static org.assertj.core.api.Assertions.assertThat;
18+
import static org.mockito.ArgumentMatchers.any;
19+
import static org.mockito.Mockito.mock;
20+
import static org.mockito.Mockito.verify;
21+
import static org.mockito.Mockito.when;
22+
23+
public class DefaultGraphQLInvocationTest {
24+
25+
26+
@Test
27+
public void testCustomizerIsCalled() {
28+
29+
String query = "query myQuery {foo}";
30+
String operationName = "myQuery";
31+
Map<String, Object> variables = new LinkedHashMap<>();
32+
33+
DefaultGraphQLInvocation defaultGraphQLInvocation = new DefaultGraphQLInvocation();
34+
ExecutionInputCustomizer executionInputCustomizer = mock(ExecutionInputCustomizer.class);
35+
defaultGraphQLInvocation.executionInputCustomizer = executionInputCustomizer;
36+
GraphQL graphQL = mock(GraphQL.class);
37+
defaultGraphQLInvocation.graphQL = graphQL;
38+
ExecutionResult executionResult = mock(ExecutionResult.class);
39+
when(graphQL.executeAsync(any(ExecutionInput.class))).thenReturn(completedFuture(executionResult));
40+
41+
GraphQLInvocationData graphQLInvocationData = new GraphQLInvocationData(query, operationName, variables);
42+
ServerWebExchange serverWebExchange = mock(ServerWebExchange.class);
43+
44+
ArgumentCaptor<ExecutionInput> captor1 = ArgumentCaptor.forClass(ExecutionInput.class);
45+
ArgumentCaptor<ServerWebExchange> captor2 = ArgumentCaptor.forClass(ServerWebExchange.class);
46+
ExecutionInput executionInputResult = mock(ExecutionInput.class);
47+
when(executionInputCustomizer.customizeExecutionInput(captor1.capture(), captor2.capture())).thenReturn(Mono.just(executionInputResult));
48+
49+
Mono<ExecutionResult> invoke = defaultGraphQLInvocation.invoke(graphQLInvocationData, serverWebExchange);
50+
51+
assertThat(captor1.getValue().getQuery()).isEqualTo(query);
52+
assertThat(captor1.getValue().getOperationName()).isEqualTo(operationName);
53+
assertThat(captor1.getValue().getVariables()).isSameAs(variables);
54+
55+
invoke.block();
56+
57+
verify(graphQL).executeAsync(executionInputResult);
58+
59+
}
60+
61+
62+
}

graphql-java-spring-webmvc/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ dependencies {
66
compile "com.fasterxml.jackson.core:jackson-databind:$jacksonVersion"
77
compile "com.graphql-java:graphql-java:$graphqlJavaVersion"
88

9+
testCompile("org.assertj:assertj-core:$assertJVersion")
910
testCompile group: 'junit', name: 'junit', version: '4.12'
1011
testCompile "org.springframework:spring-test:$springVersion"
1112
testCompile group: 'javax.servlet', name: 'javax.servlet-api', version: '4.0.1'
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package graphql.spring.web.servlet;
2+
3+
import graphql.ExecutionInput;
4+
import graphql.PublicApi;
5+
import org.springframework.web.context.request.WebRequest;
6+
7+
import java.util.concurrent.CompletableFuture;
8+
9+
/**
10+
* Lets you customize the #ExecutionInput before the query is executed.
11+
* You can for example set a context object or define a root value.
12+
* <p>
13+
* This is only used if you use the default {@link GraphQLInvocation}.
14+
*/
15+
@PublicApi
16+
public interface ExecutionInputCustomizer {
17+
18+
CompletableFuture<ExecutionInput> customizeExecutionInput(ExecutionInput executionInput, WebRequest webRequest);
19+
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package graphql.spring.web.servlet.components;
2+
3+
import graphql.ExecutionInput;
4+
import graphql.Internal;
5+
import graphql.spring.web.servlet.ExecutionInputCustomizer;
6+
import org.springframework.stereotype.Component;
7+
import org.springframework.web.context.request.WebRequest;
8+
9+
import java.util.concurrent.CompletableFuture;
10+
11+
@Component
12+
@Internal
13+
public class DefaultExecutionInputCustomizer implements ExecutionInputCustomizer {
14+
15+
@Override
16+
public CompletableFuture<ExecutionInput> customizeExecutionInput(ExecutionInput executionInput, WebRequest webRequest) {
17+
return CompletableFuture.completedFuture(executionInput);
18+
}
19+
}

graphql-java-spring-webmvc/src/main/java/graphql/spring/web/servlet/components/DefaultGraphQLInvocation.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import graphql.ExecutionResult;
55
import graphql.GraphQL;
66
import graphql.Internal;
7+
import graphql.spring.web.servlet.ExecutionInputCustomizer;
78
import graphql.spring.web.servlet.GraphQLInvocation;
89
import graphql.spring.web.servlet.GraphQLInvocationData;
910
import org.springframework.beans.factory.annotation.Autowired;
@@ -17,7 +18,10 @@
1718
public class DefaultGraphQLInvocation implements GraphQLInvocation {
1819

1920
@Autowired
20-
private GraphQL graphQL;
21+
GraphQL graphQL;
22+
23+
@Autowired
24+
ExecutionInputCustomizer executionInputCustomizer;
2125

2226
@Override
2327
public CompletableFuture<ExecutionResult> invoke(GraphQLInvocationData invocationData, WebRequest webRequest) {
@@ -26,7 +30,8 @@ public CompletableFuture<ExecutionResult> invoke(GraphQLInvocationData invocatio
2630
.operationName(invocationData.getOperationName())
2731
.variables(invocationData.getVariables())
2832
.build();
29-
return graphQL.executeAsync(executionInput);
33+
CompletableFuture<ExecutionInput> customizedExecutionInput = executionInputCustomizer.customizeExecutionInput(executionInput, webRequest);
34+
return customizedExecutionInput.thenCompose(graphQL::executeAsync);
3035
}
3136

3237
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package graphql.spring.web.servlet.components;
2+
3+
import graphql.ExecutionInput;
4+
import graphql.ExecutionResult;
5+
import graphql.GraphQL;
6+
import graphql.spring.web.servlet.ExecutionInputCustomizer;
7+
import graphql.spring.web.servlet.GraphQLInvocationData;
8+
import org.junit.Test;
9+
import org.mockito.ArgumentCaptor;
10+
import org.springframework.web.context.request.WebRequest;
11+
12+
import java.util.LinkedHashMap;
13+
import java.util.Map;
14+
import java.util.concurrent.CompletableFuture;
15+
16+
import static java.util.concurrent.CompletableFuture.completedFuture;
17+
import static org.assertj.core.api.Assertions.assertThat;
18+
import static org.mockito.ArgumentMatchers.any;
19+
import static org.mockito.Mockito.mock;
20+
import static org.mockito.Mockito.verify;
21+
import static org.mockito.Mockito.when;
22+
23+
public class DefaultGraphQLInvocationTest {
24+
25+
26+
@Test
27+
public void testCustomizerIsCalled() {
28+
29+
String query = "query myQuery {foo}";
30+
String operationName = "myQuery";
31+
Map<String, Object> variables = new LinkedHashMap<>();
32+
33+
DefaultGraphQLInvocation defaultGraphQLInvocation = new DefaultGraphQLInvocation();
34+
ExecutionInputCustomizer executionInputCustomizer = mock(ExecutionInputCustomizer.class);
35+
defaultGraphQLInvocation.executionInputCustomizer = executionInputCustomizer;
36+
GraphQL graphQL = mock(GraphQL.class);
37+
defaultGraphQLInvocation.graphQL = graphQL;
38+
ExecutionResult executionResult = mock(ExecutionResult.class);
39+
when(graphQL.executeAsync(any(ExecutionInput.class))).thenReturn(completedFuture(executionResult));
40+
41+
GraphQLInvocationData graphQLInvocationData = new GraphQLInvocationData(query, operationName, variables);
42+
WebRequest webRequest = mock(WebRequest.class);
43+
44+
ArgumentCaptor<ExecutionInput> captor1 = ArgumentCaptor.forClass(ExecutionInput.class);
45+
ArgumentCaptor<WebRequest> captor2 = ArgumentCaptor.forClass(WebRequest.class);
46+
ExecutionInput executionInputResult = mock(ExecutionInput.class);
47+
when(executionInputCustomizer.customizeExecutionInput(captor1.capture(), captor2.capture())).thenReturn(completedFuture(executionInputResult));
48+
49+
CompletableFuture<ExecutionResult> invoke = defaultGraphQLInvocation.invoke(graphQLInvocationData, webRequest);
50+
51+
assertThat(captor1.getValue().getQuery()).isEqualTo(query);
52+
assertThat(captor1.getValue().getOperationName()).isEqualTo(operationName);
53+
assertThat(captor1.getValue().getVariables()).isSameAs(variables);
54+
55+
verify(graphQL).executeAsync(executionInputResult);
56+
57+
}
58+
59+
}

0 commit comments

Comments
 (0)