Skip to content

Commit 4e7d8de

Browse files
authored
Merge pull request #8 from graphql-java-kickstart/feature/custom-headers
Feature/custom headers
2 parents 56b2814 + afe4d30 commit 4e7d8de

File tree

12 files changed

+187
-29
lines changed

12 files changed

+187
-29
lines changed

gradle.properties

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
version=0.2.0-SNAPSHOT
1+
version=0.3.0-SNAPSHOT
22

33
PROJECT_GROUP = com.graphql-java-kickstart
44
PROJECT_NAME = graphql-spring-webclient
@@ -17,6 +17,8 @@ LIB_PROJECT_REACTOR_VER = 1.0.1
1717

1818
LIB_GRAPHQL_TOOLS_VER = 6.0.0
1919
LIB_GRAPHQL_SPRING_VER = 7.0.0
20+
LIB_GRAPHQL_JAVA_VER = 14.0
21+
LIB_GRAPHQL_SERVLET_VER = 9.0.1
2022

2123
### Gradle Plugins
2224

graphql-webclient/build.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,6 @@ dependencies {
66
testImplementation "com.graphql-java-kickstart:graphql-kickstart-spring-boot-starter-webflux:$LIB_GRAPHQL_SPRING_VER"
77
testImplementation "com.graphql-java-kickstart:graphql-kickstart-spring-boot-starter-tools:$LIB_GRAPHQL_SPRING_VER"
88
testImplementation "com.graphql-java-kickstart:graphql-java-tools:$LIB_GRAPHQL_TOOLS_VER"
9+
testImplementation "com.graphql-java-kickstart:graphql-kickstart-spring-support:$LIB_GRAPHQL_SPRING_VER"
10+
testImplementation "com.graphql-java-kickstart:graphql-java-kickstart:$LIB_GRAPHQL_SERVLET_VER"
911
}
Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,21 @@
11
package graphql.kickstart.spring.webclient.boot;
22

3-
import lombok.Builder;
4-
import lombok.Value;
3+
import org.springframework.http.HttpHeaders;
54

