Skip to content

Commit 6b792b6

Browse files
committed
Fix fileVariables overriding variables, tests for HttpGraphQlTester, MultipartBodyCreator
1 parent 910b128 commit 6b792b6

File tree

9 files changed

+206
-12
lines changed

9 files changed

+206
-12
lines changed

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

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,7 @@
1818

1919
import java.lang.reflect.Type;
2020
import java.time.Duration;
21-
import java.util.ArrayList;
22-
import java.util.Arrays;
23-
import java.util.LinkedHashMap;
24-
import java.util.List;
25-
import java.util.Map;
21+
import java.util.*;
2622
import java.util.function.Consumer;
2723
import java.util.function.Predicate;
2824
import java.util.function.Supplier;
@@ -38,6 +34,7 @@
3834
import org.springframework.graphql.GraphQlResponse;
3935
import org.springframework.graphql.ResponseError;
4036
import org.springframework.graphql.client.GraphQlTransport;
37+
import org.springframework.graphql.client.MultipartClientGraphQlRequest;
4138
import org.springframework.graphql.support.DefaultGraphQlRequest;
4239
import org.springframework.graphql.support.DocumentSource;
4340
import org.springframework.lang.Nullable;
@@ -127,7 +124,9 @@ private final class DefaultRequest implements Request<DefaultRequest> {
127124

128125
private final Map<String, Object> extensions = new LinkedHashMap<>();
129126

130-
private DefaultRequest(String document) {
127+
private final Map<String, Object> fileVariables = new LinkedHashMap<>();
128+
129+
private DefaultRequest(String document) {
131130
Assert.notNull(document, "`document` is required");
132131
this.document = document;
133132
}
@@ -144,7 +143,19 @@ public DefaultRequest variable(String name, @Nullable Object value) {
144143
return this;
145144
}
146145

147-
@Override
146+
@Override
147+
public DefaultRequest fileVariable(String name, Object value) {
148+
this.fileVariables.put(name, value);
149+
return this;
150+
}
151+
152+
@Override
153+
public DefaultRequest fileVariables(Map<String, Object> variables) {
154+
this.fileVariables.putAll(variables);
155+
return this;
156+
}
157+
158+
@Override
148159
public DefaultRequest extension(String name, Object value) {
149160
this.extensions.put(name, value);
150161
return this;
@@ -156,6 +167,16 @@ public Response execute() {
156167
return transport.execute(request()).map(response -> mapResponse(response, request())).block(responseTimeout);
157168
}
158169

170+
@Override
171+
public Response executeFileUpload() {
172+
return transport.executeFileUpload(requestFileUpload()).map(response -> mapResponse(response, requestFileUpload())).block(responseTimeout);
173+
}
174+
175+
@Override
176+
public void executeFileUploadAndVerify() {
177+
executeFileUpload().path("$.errors").pathDoesNotExist();
178+
}
179+
159180
@Override
160181
public void executeAndVerify() {
161182
execute().path("$.errors").pathDoesNotExist();
@@ -170,6 +191,10 @@ private GraphQlRequest request() {
170191
return new DefaultGraphQlRequest(this.document, this.operationName, this.variables, this.extensions);
171192
}
172193

194+
private GraphQlRequest requestFileUpload() {
195+
return new MultipartClientGraphQlRequest(this.document, this.operationName, this.variables, this.extensions, new HashMap<>(), this.fileVariables);
196+
}
197+
173198
private DefaultResponse mapResponse(GraphQlResponse response, GraphQlRequest request) {
174199
return new DefaultResponse(response, errorFilter, assertDecorator(request), jsonPathConfig);
175200
}

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

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,11 @@
1818

1919
import java.time.Duration;
2020
import java.util.List;
21+
import java.util.Map;
2122
import java.util.function.Consumer;
2223
import java.util.function.Predicate;
2324

25+
import org.springframework.graphql.client.GraphQlClient;
2426
import reactor.core.publisher.Flux;
2527

2628
import org.springframework.core.ParameterizedTypeReference;
@@ -149,6 +151,10 @@ interface Request<T extends Request<T>> {
149151
*/
150152
T variable(String name, @Nullable Object value);
151153

154+
T fileVariable(String name, Object value);
155+
156+
T fileVariables(Map<String, Object> variables);
157+
152158
/**
153159
* Add a value for a protocol extension.
154160
* @param name the protocol extension name
@@ -166,7 +172,9 @@ interface Request<T extends Request<T>> {
166172
*/
167173
Response execute();
168174

169-
/**
175+
void executeFileUploadAndVerify();
176+
177+
/**
170178
* Execute the GraphQL request and verify the response contains no errors.
171179
*/
172180
void executeAndVerify();
@@ -180,6 +188,8 @@ interface Request<T extends Request<T>> {
180188
*/
181189
Subscription executeSubscription();
182190

191+
Response executeFileUpload();
192+
183193
}
184194

185195
/**
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package org.springframework.graphql.test.tester;
2+
3+
import org.junit.jupiter.api.Test;
4+
import org.springframework.core.io.ClassPathResource;
5+
import org.springframework.graphql.server.webflux.GraphQlHttpHandler;
6+
import org.springframework.http.codec.multipart.FilePart;
7+
import org.springframework.test.web.reactive.server.WebTestClient;
8+
import org.springframework.web.reactive.function.server.RouterFunction;
9+
import org.springframework.web.reactive.function.server.ServerResponse;
10+
11+
import java.util.ArrayList;
12+
import java.util.Collection;
13+
import java.util.List;
14+
import java.util.stream.Collectors;
15+
16+
import static org.assertj.core.api.Assertions.assertThat;
17+
import static org.springframework.web.reactive.function.server.RouterFunctions.route;
18+
19+
public class HttpGraphQlTesterTests {
20+
21+
private static final String DOCUMENT = "{ Mutation }";
22+
23+
@Test
24+
void shouldSendOneFile() {
25+
MultipartHttpBuilderSetup testerSetup = new MultipartHttpBuilderSetup();
26+
27+
HttpGraphQlTester.Builder<?> builder = testerSetup.initBuilder();
28+
HttpGraphQlTester tester = builder.build();
29+
tester.document(DOCUMENT)
30+
.variable("existingVar", "itsValue")
31+
.fileVariable("fileInput", new ClassPathResource("/foo.txt"))
32+
.executeFileUpload();
33+
assertThat(testerSetup.getWebGraphQlRequest().getVariables().get("existingVar")).isEqualTo("itsValue");
34+
assertThat(testerSetup.getWebGraphQlRequest().getVariables().get("fileInput")).isNotNull();
35+
assertThat(((FilePart)testerSetup.getWebGraphQlRequest().getVariables().get("fileInput")).filename()).isEqualTo("foo.txt");
36+
}
37+
38+
@Test
39+
void shouldSendOneCollectionOfFiles() {
40+
MultipartHttpBuilderSetup testerSetup = new MultipartHttpBuilderSetup();
41+
42+
HttpGraphQlTester.Builder<?> builder = testerSetup.initBuilder();
43+
HttpGraphQlTester tester = builder.build();
44+
List<ClassPathResource> resources = new ArrayList<>();
45+
resources.add(new ClassPathResource("/foo.txt"));
46+
resources.add(new ClassPathResource("/bar.txt"));
47+
tester.document(DOCUMENT)
48+
.variable("existingVar", "itsValue")
49+
.fileVariable("filesInput", resources)
50+
.executeFileUpload();
51+
assertThat(testerSetup.getWebGraphQlRequest().getVariables().get("existingVar")).isEqualTo("itsValue");
52+
assertThat(testerSetup.getWebGraphQlRequest().getVariables().get("filesInput")).isNotNull();
53+
assertThat(((Collection<FilePart>)testerSetup.getWebGraphQlRequest().getVariables().get("filesInput")).size()).isEqualTo(2);
54+
assertThat(((Collection<FilePart>)testerSetup.getWebGraphQlRequest().getVariables().get("filesInput")).stream().map(filePart -> filePart.filename()).collect(Collectors.toSet())).contains("foo.txt", "bar.txt");
55+
}
56+
57+
private static class MultipartHttpBuilderSetup extends WebGraphQlTesterBuilderTests.WebBuilderSetup {
58+
59+
@Override
60+
public HttpGraphQlTester.Builder<?> initBuilder() {
61+
GraphQlHttpHandler handler = new GraphQlHttpHandler(webGraphQlHandler());
62+
RouterFunction<ServerResponse> routerFunction = route().POST("/**", handler::handleMultipartRequest).build();
63+
return HttpGraphQlTester.builder(WebTestClient.bindToRouterFunction(routerFunction).configureClient());
64+
}
65+
66+
}
67+
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ private interface TesterBuilderSetup {
204204
}
205205

206206

207-
private static class WebBuilderSetup implements TesterBuilderSetup {
207+
static class WebBuilderSetup implements TesterBuilderSetup {
208208

209209
@Nullable
210210
private WebGraphQlRequest request;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
hello from bar here!
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
hello here!

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,13 +128,15 @@ public RequestSpec variables(Map<String, Object> variables) {
128128

129129
@Override
130130
public DefaultRequestSpec fileVariable(String name, Object value) {
131+
Assert.notNull(name, "'name' is required");
132+
Assert.notNull(value, "'value' is required");
131133
this.fileVariables.put(name, value);
132134
return this;
133135
}
134136

135137
@Override
136138
public RequestSpec fileVariables(Map<String, Object> files) {
137-
this.fileVariables.putAll(variables);
139+
this.fileVariables.putAll(files);
138140
return this;
139141
}
140142

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,11 @@
2828
* @author Nikita Konev
2929
* @since 1.0.0
3030
*/
31-
final class MultipartClientGraphQlRequest extends DefaultClientGraphQlRequest implements ClientGraphQlRequest {
31+
public final class MultipartClientGraphQlRequest extends DefaultClientGraphQlRequest implements ClientGraphQlRequest {
3232

3333
private final Map<String, Object> fileVariables = new ConcurrentHashMap<>();
3434

35-
MultipartClientGraphQlRequest(
35+
public MultipartClientGraphQlRequest(
3636
String document, @Nullable String operationName,
3737
Map<String, Object> variables, Map<String, Object> extensions,
3838
Map<String, Object> attributes,
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package org.springframework.graphql.client;
2+
3+
import org.junit.jupiter.api.Assertions;
4+
import org.junit.jupiter.api.Test;
5+
import org.springframework.core.io.ClassPathResource;
6+
import org.springframework.http.HttpEntity;
7+
import org.springframework.util.MultiValueMap;
8+
9+
import java.util.*;
10+
11+
public class MultipartBodyCreatorTests {
12+
13+
@Test
14+
public void shouldGenerateVariableForOneFile() {
15+
Map<String, Object> variables = new HashMap<>();
16+
variables.put("existingVar", "itsValue");
17+
MultipartClientGraphQlRequest multipartClientGraphQlRequest = new MultipartClientGraphQlRequest(
18+
"mockDoc",
19+
"opName",
20+
variables,
21+
Collections.emptyMap(),
22+
Collections.emptyMap(),
23+
Collections.singletonMap("fileInput", new ClassPathResource("/foo.txt"))
24+
);
25+
MultiValueMap<String, ?> stringMultiValueMap = MultipartBodyCreator.convertRequestToMultipartData(multipartClientGraphQlRequest);
26+
27+
HttpEntity<?> operations = (HttpEntity<?>) stringMultiValueMap.get("operations").get(0);
28+
Map<String, Object> operationsBody = (Map<String, Object>) operations.getBody();
29+
Assertions.assertEquals("mockDoc", operationsBody.get("query"));
30+
Assertions.assertEquals("opName", operationsBody.get("operationName"));
31+
Map<String, Object> resultVariables = (Map<String, Object>) operationsBody.get("variables");
32+
Assertions.assertTrue(resultVariables.containsKey("fileInput"));
33+
Assertions.assertNull(resultVariables.get("fileInput"));
34+
Assertions.assertEquals("itsValue", resultVariables.get("existingVar"));
35+
36+
HttpEntity<?> mappings = (HttpEntity<?>) stringMultiValueMap.get("map").get(0);
37+
Map<String, Object> mappingsBody = (Map<String, Object>) mappings.getBody();
38+
Assertions.assertTrue((((List<String>)mappingsBody.get("uploadPart0")).containsAll(Collections.singletonList("variables.fileInput"))));
39+
40+
HttpEntity<?> filePart = (HttpEntity<?>) stringMultiValueMap.get("uploadPart0").get(0);
41+
Assertions.assertTrue(filePart.getBody() instanceof ClassPathResource);
42+
}
43+
44+
@Test
45+
public void shouldGenerateVariableForCollectionOfFiles() {
46+
Map<String, Object> variables = new HashMap<>();
47+
variables.put("existingVar", "itsValue");
48+
List<ClassPathResource> resources = new ArrayList<>();
49+
resources.add(new ClassPathResource("/foo.txt"));
50+
resources.add(new ClassPathResource("/bar.txt"));
51+
52+
MultipartClientGraphQlRequest multipartClientGraphQlRequest = new MultipartClientGraphQlRequest(
53+
"mockDoc",
54+
"opName",
55+
variables,
56+
Collections.emptyMap(),
57+
Collections.emptyMap(),
58+
Collections.singletonMap("fileInput", resources)
59+
);
60+
MultiValueMap<String, ?> stringMultiValueMap = MultipartBodyCreator.convertRequestToMultipartData(multipartClientGraphQlRequest);
61+
62+
HttpEntity<?> operations = (HttpEntity<?>) stringMultiValueMap.get("operations").get(0);
63+
Map<String, Object> operationsBody = (Map<String, Object>) operations.getBody();
64+
Assertions.assertEquals("mockDoc", operationsBody.get("query"));
65+
Assertions.assertEquals("opName", operationsBody.get("operationName"));
66+
Map<String, Object> resultVariables = (Map<String, Object>) operationsBody.get("variables");
67+
Assertions.assertTrue(resultVariables.containsKey("fileInput"));
68+
List<Object> fileInputValues = (List<Object>) resultVariables.get("fileInput");
69+
Assertions.assertNotNull(fileInputValues);
70+
Assertions.assertEquals(2, fileInputValues.size());
71+
Assertions.assertNull(fileInputValues.get(0));
72+
Assertions.assertNull(fileInputValues.get(1));
73+
74+
Assertions.assertEquals("itsValue", resultVariables.get("existingVar"));
75+
76+
HttpEntity<?> mappings = (HttpEntity<?>) stringMultiValueMap.get("map").get(0);
77+
Map<String, Object> mappingsBody = (Map<String, Object>) mappings.getBody();
78+
Assertions.assertTrue((((List<String>)mappingsBody.get("uploadPart0")).containsAll(Collections.singletonList("variables.fileInput.0"))));
79+
Assertions.assertTrue((((List<String>)mappingsBody.get("uploadPart1")).containsAll(Collections.singletonList("variables.fileInput.1"))));
80+
81+
HttpEntity<?> filePart0 = (HttpEntity<?>) stringMultiValueMap.get("uploadPart0").get(0);
82+
Assertions.assertTrue(filePart0.getBody() instanceof ClassPathResource);
83+
84+
HttpEntity<?> filePart1 = (HttpEntity<?>) stringMultiValueMap.get("uploadPart1").get(0);
85+
Assertions.assertTrue(filePart1.getBody() instanceof ClassPathResource);
86+
87+
}
88+
}

0 commit comments

Comments
 (0)