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

Commit c70201e

Browse files
committed
rename to webflux
add defer handling
1 parent 6fecc5a commit c70201e

File tree

15 files changed

+173
-33
lines changed

15 files changed

+173
-33
lines changed

graphql-java-spring-boot-starter/build.gradle renamed to graphql-java-spring-boot-starter-webflux/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@ def springBootVersion = "2.1.0.RELEASE"
55

66
dependencies {
77
compile "org.springframework.boot:spring-boot-autoconfigure:$springBootVersion"
8-
compile project(':graphql-java-spring-web')
8+
compile project(':graphql-java-spring-webflux')
99
}

graphql-java-spring-boot-starter/src/main/java/graphql/spring/GraphQLEndpointConfiguration.java renamed to graphql-java-spring-boot-starter-webflux/src/main/java/graphql/spring/GraphQLEndpointConfiguration.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package graphql.spring;
22

3-
import graphql.spring.controller.GraphQLController;
3+
import graphql.spring.reactive.controller.GraphQLController;
44
import org.springframework.beans.factory.annotation.Autowired;
55
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
66
import org.springframework.context.ApplicationContext;

graphql-java-spring-web/src/main/java/graphql/spring/controller/GraphQLInvocation.java

Lines changed: 0 additions & 11 deletions
This file was deleted.

graphql-java-spring-web/build.gradle renamed to graphql-java-spring-webflux/build.gradle

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
def springVersion = "5.1.2.RELEASE"
22
def jacksonVersion = "2.9.6"
3-
def graphqlJavaVersion = "11.0"
3+
//def graphqlJavaVersion = "11.0"
4+
def graphqlJavaVersion = "2018-09-16T01-27-46-27a6e44"
45

