Skip to content

Commit ceac689

Browse files
Adjustments and plenty of Javadoc
1 parent 40b7dfa commit ceac689

9 files changed

Lines changed: 360 additions & 147 deletions
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package graphql.incremental;
2+
3+
import graphql.ExperimentalApi;
4+
import graphql.GraphQLError;
5+
6+
import javax.annotation.Nullable;
7+
import java.util.LinkedHashMap;
8+
import java.util.List;
9+
import java.util.Map;
10+
11+
/**
12+
* Represents a defer payload
13+
*/
14+
@ExperimentalApi
15+
public class DeferPayload extends IncrementalPayload {
16+
private final Object data;
17+
18+
private DeferPayload(Object data, List<Object> path, String label, List<GraphQLError> errors, Map<Object, Object> extensions) {
19+
super(path, label, errors, extensions);
20+
this.data = data;
21+
}
22+
23+
/**
24+
* @return the resolved data
25+
* @param <T> the type to cast the result to
26+
*/
27+
@Nullable
28+
public <T> T getData() {
29+
//noinspection unchecked
30+
return (T) this.data;
31+
}
32+
33+
/**
34+
* @return a map of this payload that strictly follows the spec
35+
*/
36+
@Override
37+
public Map<String, Object> toSpecification() {
38+
Map<String, Object> map = new LinkedHashMap<>(super.toSpecification());
39+
40+
if (data != null) {
41+
map.put("data", data);
42+
}
43+
44+
return map;
45+
}
46+
47+
/**
48+
* @return a {@link DeferPayload.Builder} that can be used to create an instance of {@link DeferPayload}
49+
*/
50+
public static DeferPayload.Builder newDeferredItem() {
51+
return new DeferPayload.Builder();
52+
}
53+
54+
public static class Builder extends IncrementalPayload.Builder<DeferPayload> {
55+
private Object data = null;
56+
57+
public Builder data(Object data) {
58+
this.data = data;
59+
return this;
60+
}
61+
62+
public Builder from(DeferPayload deferredItem) {
63+
super.from(deferredItem);
64+
this.data = deferredItem.data;
65+
return this;
66+
}
67+
68+
@Override
69+
public DeferPayload build() {
70+
return new DeferPayload(data, this.path, this.label, this.errors, this.extensions);
71+
}
72+
}
73+
}

src/main/java/graphql/incremental/DeferredItem.java

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

src/main/java/graphql/incremental/DelayedIncrementalExecutionResult.java

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,37 @@
22

33
import graphql.ExperimentalApi;
44

5+
import javax.annotation.Nullable;
56
import java.util.List;
7+
import java.util.Map;
68

9+
/**
10+
* Represents a result that is delivered asynchronously, after the initial {@link IncrementalExecutionResult}.
11+
* <p>
12+
* Multiple defer and/or stream payloads (represented by {@link IncrementalPayload}) can be part of the same
13+
* {@link DelayedIncrementalExecutionResult}
14+
*/
715
@ExperimentalApi
816
public interface DelayedIncrementalExecutionResult {
9-
List<IncrementalItem> getIncremental();
10-
11-
String getLabel();
17+
/**
18+
* @return a list of defer and/or stream payloads.
19+
*/
20+
@Nullable
21+
List<IncrementalPayload> getIncremental();
1222

23+
/**
24+
* Indicates whether the stream will continue emitting {@link DelayedIncrementalExecutionResult}s after this one.
25+
* <p>
26+
* The value returned by this method should be "true" for all but the last response in the stream. The value of this
27+
* entry is `false` for the last response of the stream.
28+
*
29+
* @return "true" if there are more responses in the stream, "false" otherwise.
30+
*/
1331
boolean hasNext();
32+
33+
/**
34+
* @return a map of extensions or null if there are none
35+
*/
36+
@Nullable
37+
Map<Object, Object> getExtensions();
1438
}
Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,67 @@
11
package graphql.incremental;
22

3+
import graphql.ExperimentalApi;
4+
35
import java.util.Collections;
46
import java.util.List;
7+
import java.util.Map;
58

