Convert websocket error message payload to array
As expected in graphql-ws protocol See gh-200
This commit is contained in:
@@ -245,7 +245,10 @@ public class GraphQlWebSocketHandler implements WebSocketHandler {
|
||||
.message(ex.getMessage())
|
||||
.build()
|
||||
.toSpecification();
|
||||
return Mono.just(encode(session, id, MessageType.ERROR, errorMap));
|
||||
|
||||
// Payload needs to be an array
|
||||
// see: https://github.com/enisdenjo/graphql-ws/blob/master/docs/interfaces/common.ErrorMessage.md#payload
|
||||
return Mono.just(encode(session, id, MessageType.ERROR, Collections.singletonList(errorMap)));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -259,7 +259,10 @@ public class GraphQlWebSocketHandler extends TextWebSocketHandler implements Sub
|
||||
String message = ex.getMessage();
|
||||
Map<String, Object> errorMap = GraphqlErrorBuilder.newError().errorType(errorType).message(message).build()
|
||||
.toSpecification();
|
||||
return Mono.just(encode(id, MessageType.ERROR, errorMap));
|
||||
|
||||
// Payload needs to be an array
|
||||
// see: https://github.com/enisdenjo/graphql-ws/blob/master/docs/interfaces/common.ErrorMessage.md#payload
|
||||
return Mono.just(encode(id, MessageType.ERROR, Collections.singletonList(errorMap)));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,8 @@ import java.util.function.BiConsumer;
|
||||
|
||||
import org.assertj.core.api.InstanceOfAssertFactories;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.graphql.GraphQlSetup;
|
||||
import org.springframework.graphql.web.WebGraphQlHandler;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.core.publisher.Sinks;
|
||||
@@ -245,6 +247,60 @@ public class GraphQlWebSocketHandlerTests extends WebSocketHandlerTestSupport {
|
||||
.verifyTimeout(Duration.ofMillis(500));
|
||||
}
|
||||
|
||||
@Test
|
||||
void errorMessagePayloadIsCorrectArray() {
|
||||
final String GREETING_QUERY = "{" +
|
||||
"\"id\":\"" + SUBSCRIPTION_ID + "\"," +
|
||||
"\"type\":\"subscribe\"," +
|
||||
"\"payload\":{\"query\": \"" +
|
||||
" subscription TestTypenameSubscription {" +
|
||||
" greeting" +
|
||||
" }\"}" +
|
||||
"}";
|
||||
|
||||
WebGraphQlHandler initHandler = GraphQlSetup.schemaContent("" +
|
||||
"type Subscription { greeting: String! }" +
|
||||
"type Query { greetingUnused: String! }")
|
||||
.subscriptionFetcher("greeting", env -> Flux.just("a", null, "b"))
|
||||
.webInterceptor()
|
||||
.toWebGraphQlHandler();
|
||||
|
||||
GraphQlWebSocketHandler handler = new GraphQlWebSocketHandler(
|
||||
initHandler,
|
||||
ServerCodecConfigurer.create(),
|
||||
Duration.ofSeconds(60));
|
||||
|
||||
TestWebSocketSession session = new TestWebSocketSession(Flux.just(
|
||||
toWebSocketMessage("{\"type\":\"connection_init\"}"),
|
||||
toWebSocketMessage(GREETING_QUERY)));
|
||||
handler.handle(session).block();
|
||||
|
||||
StepVerifier.create(session.getOutput())
|
||||
.consumeNextWith((message) -> assertMessageType(message, "connection_ack"))
|
||||
.consumeNextWith((message) -> assertThat(decode(message))
|
||||
.hasSize(3)
|
||||
.containsEntry("id", SUBSCRIPTION_ID)
|
||||
.containsEntry("type", "next")
|
||||
.extractingByKey("payload", as(InstanceOfAssertFactories.map(String.class, Object.class)))
|
||||
.extractingByKey("data", as(InstanceOfAssertFactories.map(String.class, Object.class)))
|
||||
.containsEntry("greeting", "a"))
|
||||
.consumeNextWith((message) -> assertThat(decode(message))
|
||||
.hasSize(3)
|
||||
.containsEntry("id", SUBSCRIPTION_ID)
|
||||
.containsEntry("type", "error")
|
||||
.hasEntrySatisfying("payload", payload -> assertThat(payload)
|
||||
.asList()
|
||||
.hasSize(1)
|
||||
.allSatisfy(theError -> assertThat(theError)
|
||||
.asInstanceOf(InstanceOfAssertFactories.map(String.class, Object.class))
|
||||
.hasSize(3)
|
||||
.hasEntrySatisfying("locations", loc -> assertThat(loc).asList().isEmpty())
|
||||
.hasEntrySatisfying("message", msg -> assertThat(msg).asString().contains("null"))
|
||||
.extractingByKey("extensions", as(InstanceOfAssertFactories.map(String.class, Object.class)))
|
||||
.containsEntry("classification", "DataFetchingException"))))
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
private TestWebSocketSession handle(Flux<WebSocketMessage> input, WebInterceptor... interceptors) {
|
||||
GraphQlWebSocketHandler handler = new GraphQlWebSocketHandler(
|
||||
initHandler(interceptors),
|
||||
|
||||
@@ -29,6 +29,9 @@ import java.util.function.Consumer;
|
||||
|
||||
import org.assertj.core.api.InstanceOfAssertFactories;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.graphql.GraphQlSetup;
|
||||
import org.springframework.graphql.web.WebGraphQlHandler;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
@@ -249,6 +252,57 @@ public class GraphQlWebSocketHandlerTests extends WebSocketHandlerTestSupport {
|
||||
.verifyTimeout(Duration.ofMillis(500));
|
||||
}
|
||||
|
||||
@Test
|
||||
void errorMessagePayloadIsCorrectArray() throws Exception {
|
||||
final String GREETING_QUERY = "{" +
|
||||
"\"id\":\"" + SUBSCRIPTION_ID + "\"," +
|
||||
"\"type\":\"subscribe\"," +
|
||||
"\"payload\":{\"query\": \"" +
|
||||
" subscription TestTypenameSubscription {" +
|
||||
" greeting" +
|
||||
" }\"}" +
|
||||
"}";
|
||||
|
||||
WebGraphQlHandler initHandler = GraphQlSetup.schemaContent("" +
|
||||
"type Subscription { greeting: String! }" +
|
||||
"type Query { greetingUnused: String! }")
|
||||
.subscriptionFetcher("greeting", env -> Flux.just("a", null, "b"))
|
||||
.webInterceptor()
|
||||
.toWebGraphQlHandler();
|
||||
|
||||
GraphQlWebSocketHandler handler = new GraphQlWebSocketHandler(initHandler, converter, Duration.ofSeconds(60));
|
||||
|
||||
handle(handler,
|
||||
new TextMessage("{\"type\":\"connection_init\"}"),
|
||||
new TextMessage(GREETING_QUERY));
|
||||
|
||||
StepVerifier.create(this.session.getOutput())
|
||||
.consumeNextWith((message) -> assertMessageType(message, "connection_ack"))
|
||||
.consumeNextWith((message) -> assertThat(decode(message))
|
||||
.hasSize(3)
|
||||
.containsEntry("id", SUBSCRIPTION_ID)
|
||||
.containsEntry("type", "next")
|
||||
.extractingByKey("payload", as(InstanceOfAssertFactories.map(String.class, Object.class)))
|
||||
.extractingByKey("data", as(InstanceOfAssertFactories.map(String.class, Object.class)))
|
||||
.containsEntry("greeting", "a"))
|
||||
.consumeNextWith((message) -> assertThat(decode(message))
|
||||
.hasSize(3)
|
||||
.containsEntry("id", SUBSCRIPTION_ID)
|
||||
.containsEntry("type", "error")
|
||||
.hasEntrySatisfying("payload", payload -> assertThat(payload)
|
||||
.asList()
|
||||
.hasSize(1)
|
||||
.allSatisfy(theError -> assertThat(theError)
|
||||
.asInstanceOf(InstanceOfAssertFactories.map(String.class, Object.class))
|
||||
.hasSize(3)
|
||||
.hasEntrySatisfying("locations", loc -> assertThat(loc).asList().isEmpty())
|
||||
.hasEntrySatisfying("message", msg -> assertThat(msg).asString().contains("null"))
|
||||
.extractingByKey("extensions", as(InstanceOfAssertFactories.map(String.class, Object.class)))
|
||||
.containsEntry("classification", "DataFetchingException"))))
|
||||
.then(this.session::close)
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
private void handle(GraphQlWebSocketHandler handler, TextMessage... textMessages) throws Exception {
|
||||
handler.afterConnectionEstablished(this.session);
|
||||
for (TextMessage message : textMessages) {
|
||||
|
||||
Reference in New Issue
Block a user