Skip to content

Commit 79f15ec

Browse files
defer server
1 parent ba0fe20 commit 79f15ec

File tree

3 files changed

+156
-17
lines changed

3 files changed

+156
-17
lines changed
Lines changed: 117 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
11
package com.graphqljava.defer;
22

3+
import com.fasterxml.jackson.core.JsonProcessingException;
4+
import com.fasterxml.jackson.databind.ObjectMapper;
5+
import graphql.DeferredExecutionResult;
36
import graphql.ExecutionInput;
47
import graphql.ExecutionResult;
58
import graphql.GraphQL;
9+
import org.reactivestreams.Publisher;
10+
import org.reactivestreams.Subscriber;
11+
import org.reactivestreams.Subscription;
612
import org.slf4j.Logger;
713
import org.slf4j.LoggerFactory;
814
import org.springframework.beans.factory.annotation.Autowired;
@@ -13,22 +19,35 @@
1319
import org.springframework.web.bind.annotation.ResponseBody;
1420
import org.springframework.web.bind.annotation.RestController;
1521

22+
import javax.servlet.http.HttpServletResponse;
23+
import java.io.IOException;
24+
import java.io.PrintWriter;
1625
import java.util.LinkedHashMap;
26+
import java.util.List;
1727
import java.util.Map;
1828