9+
@ExperimentalApi
610
public class DelayedIncrementalExecutionResultImpl implements DelayedIncrementalExecutionResult {
7-
private final List<IncrementalItem> incrementalItems;
8-
private final String label;
11+
private final List<IncrementalPayload> incrementalItems;
912
private final boolean hasNext;
13+
private final Map<Object, Object> extensions;
1014

11-
private DelayedIncrementalExecutionResultImpl(List<IncrementalItem> incrementalItems, String label, boolean hasNext) {
12-
this.incrementalItems = incrementalItems;
13-
this.label = label;
14-
this.hasNext = hasNext;
15+
private DelayedIncrementalExecutionResultImpl(Builder builder) {
16+
this.incrementalItems = builder.incrementalItems;
17+
this.hasNext = builder.hasNext;
18+
this.extensions = builder.extensions;
1519
}
1620

1721
@Override
18-
public List<IncrementalItem> getIncremental() {
22+
public List<IncrementalPayload> getIncremental() {
1923
return this.incrementalItems;
2024
}
2125

2226
@Override
23-
public String getLabel() {
24-
return this.label;
27+
public boolean hasNext() {
28+
return this.hasNext;
2529
}
2630

2731
@Override
28-
public boolean hasNext() {
29-
return this.hasNext;
32+
public Map<Object, Object> getExtensions() {
33+
return this.extensions;
3034
}
3135

36+
/**
37+
* @return a {@link Builder} that can be used to create an instance of {@link DelayedIncrementalExecutionResultImpl}
38+
*/
3239
public static Builder newIncrementalExecutionResult() {
3340
return new Builder();
3441
}
3542

3643
public static class Builder {
3744
private boolean hasNext = false;
38-
private List<IncrementalItem> incrementalItems = Collections.emptyList();
39-
private String label = null;
45+
private List<IncrementalPayload> incrementalItems = Collections.emptyList();
46+
private Map<Object, Object> extensions;
4047

41-
public DelayedIncrementalExecutionResultImpl.Builder hasNext(boolean hasNext) {
48+
public Builder hasNext(boolean hasNext) {
4249
this.hasNext = hasNext;
4350
return this;
4451
}
4552

46-
public DelayedIncrementalExecutionResultImpl.Builder incrementalItems(List<IncrementalItem> incrementalItems) {
53+
public Builder incrementalItems(List<IncrementalPayload> incrementalItems) {
4754
this.incrementalItems = incrementalItems;
4855
return this;
4956
}
5057

51-
public DelayedIncrementalExecutionResultImpl.Builder label(String label) {
52-
this.label = label;
58+
public Builder extensions(boolean hasNext) {
59+
this.hasNext = hasNext;
5360
return this;
5461
}
5562

5663
public DelayedIncrementalExecutionResultImpl build() {
57-
return new DelayedIncrementalExecutionResultImpl(this.incrementalItems, this.label, this.hasNext);
64+
return new DelayedIncrementalExecutionResultImpl(this);
5865
}
5966
}
6067
}

src/main/java/graphql/incremental/IncrementalExecutionResult.java

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,108 @@
44
import graphql.ExperimentalApi;
55
import org.reactivestreams.Publisher;
66

7+
import javax.annotation.Nullable;
8+
import java.util.List;
9+
10+
/**
11+
* A result that is part of an execution that includes incrementally delivered data (data has been deferred of streamed).
12+
* <p>
13+
* For example, this query
14+
* <pre>
15+
* query {
16+
* person(id: "cGVvcGxlOjE=") {
17+
* ...HomeWorldFragment @defer(label: "homeWorldDefer")
18+
* name
19+
* films @stream(initialCount: 1, label: "filmsStream") {
20+
* title
21+
* }
22+
* }
23+
* }
24+
* fragment HomeWorldFragment on Person {
25+
* homeWorld {
26+
* name
27+
* }
28+
* }
29+
* </pre>
30+
* Could result on an incremental response with the following payloads (in JSON format here for simplicity).
31+
* <p>
32+
* <b>Response 1, the initial response does not contain any deferred or streamed results.</b>
33+
* <pre>
34+
* {
35+
* "data": {
36+
* "person": {
37+
* "name": "Luke Skywalker",
38+
* "films": [{ "title": "A New Hope" }]
39+
* }
40+
* },
41+
* "hasNext": true
42+
* }
43+
* </pre>
44+
*
45+
* <b>Response 2, contains the defer payload and the first stream payload.</b>
46+
* <pre>
47+
* {
48+
* "incremental": [
49+
* {
50+
* "label": "homeWorldDefer",
51+
* "path": ["person"],
52+
* "data": { "homeWorld": { "name": "Tatooine" } }
53+
* },
54+
* {
55+
* "label": "filmsStream",
56+
* "path": ["person", "films", 1],
57+
* "items": [{ "title": "The Empire Strikes Back" }]
58+
* }
59+
* ],
60+
* "hasNext": true
61+
* }
62+
* </pre>
63+
*
64+
* <b>Response 3, contains the final stream payload. Note how "hasNext" is "false", indicating this is the final response.
65+
* <pre>
66+
* {
67+
* "incremental": [
68+
* {
69+
* "label": "filmsStream",
70+
* "path": ["person", "films", 2],
71+
* "items": [{ "title": "Return of the Jedi" }]
72+
* }
73+
* ],
74+
* "hasNext": false
75+
* }
76+
* </pre>
77+
*
78+
* <p>
79+
* This implementation is based on the state of <a href="https://github.com/graphql/graphql-spec/pull/742">Defer/Stream PR</a>
80+
* More specifically at the state of this
81+
* <a href="https://github.com/graphql/graphql-spec/commit/c630301560d9819d33255d3ba00f548e8abbcdc6">commit</a>
82+
* <p>
83+
* The execution behaviour should match what we get from running Apollo Server 4.9.5 with graphql-js v17.0.0-alpha.2
84+
*/
785
@ExperimentalApi
886
public interface IncrementalExecutionResult extends ExecutionResult {
87+
/**
88+
* Indicates whether there are pending incremental data.
89+
* @return "true" if there are incremental data, "false" otherwise.
90+
*/
991
boolean hasNext();
1092

93+
/**
94+
* Returns a list of defer and/or stream payloads that the execution engine decided (for whatever reason) to resolve at the same time as the initial payload.
95+
* <p>
96+
* <quote>(...)this field may appear on both the initial and subsequent values.</quote>
97+
* <p>
98+
* <a href="https://github.com/graphql/graphql-spec/pull/742/files#diff-98d0cd153b72b63c417ad4238e8cc0d3385691ccbde7f7674bc0d2a718b896ecR271">source</a>
99+
*
100+
* @return a list of Stream and/or Defer payloads that were resolved at the same time as the initial payload.
101+
*/
102+
@Nullable
103+
List<IncrementalPayload> getIncremental();
104+
105+
/**
106+
* This {@link Publisher} will asynchronously emit events containing defer and/or stream payloads.
107+
*
108+
* @return a {@link Publisher} that clients can subscribe to receive incremental payloads.
109+
*/
11110
Publisher<DelayedIncrementalExecutionResult> getIncrementalItemPublisher();
12111
}

0 commit comments

Comments
 (0)