From 518617dd34b2636f085c2bf0f0c5a290ecb38636 Mon Sep 17 00:00:00 2001 From: rstoyanchev Date: Fri, 16 May 2025 08:48:13 +0100 Subject: [PATCH] Lenient adapt non-Publisher subscription result Closes gh-1213 --- .../ReactiveAdapterRegistryHelper.java | 9 ++++---- .../webflux/GraphQlSseHandlerTests.java | 22 ++++++++++++++++++- .../server/webmvc/GraphQlSseHandlerTests.java | 21 +++++++++++++++++- 3 files changed, 45 insertions(+), 7 deletions(-) diff --git a/spring-graphql/src/main/java/org/springframework/graphql/execution/ReactiveAdapterRegistryHelper.java b/spring-graphql/src/main/java/org/springframework/graphql/execution/ReactiveAdapterRegistryHelper.java index c3efb3c2..4368f566 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/execution/ReactiveAdapterRegistryHelper.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/execution/ReactiveAdapterRegistryHelper.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2024 the original author or authors. + * Copyright 2002-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,7 +25,6 @@ import reactor.core.publisher.Mono; import org.springframework.core.ReactiveAdapter; import org.springframework.core.ReactiveAdapterRegistry; -import org.springframework.util.Assert; /** * Helper to adapt a result Object to {@link Mono} or {@link Flux} through @@ -90,7 +89,8 @@ public abstract class ReactiveAdapterRegistryHelper { /** * Return a {@link Flux} for the given result Object, adapting to a - * {@link Publisher} first if necessary via {@link ReactiveAdapterRegistry}. + * {@link Publisher} via {@link ReactiveAdapterRegistry} or wrapping it as + * {@code Flux} if necessary. * @param result the result Object to adapt * @return a {@link Flux}, possibly empty if the result is {@code null} */ @@ -102,8 +102,7 @@ public abstract class ReactiveAdapterRegistryHelper { return Flux.from(publisher); } ReactiveAdapter adapter = registry.getAdapter(result.getClass()); - Assert.state(adapter != null, "Expected Publisher for a subscription"); - return Flux.from(adapter.toPublisher(result)); + return ((adapter != null) ? Flux.from(adapter.toPublisher(result)) : Flux.just(result)); } /** diff --git a/spring-graphql/src/test/java/org/springframework/graphql/server/webflux/GraphQlSseHandlerTests.java b/spring-graphql/src/test/java/org/springframework/graphql/server/webflux/GraphQlSseHandlerTests.java index 60cb2394..5cf95e6c 100644 --- a/spring-graphql/src/test/java/org/springframework/graphql/server/webflux/GraphQlSseHandlerTests.java +++ b/spring-graphql/src/test/java/org/springframework/graphql/server/webflux/GraphQlSseHandlerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2024 the original author or authors. + * Copyright 2020-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -104,6 +104,26 @@ class GraphQlSseHandlerTests { """); } + @Test // gh-1213 + void shouldHandleNonPublisherValue() { + + SerializableGraphQlRequest request = initRequest( + "subscription TestSubscription { bookSearch(author:\"Orwell\") { id name } }"); + + GraphQlSseHandler handler = createHandler(env -> BookSource.getBook(1L)); + MockServerHttpResponse response = handleRequest(this.httpRequest, handler, request); + + assertThat(response.getHeaders().getContentType().isCompatibleWith(MediaType.TEXT_EVENT_STREAM)).isTrue(); + assertThat(response.getBodyAsString().block()).isEqualTo(""" + event:next + data:{"data":{"bookSearch":{"id":"1","name":"Nineteen Eighty-Four"}}} + + event:complete + data:{} + + """); + } + @Test void shouldWriteEventsAndTerminalError() { diff --git a/spring-graphql/src/test/java/org/springframework/graphql/server/webmvc/GraphQlSseHandlerTests.java b/spring-graphql/src/test/java/org/springframework/graphql/server/webmvc/GraphQlSseHandlerTests.java index ecaf18dd..247df4ce 100644 --- a/spring-graphql/src/test/java/org/springframework/graphql/server/webmvc/GraphQlSseHandlerTests.java +++ b/spring-graphql/src/test/java/org/springframework/graphql/server/webmvc/GraphQlSseHandlerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2024 the original author or authors. + * Copyright 2020-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -111,6 +111,25 @@ class GraphQlSseHandlerTests { """); } + @Test // gh-1213 + void shouldHandleNonPublisherValue() throws Exception { + GraphQlSseHandler handler = createSseHandler(env -> BookSource.getBook(1L)); + MockHttpServletRequest request = createServletRequest(""" + { "query": "subscription TestSubscription { bookSearch { id name } }" } + """); + MockHttpServletResponse response = handleAndAwait(request, handler); + + assertThat(response.getContentType()).isEqualTo(MediaType.TEXT_EVENT_STREAM_VALUE); + assertThat(response.getContentAsString()).isEqualTo(""" + event:next + data:{"data":{"bookSearch":{"id":"1","name":"Nineteen Eighty-Four"}}} + + event:complete + data: + + """); + } + @Test void shouldWriteEventsAndTerminalError() throws Exception {