Convert websocket error message payload to array

As expected in graphql-ws protocol

See gh-200
This commit is contained in:
Jordie
2021-11-27 15:00:19 +01:00
committed by rstoyanchev
parent 6d37d9bbc7
commit 175673d291
4 changed files with 118 additions and 2 deletions

View File

@@ -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)));
});
}

View File

@@ -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)));
});
}

View File

@@ -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),

View File

@@ -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) {