1929
@RestController
2030
public class GraphQLController {
2131

2232

33+
public static final String CRLF = "\r\n";
2334
@Autowired
2435
GraphQL graphql;
2536

37+
@Autowired
38+
ObjectMapper objectMapper;
39+
40+
2641
Logger log = LoggerFactory.getLogger(GraphQLController.class);
2742

43+
2844
@RequestMapping(value = "/graphql", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
2945
@ResponseBody
30-
public Map<String, Object> graphql(@RequestBody Map<String, Object> body) {
46+
public void graphql(@RequestBody Map<String, Object> body, HttpServletResponse httpServletResponse) throws IOException {
3147
String query = (String) body.get("query");
48+
if (query == null) {
49+
query = "";
50+
}
3251
Map<String, Object> variables = (Map<String, Object>) body.get("variables");
3352
if (variables == null) {
3453
variables = new LinkedHashMap<>();
@@ -39,12 +58,103 @@ public Map<String, Object> graphql(@RequestBody Map<String, Object> body) {
3958
.build();
4059

4160
ExecutionResult executionResult = graphql.execute(executionInput);
42-
Map<String, Object> result = new LinkedHashMap<>();
43-
if (executionResult.getErrors().size() > 0) {
44-
result.put("errors", executionResult.getErrors());
45-
log.error("Errors: {}", executionResult.getErrors());
61+
Map<Object, Object> extensions = executionResult.getExtensions();
62+
if (extensions != null && extensions.containsKey(GraphQL.DEFERRED_RESULTS)) {
63+
Publisher<DeferredExecutionResult> deferredResults = (Publisher<DeferredExecutionResult>) extensions.get(GraphQL.DEFERRED_RESULTS);
64+
sendDeferResponse(httpServletResponse, executionResult, deferredResults);
65+
} else {
66+
sendNormalResponse(httpServletResponse, executionResult);
67+
}
68+
}
69+
70+
private void sendNormalResponse(HttpServletResponse httpServletResponse, ExecutionResult executionResult) throws IOException {
71+
Map<String, Object> result = executionResult.toSpecification();
72+
httpServletResponse.setStatus(HttpServletResponse.SC_OK);
73+
httpServletResponse.setCharacterEncoding("UTF-8");
74+
httpServletResponse.setContentType("application/json");
75+
String body = objectMapper.writeValueAsString(result);
76+
PrintWriter writer = httpServletResponse.getWriter();
77+
writer.write(body);
78+
writer.close();
79+
80+
}
81+
82+
private void sendDeferResponse(HttpServletResponse httpServletResponse, ExecutionResult executionResult, Publisher<DeferredExecutionResult> deferredResults) throws IOException {
83+
httpServletResponse.setStatus(HttpServletResponse.SC_OK);
84+
httpServletResponse.setCharacterEncoding("UTF-8");
85+
httpServletResponse.setContentType("multipart/mixed; boundary=\"-\"");
86+
httpServletResponse.setHeader("Transfer-Encoding", "chunked");
87+
PrintWriter writer = httpServletResponse.getWriter();
88+
89+
writer.write("---" + CRLF);
90+
DeferPart deferPart = new DeferPart(executionResult.toSpecification());
91+
String body = deferPart.write();
92+
writer.write(body);
93+
httpServletResponse.flushBuffer();
94+
95+
deferredResults.subscribe(new Subscriber<DeferredExecutionResult>() {
96+
97+
Subscription subscription;
98+
99+
@Override
100+
public void onSubscribe(Subscription s) {
101+
subscription = s;
102+
subscription.request(10);
103+
}
104+
105+
@Override
106+
public void onNext(DeferredExecutionResult executionResult) {
107+
DeferPart deferPart = new DeferPart(executionResult.toSpecification());
108+
String body = deferPart.write();
109+
writer.write(body);
110+
try {
111+
httpServletResponse.flushBuffer();
112+
} catch (IOException e) {
113+
e.printStackTrace();
114+
}
115+
subscription.request(10);
116+
}
117+
118+
@Override
119+
public void onError(Throwable t) {
120+
t.printStackTrace();
121+
}
122+
123+
@Override
124+
public void onComplete() {
125+
writer.close();
126+
}
127+
});
128+
129+
130+
}
131+
132+
133+
private class DeferPart {
134+
135+
private Object body;
136+
137+
public DeferPart(Object data) {
138+
this.body = data;
139+
}
140+
141+
public String write() {
142+
StringBuilder result = new StringBuilder();
143+
String bodyString = bodyToString();
144+
result.append("Content-Type: application/json").append(CRLF);
145+
result.append("Content-Length: ").append(bodyString.length()).append(CRLF);
146+
result.append(bodyString).append(CRLF);
147+
result.append(CRLF).append("---").append(CRLF);
148+
return result.toString();
46149
}
47-
result.put("data", executionResult.getData());
48-
return result;
150+
151+
private String bodyToString() {
152+
try {
153+
return objectMapper.writeValueAsString(body);
154+
} catch (JsonProcessingException e) {
155+
throw new RuntimeException(e);
156+
}
157+
}
158+
49159
}
50160
}

defer/server/src/main/java/com/graphqljava/defer/GraphQLProvider.java

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.graphqljava.defer;
22

33
import com.google.common.base.Charsets;
4+
import com.google.common.collect.ImmutableList;
45
import com.google.common.collect.ImmutableMap;
56
import com.google.common.io.Resources;
67
import graphql.GraphQL;
@@ -16,13 +17,38 @@
1617
import javax.annotation.PostConstruct;
1718
import java.io.IOException;
1819
import java.net.URL;
20+
import java.util.List;
21+
import java.util.Map;
22+
import java.util.concurrent.CompletableFuture;
1923

2024
@Component
2125
public class GraphQLProvider {
2226

2327
GraphQL graphQL;
2428

25-
DataFetcher<Object> fooDataFetcher = environment -> ImmutableMap.of("id", "fooId");
29+
List<Map> books = ImmutableList.of(
30+
ImmutableMap.of("title", "Harry Potter and the Chamber of Secrets", "author", "J.K. Rowling")
31+
);
32+
33+
List<Map> comments = ImmutableList.of(
34+
ImmutableMap.of("user", "andi", "text", "great"),
35+
ImmutableMap.of("user", "brad", "text", "read better ones"),
36+
ImmutableMap.of("user", "felipe", "text", "scary")
37+
);
38+
39+
DataFetcher<Object> booksFetcher = environment -> books;
40+
DataFetcher<Object> commentsFetcher = environment -> CompletableFuture.supplyAsync(() -> {
41+
sleep();
42+
return comments;
43+
});
44+
45+
private void sleep() {
46+
try {
47+
Thread.sleep(5000);
48+
} catch (InterruptedException e) {
49+
throw new RuntimeException(e);
50+
}
51+
}
2652

2753
@PostConstruct
2854
public void init() throws IOException {
@@ -43,7 +69,9 @@ private GraphQLSchema buildSchema(String sdl) {
4369
private RuntimeWiring buildWiring() {
4470
return RuntimeWiring.newRuntimeWiring()
4571
.type("Query", builder -> builder
46-
.dataFetcher("foo", fooDataFetcher))
72+
.dataFetcher("books", booksFetcher))
73+
.type("Book", builder -> builder
74+
.dataFetcher("comments", commentsFetcher))
4775
.build();
4876
}
4977

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
1-
type Query {
2-
foo: Foo
1+
type Book {
2+
title: String
3+
author: String
4+
comments: [Comment]
35
}
46

5-
type Foo {
6-
id: ID
7-
complexValue: ComplexValue
7+
type Comment {
8+
user: String
9+
text: String
810
}
911

10-
type ComplexValue {
11-
id: ID
12-
hello: String
12+
type Query {
13+
books: [Book]
1314
}
1415

1516

0 commit comments

Comments
 (0)