Skip to content

Commit 383b72c

Browse files
committed
implement reactive endpoint for GraphQL Query
issue spring-projects#3501
1 parent 2753c45 commit 383b72c

File tree

3 files changed

+63
-40
lines changed

3 files changed

+63
-40
lines changed

spring-integration-graphql/src/main/java/org/springframework/integration/graphql/outbound/GraphqlQueryMessageHandler.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,12 @@
1616

1717
package org.springframework.integration.graphql.outbound;
1818

19-
import graphql.ExecutionResult;
2019
import org.springframework.graphql.GraphQlService;
2120
import org.springframework.graphql.RequestInput;
2221
import org.springframework.integration.handler.AbstractReplyProducingMessageHandler;
2322
import org.springframework.messaging.Message;
24-
import reactor.core.publisher.Flux;
23+
24+
import graphql.ExecutionResult;
2525
import reactor.core.publisher.Mono;
2626

2727
/**
@@ -34,20 +34,20 @@ public class GraphqlQueryMessageHandler extends AbstractReplyProducingMessageHan
3434

3535
public GraphqlQueryMessageHandler(final GraphQlService graphQlService) {
3636
this.graphQlService = graphQlService;
37+
setAsync(true);
3738
}
3839

3940
@Override
4041
protected Object handleRequestMessage(Message<?> requestMessage) {
4142

42-
if( requestMessage.getPayload() instanceof RequestInput ) {
43+
if (requestMessage.getPayload() instanceof RequestInput) {
4344

4445
Mono<ExecutionResult> result = this.graphQlService
4546
.execute((RequestInput) requestMessage.getPayload());
4647

4748
return result;
48-
49-
} else {
50-
49+
}
50+
else {
5151
throw new IllegalArgumentException("Message payload needs to be 'org.springframework.graphql.RequestInput'");
5252
}
5353
}

spring-integration-graphql/src/test/java/org/springframework/integration/graphql/outbound/GraphqlQueryMessageHandlerTests.java

Lines changed: 40 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,26 @@
1616

1717
package org.springframework.integration.graphql.outbound;
1818

19-
import graphql.ExecutionResult;
20-
import graphql.ExecutionResultImpl;
21-
import org.junit.jupiter.api.BeforeEach;
19+
import static org.assertj.core.api.Assertions.assertThat;
20+
21+
import java.time.Duration;
22+
import java.util.Collections;
23+
import java.util.Map;
24+
2225
import org.junit.jupiter.api.Test;
26+
2327
import org.springframework.beans.factory.annotation.Autowired;
24-
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
2528
import org.springframework.context.annotation.Bean;
2629
import org.springframework.context.annotation.Configuration;
2730
import org.springframework.core.io.ClassPathResource;
2831
import org.springframework.graphql.GraphQlService;
2932
import org.springframework.graphql.RequestInput;
3033
import org.springframework.graphql.data.method.annotation.QueryMapping;
3134
import org.springframework.graphql.data.method.annotation.support.AnnotatedControllerConfigurer;
32-
import org.springframework.graphql.execution.*;
35+
import org.springframework.graphql.execution.BatchLoaderRegistry;
36+
import org.springframework.graphql.execution.DefaultBatchLoaderRegistry;
37+
import org.springframework.graphql.execution.ExecutionGraphQlService;
38+
import org.springframework.graphql.execution.GraphQlSource;
3339
import org.springframework.integration.channel.FluxMessageChannel;
3440
import org.springframework.integration.channel.QueueChannel;
3541
import org.springframework.integration.config.EnableIntegration;
@@ -39,24 +45,18 @@
3945
import org.springframework.messaging.Message;
4046
import org.springframework.messaging.MessageHandlingException;
4147
import org.springframework.messaging.PollableChannel;
42-
import org.springframework.messaging.support.GenericMessage;
48+
import org.springframework.messaging.support.ErrorMessage;
4349
import org.springframework.messaging.support.MessageBuilder;
4450
import org.springframework.stereotype.Controller;
4551
import org.springframework.test.annotation.DirtiesContext;
46-
import org.springframework.test.context.TestPropertySource;
4752
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
48-
import reactor.core.Disposable;
53+
54+
import graphql.ExecutionResult;
55+
import graphql.ExecutionResultImpl;
4956
import reactor.core.publisher.Flux;
5057
import reactor.core.publisher.Mono;
5158
import reactor.test.StepVerifier;
5259

53-
import java.time.Duration;
54-
import java.util.Map;
55-
56-
import static java.util.Collections.emptyMap;
57-
import static org.assertj.core.api.Assertions.assertThat;
58-
import static org.junit.jupiter.api.Assertions.assertThrows;
59-
6060
/**
6161
*
6262
* @author Daniel Frey
@@ -72,33 +72,30 @@ public class GraphqlQueryMessageHandlerTests {
7272
@Autowired
7373
private FluxMessageChannel resultChannel;
7474

75+
@Autowired
76+
private PollableChannel errorChannel;
77+
7578
@Test
7679
void testHandleMessageForQuery() {
7780

78-
ExecutionResult expected = new ExecutionResultImpl(
79-
Map.of( "testQuery", Map.of( "id", "test-data") ),
80-
null, null
81-
);
8281
StepVerifier verifier = StepVerifier.create(
83-
// Flux.from(
84-
this.resultChannel
85-
// )
86-
// .map(Message::getPayload)
87-
// .cast(ExecutionResult.class)
82+
Flux.from(this.resultChannel)
83+
.map(Message::getPayload)
84+
.cast(ExecutionResult.class)
8885
)
89-
.consumeNextWith( result -> {
86+
.consumeNextWith(result -> {
9087
assertThat(result).isInstanceOf(ExecutionResultImpl.class);
91-
// Map<String, Object> data = ((ExecutionResult) result).getData();
92-
// Map<String, Object> testQuery = (Map<String, Object>) data.get("testQuery");
93-
// assertThat(testQuery.get("id")).isEqualTo("test-data");
88+
Map<String, Object> data = result.getData();
89+
Map<String, Object> testQuery = (Map<String, Object>) data.get("testQuery");
90+
assertThat(testQuery.get("id")).isEqualTo("test-data");
9491
}
9592
)
9693
.thenCancel()
9794
.verifyLater();
9895

9996
this.inputChannel.send(
10097
MessageBuilder
101-
.withPayload(new RequestInput("{ testQuery { id } }", null, emptyMap()))
98+
.withPayload(new RequestInput("{ testQuery { id } }", null, Collections.emptyMap()))
10299
.build()
103100
);
104101

@@ -108,8 +105,20 @@ void testHandleMessageForQuery() {
108105
@Test
109106
void testHandleMessageForQueryWithInvalidPayload() {
110107

111-
Message<?> testMessage = new GenericMessage<>("{ testQuery { id } }");
112-
assertThrows( IllegalArgumentException.class, () -> this.inputChannel.send(testMessage));
108+
this.inputChannel.send(
109+
MessageBuilder
110+
.withPayload("{ testQuery { id } }")
111+
.build()
112+
);
113+
114+
Message<?> errorMessage = errorChannel.receive(10_000);
115+
assertThat(errorMessage).isNotNull()
116+
.isInstanceOf(ErrorMessage.class)
117+
.extracting(Message::getPayload)
118+
.isInstanceOf(MessageHandlingException.class)
119+
.satisfies((ex) -> assertThat((Exception) ex)
120+
.hasMessageContaining(
121+
"Message payload needs to be 'org.springframework.graphql.RequestInput'"));
113122

114123
}
115124

@@ -125,9 +134,6 @@ public Mono<QueryResult> testQuery() {
125134

126135
@Configuration
127136
@EnableIntegration
128-
@TestPropertySource(properties = {
129-
"logging.level.org.springframework.integration=TRACE"
130-
})
131137
static class TestConfig {
132138

133139
@Bean
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<Configuration status="WARN">
3+
<Appenders>
4+
<Console name="STDOUT" target="SYSTEM_OUT">
5+
<PatternLayout pattern="%d %p [%t] [%c] - %m%n" />
6+
</Console>
7+
</Appenders>
8+
<Loggers>
9+
<Logger name="test" level="debug"/>
10+
<Logger name="org.springframework" level="warn"/>
11+
<Logger name="org.springframework.graphql" level="warn"/>
12+
<Logger name="org.springframework.integration.graphql" level="warn"/>
13+
<Root level="warn">
14+
<AppenderRef ref="STDOUT" />
15+
</Root>
16+
</Loggers>
17+
</Configuration>

0 commit comments

Comments
 (0)