Skip to content

Commit 0ceb053

Browse files
committed
Add support for Flux responses
1 parent fda2092 commit 0ceb053

File tree

8 files changed

+66
-20
lines changed

8 files changed

+66
-20
lines changed

graphql-webclient-spring-boot-autoconfigure/src/main/java/graphql/kickstart/spring/webclient/boot/GraphQLWebClientAutoConfiguration.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.fasterxml.jackson.databind.ObjectMapper;
44
import lombok.RequiredArgsConstructor;
55
import lombok.extern.slf4j.Slf4j;
6+
import org.springframework.beans.factory.annotation.Autowired;
67
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
78
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
89
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
@@ -28,11 +29,11 @@ public class GraphQLWebClientAutoConfiguration {
2829

2930
@Bean
3031
@ConditionalOnMissingBean
31-
public WebClient webClient(ReactiveClientRegistrationRepository clientRegistrations) {
32+
public WebClient webClient(@Autowired(required = false) ReactiveClientRegistrationRepository clientRegistrations) {
3233
WebClient.Builder clientBuilder = WebClient.builder()
3334
.baseUrl(graphqlClientProperties.getUrl());
3435

35-
if (clientRegistrations.findByRegistrationId("graphql").blockOptional().isPresent()) {
36+
if (clientRegistrations != null && clientRegistrations.findByRegistrationId("graphql").blockOptional().isPresent()) {
3637
ServerOAuth2AuthorizedClientExchangeFilterFunction oauth =
3738
new ServerOAuth2AuthorizedClientExchangeFilterFunction(
3839
clientRegistrations,
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
package graphql.kickstart.spring.webclient.boot;
22

3-
import java.util.Collections;
43
import java.util.List;
54
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
65
import org.springframework.boot.context.properties.EnableConfigurationProperties;
76
import org.springframework.context.annotation.Bean;
87
import org.springframework.context.annotation.Configuration;
9-
import org.springframework.security.oauth2.client.registration.ClientRegistration;
108
import org.springframework.security.oauth2.client.registration.InMemoryReactiveClientRegistrationRepository;
119
import org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository;
1210

@@ -17,10 +15,10 @@ class OAuth2ClientRegistrationRepositoryConfiguration {
1715
@Bean
1816
@ConditionalOnMissingBean({ReactiveClientRegistrationRepository.class})
1917
InMemoryReactiveClientRegistrationRepository clientRegistrationRepository(OAuth2ClientRegistrationProperties properties) {
20-
List<ClientRegistration> registrations = properties.getClientRegistration()
18+
return properties.getClientRegistration()
2119
.map(List::of)
22-
.orElseGet(Collections::emptyList);
23-
return new InMemoryReactiveClientRegistrationRepository(registrations);
20+
.map(InMemoryReactiveClientRegistrationRepository::new)
21+
.orElse(null);
2422
}
2523

2624
}

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package graphql.kickstart.spring.webclient.boot;
22

33
import java.util.Map;
4+
import reactor.core.publisher.Flux;
45
import reactor.core.publisher.Mono;
56

67
public interface GraphQLWebClient {
@@ -9,4 +10,8 @@ public interface GraphQLWebClient {
910

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

13+
<T> Flux<T> flux(String resource, Class<T> returnType);
14+
15+
<T> Flux<T> flux(String resource, Map<String, Object> variables, Class<T> returnType);
16+
1217
}

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

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import java.io.IOException;
55
import java.io.InputStream;
66
import java.nio.charset.StandardCharsets;
7+
import java.util.List;
78
import java.util.Map;
89
import lombok.RequiredArgsConstructor;
910
import lombok.SneakyThrows;
@@ -13,6 +14,7 @@
1314
import org.springframework.http.MediaType;
1415
import org.springframework.util.StreamUtils;
1516
import org.springframework.web.reactive.function.client.WebClient;
17+
import reactor.core.publisher.Flux;
1618
import reactor.core.publisher.Mono;
1719

1820
@Slf4j
@@ -29,23 +31,12 @@ public <T> Mono<T> post(String resource, Class<T> returnType) {
2931

3032
@Override
3133
public <T> Mono<T> post(String resource, Map<String, Object> variables, Class<T> returnType) {
32-
GraphQLRequest request = GraphQLRequest.builder()
33-
.query(loadQuery(resource))
34-
.variables(variables)
35-
.build();
36-
37-
return webClient.post()
38-
.contentType(MediaType.APPLICATION_JSON)
39-
.bodyValue(request)
40-
.retrieve()
41-
.bodyToMono(GraphQLResponse.class)
42-
.flatMap(it -> Mono.justOrEmpty(it.getFirstObject()))
43-
.map(it -> readValue(it, returnType));
34+
return execute(resource, variables).map(it -> readValue(it, returnType));
4435
}
4536

4637
@SneakyThrows
4738
private <T> T readValue(Object value, Class<T> returnType) {
48-
log.debug("Read value: '{}'", value);
39+
log.trace("Read value: {}", value);
4940
return objectMapper.convertValue(value, returnType);
5041
}
5142

@@ -60,4 +51,33 @@ private String loadResource(Resource resource) throws IOException {
6051
}
6152
}
6253

54+
@Override
55+
public <T> Flux<T> flux(String resource, Class<T> returnType) {
56+
return flux(resource, null, returnType);
57+
}
58+
59+
@Override
60+
@SuppressWarnings("unchecked")
61+
public <T> Flux<T> flux(String resource, Map<String, Object> variables, Class<T> returnType) {
62+
Mono<Object> responseObject = execute(resource, variables);
63+
64+
return responseObject.map(List.class::cast)
65+
.flatMapMany(Flux::fromIterable)
66+
.map(it -> readValue(it, returnType));
67+
}
68+
69+
private Mono<Object> execute(String resource, Map<String, Object> variables) {
70+
GraphQLRequest request = GraphQLRequest.builder()
71+
.query(loadQuery(resource))
72+
.variables(variables)
73+
.build();
74+
75+
return webClient.post()
76+
.contentType(MediaType.APPLICATION_JSON)
77+
.bodyValue(request)
78+
.retrieve()
79+
.bodyToMono(GraphQLResponse.class)
80+
.flatMap(it -> Mono.justOrEmpty(it.getFirstObject()));
81+
}
82+
6383
}

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import com.fasterxml.jackson.databind.ObjectMapper;
99
import graphql.kickstart.spring.webclient.testapp.Simple;
10+
import java.util.List;
1011
import java.util.Map;
1112
import java.util.Optional;
1213
import org.junit.jupiter.api.BeforeEach;
@@ -17,6 +18,7 @@
1718
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
1819
import org.springframework.boot.web.server.LocalServerPort;
1920
import org.springframework.web.reactive.function.client.WebClient;
21+
import reactor.core.publisher.Flux;
2022
import reactor.core.publisher.Mono;
2123

2224
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
@@ -77,4 +79,11 @@ void noResponseSucceeds() {
7779
assertTrue("response should be empty", noResponse.isEmpty());
7880
}
7981

82+
@Test
83+
void listSucceeds() {
84+
Flux<Simple> response = graphqlClient.flux("query-list.graphql", Simple.class);
85+
List<Simple> list = response.collectList().block();
86+
assertEquals(1, list.size());
87+
}
88+
8089
}

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package graphql.kickstart.spring.webclient.testapp;
22

3+
import static java.util.Collections.singletonList;
4+
35
import graphql.kickstart.tools.GraphQLQueryResolver;
6+
import java.util.List;
47
import org.springframework.stereotype.Component;
58

69
@Component
@@ -22,4 +25,8 @@ Simple simple(String id) {
2225
return new Simple(id);
2326
}
2427

28+
List<Simple> list() {
29+
return singletonList(simple("1"));
30+
}
31+
2532
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
query list {
2+
list {
3+
id
4+
}
5+
}

graphql-webclient/src/test/resources/schema.graphqls

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ type Query {
33
noResponse: String
44
echo(value: String!): String!
55
simple(id: ID!): Simple!
6+
list: [Simple!]!
67
}
78

89
type Simple {

0 commit comments

Comments
 (0)