56
dependencies {
6-
compile "org.springframework:spring-webmvc:$springVersion"
7+
compile "org.springframework:spring-webflux:$springVersion"
78
compile "org.springframework:spring-context:$springVersion"
89
compile "com.fasterxml.jackson.core:jackson-databind:$jacksonVersion"
910
compile "com.graphql-java:graphql-java:$graphqlJavaVersion"
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
package graphql.spring.reactive;
2+
3+
import com.fasterxml.jackson.core.JsonProcessingException;
4+
import com.fasterxml.jackson.databind.ObjectMapper;
5+
import graphql.DeferredExecutionResult;
6+
import graphql.ExecutionResult;
7+
import graphql.GraphQL;
8+
import org.reactivestreams.Publisher;
9+
import org.springframework.beans.factory.annotation.Autowired;
10+
import org.springframework.core.io.buffer.DataBuffer;
11+
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
12+
import org.springframework.http.HttpHeaders;
13+
import org.springframework.http.HttpStatus;
14+
import org.springframework.http.server.reactive.ServerHttpResponse;
15+
import org.springframework.stereotype.Component;
16+
import reactor.core.publisher.Flux;
17+
import reactor.core.publisher.Mono;
18+
19+
import java.nio.charset.StandardCharsets;
20+
import java.util.Map;
21+
22+
@Component
23+
public class DefaultExecutionResultHandler implements ExecutionResultHandler {
24+
25+
@Autowired
26+
ObjectMapper objectMapper;
27+
28+
private static final String CRLF = "\r\n";
29+
30+
@Override
31+
public Object handleExecutionResult(Mono<ExecutionResult> executionResultMono, ServerHttpResponse serverHttpResponse) {
32+
return executionResultMono.map(executionResult -> handleImpl(executionResult, serverHttpResponse));
33+
}
34+
35+
private Object handleImpl(ExecutionResult executionResult, ServerHttpResponse serverHttpResponse) {
36+
Map<Object, Object> extensions = executionResult.getExtensions();
37+
if (extensions != null && extensions.containsKey(GraphQL.DEFERRED_RESULTS)) {
38+
return handleDeferResponse(serverHttpResponse, executionResult, extensions);
39+
} else {
40+
return executionResult.toSpecification();
41+
}
42+
}
43+
44+
private Mono<Void> handleDeferResponse(ServerHttpResponse serverHttpResponse,
45+
ExecutionResult executionResult,
46+
Map<Object, Object> extensions) {
47+
Publisher<DeferredExecutionResult> deferredResults = (Publisher<DeferredExecutionResult>) extensions.get(GraphQL.DEFERRED_RESULTS);
48+
// this implements this apollo defer spec: https://github.com/apollographql/apollo-server/blob/defer-support/docs/source/defer-support.md
49+
// the spec says CRLF + "-----" + CRLF is needed at the end, but it works without it and with it we get client
50+
// side errors with it, so we skp it
51+
serverHttpResponse.setStatusCode(HttpStatus.OK);
52+
HttpHeaders headers = serverHttpResponse.getHeaders();
53+
headers.set("Content-Type", "multipart/mixed; boundary=\"-\"");
54+
headers.set("Connection", "keep-alive");
55+
56+
Flux<Mono<DataBuffer>> deferredDataBuffers = Flux.from(deferredResults).map(deferredExecutionResult -> {
57+
DeferPart deferPart = new DeferPart(deferredExecutionResult.toSpecification());
58+
StringBuilder builder = new StringBuilder();
59+
String body = deferPart.write();
60+
builder.append(CRLF).append("---").append(CRLF);
61+
builder.append(body);
62+
return strToDataBuffer(builder.toString());
63+
});
64+
Flux<Mono<DataBuffer>> firstResult = Flux.just(firstResult(executionResult));
65+
66+
67+
return serverHttpResponse.writeAndFlushWith(Flux.mergeSequential(firstResult, deferredDataBuffers));
68+
}
69+
70+
private Mono<DataBuffer> firstResult(ExecutionResult executionResult) {
71+
StringBuilder builder = new StringBuilder();
72+
builder.append(CRLF).append("---").append(CRLF);
73+
DeferPart deferPart = new DeferPart(executionResult.toSpecification());
74+
String body = deferPart.write();
75+
builder.append(body);
76+
Mono<DataBuffer> dataBufferMono = strToDataBuffer(body);
77+
return dataBufferMono;
78+
}
79+
80+
private Mono<DataBuffer> strToDataBuffer(String string) {
81+
byte[] bytes = string.getBytes(StandardCharsets.UTF_8);
82+
DefaultDataBufferFactory defaultDataBufferFactory = new DefaultDataBufferFactory();
83+
return Mono.just(defaultDataBufferFactory.wrap(bytes));
84+
}
85+
86+
private class DeferPart {
87+
88+
private Object body;
89+
90+
public DeferPart(Object data) {
91+
this.body = data;
92+
}
93+
94+
public String write() {
95+
StringBuilder result = new StringBuilder();
96+
String bodyString = bodyToString();
97+
result.append("Content-Type: application/json").append(CRLF);
98+
result.append("Content-Length: ").append(bodyString.length()).append(CRLF).append(CRLF);
99+
result.append(bodyString);
100+
return result.toString();
101+
}
102+
103+
private String bodyToString() {
104+
try {
105+
return objectMapper.writeValueAsString(body);
106+
} catch (JsonProcessingException e) {
107+
throw new RuntimeException(e);
108+
}
109+
}
110+
111+
}
112+
}
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package graphql.spring.controller;
1+
package graphql.spring.reactive;
22

33
import graphql.ExecutionInput;
44
import graphql.ExecutionResult;
@@ -7,9 +7,7 @@
77
import org.springframework.beans.factory.annotation.Autowired;
88
import org.springframework.stereotype.Component;
99
import org.springframework.web.context.request.WebRequest;
10-
11-
import java.util.Map;
12-
import java.util.concurrent.CompletableFuture;
10+
import reactor.core.publisher.Mono;
1311

1412
@Component
1513
@Internal
@@ -19,13 +17,13 @@ public class DefaultGraphQLInvocation implements GraphQLInvocation {
1917
private GraphQL graphQL;
2018

2119
@Override
22-
public CompletableFuture<Map<String, Object>> invoke(GraphQLInvocationData invocationData, WebRequest webRequest) {
20+
public Mono<ExecutionResult> invoke(GraphQLInvocationData invocationData, WebRequest webRequest) {
2321
ExecutionInput executionInput = ExecutionInput.newExecutionInput()
2422
.query(invocationData.getQuery())
2523
.operationName(invocationData.getOperationName())
2624
.variables(invocationData.getVariables())
2725
.build();
28-
return graphQL.executeAsync(executionInput).thenApply(ExecutionResult::toSpecification);
26+
return Mono.fromCompletionStage(graphQL.executeAsync(executionInput));
2927
}
3028

3129
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package graphql.spring.reactive;
2+
3+
import graphql.ExecutionResult;
4+
import graphql.PublicSpi;
5+
import org.springframework.http.server.reactive.ServerHttpResponse;
6+
import reactor.core.publisher.Mono;
7+
8+
@PublicSpi
9+
public interface ExecutionResultHandler {
10+
11+
Object handleExecutionResult(Mono<ExecutionResult> executionResultMono, ServerHttpResponse serverHttpResponse);
12+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package graphql.spring.reactive;
2+
3+
import graphql.ExecutionResult;
4+
import graphql.PublicApi;
5+
import org.springframework.web.context.request.WebRequest;
6+
import reactor.core.publisher.Mono;
7+
8+
@PublicApi
9+
public interface GraphQLInvocation {
10+
11+
Mono<ExecutionResult> invoke(GraphQLInvocationData invocationData, WebRequest webRequest);
12+
13+
}

graphql-java-spring-web/src/main/java/graphql/spring/controller/GraphQLInvocationData.java renamed to graphql-java-spring-webflux/src/main/java/graphql/spring/reactive/GraphQLInvocationData.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package graphql.spring.controller;
1+
package graphql.spring.reactive;
22

33
import graphql.Assert;
44
import graphql.PublicApi;

0 commit comments

Comments
 (0)