Skip to content

Commit c2c6ae6

Browse files
committed
Expose map of "extensions" in GraphQlRequest
This commit adds support for protocol, vendor specific protocol extensions in the GraphQL client requests. Closes gh-371
1 parent a2c038e commit c2c6ae6

File tree

18 files changed

+159
-19
lines changed

18 files changed

+159
-19
lines changed

spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/AbstractDirectGraphQlTransport.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ public Flux<GraphQlResponse> executeSubscription(GraphQlRequest request) {
7575

7676
private ExecutionGraphQlRequest toExecutionRequest(GraphQlRequest request) {
7777
return new DefaultExecutionGraphQlRequest(
78-
request.getDocument(), request.getOperationName(), request.getVariables(),
78+
request.getDocument(), request.getOperationName(), request.getVariables(), request.getExtensions(),
7979
idGenerator.generateId().toString(), null);
8080
}
8181

spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/DefaultGraphQlTester.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,8 @@ private final class DefaultRequest implements Request<DefaultRequest> {
125125

126126
private final Map<String, Object> variables = new LinkedHashMap<>();
127127

128+
private final Map<String, Object> extensions = new LinkedHashMap<>();
129+
128130
private DefaultRequest(String document) {
129131
Assert.notNull(document, "`document` is required");
130132
this.document = document;
@@ -142,6 +144,12 @@ public DefaultRequest variable(String name, @Nullable Object value) {
142144
return this;
143145
}
144146

147+
@Override
148+
public DefaultRequest extension(String name, Object value) {
149+
this.extensions.put(name, value);
150+
return this;
151+
}
152+
145153
@SuppressWarnings("ConstantConditions")
146154
@Override
147155
public Response execute() {
@@ -159,7 +167,7 @@ public Subscription executeSubscription() {
159167
}
160168

161169
private GraphQlRequest request() {
162-
return new DefaultGraphQlRequest(this.document, this.operationName, this.variables);
170+
return new DefaultGraphQlRequest(this.document, this.operationName, this.variables, this.extensions);
163171
}
164172

165173
private DefaultResponse mapResponse(GraphQlResponse response, GraphQlRequest request) {

spring-graphql-test/src/main/java/org/springframework/graphql/test/tester/GraphQlTester.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,15 @@ interface Request<T extends Request<T>> {
149149
*/
150150
T variable(String name, @Nullable Object value);
151151

152+
/**
153+
* Add a variable.
154+
* @param name the variable name
155+
* @param value the variable value, possibly {@code null} since GraphQL
156+
* supports providing null value vs not providing a value at all.
157+
* @return this request spec
158+
*/
159+
T extension(String name, @Nullable Object value);
160+
152161
/**
153162
* Execute the GraphQL request and return a spec for further inspection of
154163
* response data and errors.

spring-graphql-test/src/test/java/org/springframework/graphql/test/tester/GraphQlTesterTests.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,23 @@ void operationNameAndVariables() {
217217
assertThat(request.getVariables()).containsEntry("keyOnly", null);
218218
}
219219

220+
@Test
221+
void protocolExtensions() {
222+
String document = "{me {name, friends}}";
223+
getGraphQlService().setDataAsJson(document, "{\"me\": {\"name\":\"Luke Skywalker\", \"friends\":[]}}");
224+
225+
graphQlTester().document(document)
226+
.extension("firstExt", Collections.singletonMap("key", "value"))
227+
.extension("secondExt", "value")
228+
.execute();
229+
230+
ExecutionGraphQlRequest request = getGraphQlService().getGraphQlRequest();
231+
assertThat(request.getDocument()).contains(document);
232+
assertThat(request.getExtensions()).hasSize(2);
233+
assertThat(request.getExtensions()).containsEntry("firstExt", Collections.singletonMap("key", "value"))
234+
.containsEntry("secondExt", "value");
235+
}
236+
220237
@Test
221238
void errorsEmptyOnExecuteAndVerify() {
222239

spring-graphql/src/main/java/org/springframework/graphql/GraphQlRequest.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,11 @@ public interface GraphQlRequest {
5353
*/
5454
Map<String, Object> getVariables();
5555

56+
/**
57+
* Return implementor specific, protocol extensions, if any.
58+
*/
59+
Map<String, Object> getExtensions();
60+
5661
/**
5762
* Convert the request to a {@link Map} as defined in
5863
* <a href="https://github.com/graphql/graphql-over-http/blob/main/spec/GraphQLOverHTTP.md">GraphQL over HTTP</a> and

spring-graphql/src/main/java/org/springframework/graphql/client/DefaultClientGraphQlRequest.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,11 @@ final class DefaultClientGraphQlRequest extends DefaultGraphQlRequest implements
3535

3636

3737
DefaultClientGraphQlRequest(
38-
String document, @Nullable String operationName, Map<String, Object> variables,
38+
String document, @Nullable String operationName,
39+
Map<String, Object> variables, Map<String, Object> extensions,
3940
Map<String, Object> attributes) {
4041

41-
super(document, operationName, variables);
42+
super(document, operationName, variables, extensions);
4243
this.attributes.putAll(attributes);
4344
}
4445

spring-graphql/src/main/java/org/springframework/graphql/client/DefaultGraphQlClient.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ private final class DefaultRequestSpec implements RequestSpec {
9494

9595
private final Map<String, Object> attributes = new LinkedHashMap<>();
9696

97+
private final Map<String, Object> extensions = new LinkedHashMap<>();
98+
9799
DefaultRequestSpec(Mono<String> documentMono) {
98100
Assert.notNull(documentMono, "'document' is required");
99101
this.documentMono = documentMono;
@@ -117,6 +119,18 @@ public RequestSpec variables(Map<String, Object> variables) {
117119
return this;
118120
}
119121

122+
@Override
123+
public RequestSpec extension(String name, Object value) {
124+
this.extensions.put(name, value);
125+
return this;
126+
}
127+
128+
@Override
129+
public RequestSpec extensions(Map<String, Object> extensions) {
130+
this.extensions.putAll(extensions);
131+
return this;
132+
}
133+
120134
@Override
121135
public RequestSpec attribute(String name, Object value) {
122136
this.attributes.put(name, value);
@@ -157,7 +171,7 @@ public Flux<ClientGraphQlResponse> executeSubscription() {
157171

158172
private Mono<ClientGraphQlRequest> initRequest() {
159173
return this.documentMono.map(document ->
160-
new DefaultClientGraphQlRequest(document, this.operationName, this.variables, this.attributes));
174+
new DefaultClientGraphQlRequest(document, this.operationName, this.variables, this.extensions, this.attributes));
161175
}
162176

163177
}

spring-graphql/src/main/java/org/springframework/graphql/client/GraphQlClient.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,21 @@ interface RequestSpec {
151151
*/
152152
RequestSpec variables(Map<String, Object> variables);
153153

154+
/**
155+
* Add a value for a protocol extension.
156+
* @param name the protocol extension name
157+
* @param value the extension value
158+
* @return this request spec
159+
*/
160+
RequestSpec extension(String name, @Nullable Object value);
161+
162+
/**
163+
* Add all given protocol extensions.
164+
* @param extensions the protocol extensions
165+
* @return this request spec
166+
*/
167+
RequestSpec extensions(Map<String, Object> extensions);
168+
154169
/**
155170
* Set a client request attribute.
156171
* <p>This is purely for client side request processing, i.e. available

spring-graphql/src/main/java/org/springframework/graphql/server/RSocketGraphQlRequest.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ public class RSocketGraphQlRequest extends DefaultExecutionGraphQlRequest implem
4343
* @param locale the locale from the HTTP request, if any
4444
*/
4545
public RSocketGraphQlRequest(Map<String, Object> body, String id, @Nullable Locale locale) {
46-
super(getKey("query", body), getKey("operationName", body), getKey("variables", body), id, locale);
46+
super(getKey("query", body), getKey("operationName", body), getKey("variables", body),
47+
getKey("extensions", body), id, locale);
4748
}
4849

4950
@SuppressWarnings("unchecked")

spring-graphql/src/main/java/org/springframework/graphql/server/WebGraphQlRequest.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,8 @@ public class WebGraphQlRequest extends DefaultExecutionGraphQlRequest implements
5858
public WebGraphQlRequest(
5959
URI uri, HttpHeaders headers, Map<String, Object> body, String id, @Nullable Locale locale) {
6060

61-
super(getKey("query", body), getKey("operationName", body), getKey("variables", body), id, locale);
61+
super(getKey("query", body), getKey("operationName", body), getKey("variables", body),
62+
getKey("extensions", body), id, locale);
6263

6364
Assert.notNull(uri, "URI is required'");
6465
Assert.notNull(headers, "HttpHeaders is required'");

0 commit comments

Comments
 (0)