6-
@Value
7-
@Builder
8-
class GraphQLRequest {
5+
public interface GraphQLRequest<T> {
96

10-
String query;
11-
Object variables;
12-
String operationName;
7+
static GraphQLRequestBuilder<Object> builder() {
8+
return new GraphQLRequestBuilder<>(Object.class);
9+
}
10+
11+
static <T> GraphQLRequestBuilder<T> builder(Class<T> returnType) {
12+
return new GraphQLRequestBuilder<>(returnType);
13+
}
14+
15+
GraphQLRequestBody getRequestBody();
16+
17+
HttpHeaders getHeaders();
18+
19+
Class<T> getReturnType();
1320

1421
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package graphql.kickstart.spring.webclient.boot;
2+
3+
import lombok.Builder;
4+
import lombok.Value;
5+
6+
@Value
7+
@Builder
8+
class GraphQLRequestBody {
9+
10+
String query;
11+
Object variables;
12+
String operationName;
13+
14+
static GraphQLRequestBodyBuilder builder() {
15+
return new GraphQLRequestBodyBuilder();
16+
}
17+
18+
static class GraphQLRequestBodyBuilder {
19+
// added this partial builder to let Javadoc play nice with Lombok, see https://stackoverflow.com/a/58809436/12507062
20+
}
21+
22+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package graphql.kickstart.spring.webclient.boot;
2+
3+
import java.io.IOException;
4+
import java.io.InputStream;
5+
import java.nio.charset.StandardCharsets;
6+
import java.util.Arrays;
7+
import lombok.SneakyThrows;
8+
import org.springframework.core.io.ClassPathResource;
9+
import org.springframework.core.io.Resource;
10+
import org.springframework.http.HttpHeaders;
11+
import org.springframework.util.StreamUtils;
12+
13+
public class GraphQLRequestBuilder<T> {
14+
15+
private final HttpHeaders headers = new HttpHeaders();
16+
private final GraphQLRequestBody.GraphQLRequestBodyBuilder bodyBuilder = GraphQLRequestBody.builder();
17+
private final Class<T> returnType;
18+
19+
GraphQLRequestBuilder(Class<T> returnType) {
20+
this.returnType = returnType;
21+
}
22+
23+
public GraphQLRequestBuilder<T> header(String name, String value) {
24+
headers.add(name, value);
25+
return this;
26+
}
27+
28+
public GraphQLRequestBuilder<T> header(String name, String... values) {
29+
headers.addAll(name, Arrays.asList(values));
30+
return this;
31+
}
32+
33+
public GraphQLRequestBuilder<T> resource(String resource) {
34+
return query(loadQuery(resource));
35+
}
36+
37+
@SneakyThrows
38+
private String loadQuery(String path) {
39+
return loadResource(new ClassPathResource(path));
40+
}
41+
42+
private String loadResource(Resource resource) throws IOException {
43+
try (InputStream inputStream = resource.getInputStream()) {
44+
return StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);
45+
}
46+
}
47+
48+
public GraphQLRequestBuilder<T> query(String query) {
49+
bodyBuilder.query(query);
50+
return this;
51+
}
52+
53+
public GraphQLRequestBuilder<T> variables(Object variables) {
54+
bodyBuilder.variables(variables);
55+
return this;
56+
}
57+
58+
public GraphQLRequestBuilder<T> operationName(String operationName) {
59+
bodyBuilder.operationName(operationName);
60+
return this;
61+
}
62+
63+
public GraphQLRequest<T> build() {
64+
return new GraphQLRequestImpl<>(headers, bodyBuilder.build(), returnType);
65+
}
66+
67+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package graphql.kickstart.spring.webclient.boot;
2+
3+
import lombok.Value;
4+
import org.springframework.http.HttpHeaders;
5+
6+
@Value
7+
class GraphQLRequestImpl<T> implements GraphQLRequest<T> {
8+
9+
HttpHeaders headers;
10+
GraphQLRequestBody requestBody;
11+
Class<T> returnType;
12+
13+
}

graphql-webclient/src/main/java/graphql/kickstart/spring/webclient/boot/GraphQLWebClient.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,12 @@ static GraphQLWebClient newInstance(WebClient webClient, ObjectMapper objectMapp
1616

1717
<T> Mono<T> post(String resource, Map<String, Object> variables, Class<T> returnType);
1818

19+
<T> Mono<T> post(GraphQLRequest<T> request);
20+
1921
<T> Flux<T> flux(String resource, Class<T> returnType);
2022

2123
<T> Flux<T> flux(String resource, Map<String, Object> variables, Class<T> returnType);
2224

25+
@SuppressWarnings("unchecked")
26+
<T> Flux<T> flux(GraphQLRequest<T> request);
2327
}

graphql-webclient/src/main/java/graphql/kickstart/spring/webclient/boot/GraphQLWebClientImpl.java

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,12 @@
11
package graphql.kickstart.spring.webclient.boot;
22

33
import com.fasterxml.jackson.databind.ObjectMapper;
4-
import java.io.IOException;
5-
import java.io.InputStream;
6-
import java.nio.charset.StandardCharsets;
74
import java.util.List;
85
import java.util.Map;
96
import lombok.RequiredArgsConstructor;
107
import lombok.SneakyThrows;
118
import lombok.extern.slf4j.Slf4j;
12-
import org.springframework.core.io.ClassPathResource;
13-
import org.springframework.core.io.Resource;
149
import org.springframework.http.MediaType;
15-
import org.springframework.util.StreamUtils;
1610
import org.springframework.web.reactive.function.client.WebClient;
1711
import reactor.core.publisher.Flux;
1812
import reactor.core.publisher.Mono;
@@ -40,15 +34,9 @@ private <T> T readValue(Object value, Class<T> returnType) {
4034
return objectMapper.convertValue(value, returnType);
4135
}
4236

43-
@SneakyThrows
44-
private String loadQuery(String path) {
45-
return loadResource(new ClassPathResource(path));
46-
}
47-
48-
private String loadResource(Resource resource) throws IOException {
49-
try (InputStream inputStream = resource.getInputStream()) {
50-
return StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);
51-
}
37+
@Override
38+
public <T> Mono<T> post(GraphQLRequest<T> request) {
39+
return execute(request).map(it -> readValue(it, request.getReturnType()));
5240
}
5341

5442
@Override
@@ -66,15 +54,28 @@ public <T> Flux<T> flux(String resource, Map<String, Object> variables, Class<T>
6654
.map(it -> readValue(it, returnType));
6755
}
6856

57+
@Override
58+
@SuppressWarnings("unchecked")
59+
public <T> Flux<T> flux(GraphQLRequest<T> request) {
60+
Mono<Object> responseObject = execute(request);
61+
return responseObject.map(List.class::cast)
62+
.flatMapMany(Flux::fromIterable)
63+
.map(it -> readValue(it, request.getReturnType()));
64+
}
65+
6966
private Mono<Object> execute(String resource, Map<String, Object> variables) {
70-
GraphQLRequest request = GraphQLRequest.builder()
71-
.query(loadQuery(resource))
67+
GraphQLRequest<?> request = GraphQLRequest.builder(Object.class)
68+
.resource(resource)
7269
.variables(variables)
7370
.build();
71+
return execute(request);
72+
}
7473

75-
return webClient.post()
76-
.contentType(MediaType.APPLICATION_JSON)
77-
.bodyValue(request)
74+
private Mono<Object> execute(GraphQLRequest<?> request) {
75+
WebClient.RequestBodySpec spec = webClient.post()
76+
.contentType(MediaType.APPLICATION_JSON);
77+
request.getHeaders().forEach((header, values) -> spec.header(header, values.toArray(new String[0])));
78+
return spec.bodyValue(request.getRequestBody())
7879
.retrieve()
7980
.bodyToMono(GraphQLResponse.class)
8081
.flatMap(it -> Mono.justOrEmpty(it.getFirstObject()));

graphql-webclient/src/test/java/graphql/kickstart/spring/webclient/boot/GraphQLWebClientTest.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,19 @@ void simpleTypeSucceeds() {
6666
assertEquals("response id should equal 'my-id'", "my-id", object.getId());
6767
}
6868

69+
@Test
70+
void simpleTypeAsRequestSucceeds() {
71+
GraphQLRequest<Simple> request = GraphQLRequest.builder(Simple.class)
72+
.resource("query-simple.graphql")
73+
.variables(Map.of("id", "my-id"))
74+
.build();
75+
Mono<Simple> response = graphqlClient.post(request);
76+
assertNotNull("response should not be null", response);
77+
Simple object = response.block();
78+
assertNotNull(object);
79+
assertEquals("response id should equal 'my-id'", "my-id", object.getId());
80+
}
81+
6982
@Test
7083
void errorResponseSucceeds() {
7184
Mono<String> response = graphqlClient.post("error.graphql", String.class);
@@ -87,4 +100,16 @@ void listSucceeds() {
87100
assertEquals(1, list.size());
88101
}
89102

103+
@Test
104+
void headerIsAdded() {
105+
GraphQLRequest<String> request = GraphQLRequest.builder(String.class)
106+
.resource("query-header.graphql")
107+
.variables(Map.of("name", "my-custom-header"))
108+
.header("my-custom-header", "my-custom-header-value")
109+
.build();
110+
Mono<String> response = graphqlClient.post(request);
111+
assertNotNull("response should not be null", response);
112+
assertEquals("response should equal 'my-custom-header-value'", "my-custom-header-value", response.block());
113+
}
114+
90115
}

graphql-webclient/src/test/java/graphql/kickstart/spring/webclient/testapp/Query.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22

33
import static java.util.Collections.singletonList;
44

5+
import graphql.kickstart.spring.GraphQLSpringContext;
56
import graphql.kickstart.tools.GraphQLQueryResolver;
7+
import graphql.schema.DataFetchingEnvironment;
68
import java.util.List;
79
import org.springframework.stereotype.Component;
810

@@ -29,4 +31,13 @@ List<Simple> list() {
2931
return singletonList(simple("1"));
3032
}
3133

34+
String header(String name, DataFetchingEnvironment env) {
35+
GraphQLSpringContext context = env.getContext();
36+
List<String> headers = context.getServerWebExchange().getRequest().getHeaders().get(name);
37+
if (headers != null && !headers.isEmpty()) {
38+
return headers.get(0);
39+
}
40+
return null;
41+
}
42+
3243
}

0 commit comments

Comments
 (0)