|
1 | 1 | /*
|
2 |
| - * Copyright 2002-2024 the original author or authors. |
| 2 | + * Copyright 2002-2025 the original author or authors. |
3 | 3 | *
|
4 | 4 | * Licensed under the Apache License, Version 2.0 (the "License");
|
5 | 5 | * you may not use this file except in compliance with the License.
|
|
19 | 19 | import java.time.Duration;
|
20 | 20 | import java.util.Collections;
|
21 | 21 | import java.util.List;
|
| 22 | +import java.util.Map; |
22 | 23 | import java.util.concurrent.CompletableFuture;
|
| 24 | +import java.util.concurrent.atomic.AtomicBoolean; |
23 | 25 | import java.util.function.BiConsumer;
|
24 | 26 |
|
25 | 27 | import graphql.ExecutionInput;
|
|
41 | 43 | import org.junit.jupiter.api.Test;
|
42 | 44 | import reactor.core.publisher.Flux;
|
43 | 45 | import reactor.core.publisher.Mono;
|
| 46 | +import reactor.core.publisher.Sinks; |
44 | 47 | import reactor.test.StepVerifier;
|
45 | 48 |
|
| 49 | +import org.springframework.graphql.ExecutionGraphQlRequest; |
46 | 50 | import org.springframework.graphql.GraphQlSetup;
|
47 | 51 | import org.springframework.graphql.ResponseHelper;
|
48 | 52 | import org.springframework.graphql.TestThreadLocalAccessor;
|
49 | 53 |
|
50 | 54 | import static org.assertj.core.api.Assertions.assertThat;
|
| 55 | +import static org.awaitility.Awaitility.await; |
51 | 56 |
|
52 | 57 | /**
|
53 | 58 | * Tests for {@link ContextDataFetcherDecorator}.
|
@@ -257,4 +262,66 @@ void trivialDataFetcherIsNotDecorated() {
|
257 | 262 | assertThat(dataFetcher).isInstanceOf(TrivialDataFetcher.class);
|
258 | 263 | }
|
259 | 264 |
|
| 265 | + @Test |
| 266 | + void cancelMonoDataFetcherWhenRequestCancelled() throws Exception { |
| 267 | + AtomicBoolean dataFetcherCancelled = new AtomicBoolean(); |
| 268 | + GraphQL graphQl = GraphQlSetup.schemaContent(SCHEMA_CONTENT) |
| 269 | + .queryFetcher("greeting", (env) -> |
| 270 | + Mono.just("Hello") |
| 271 | + .delayElement(Duration.ofSeconds(1)) |
| 272 | + .doOnCancel(() -> dataFetcherCancelled.set(true)) |
| 273 | + ) |
| 274 | + .toGraphQl(); |
| 275 | + |
| 276 | + Sinks.Empty<Void> requestCancelled = Sinks.empty(); |
| 277 | + ExecutionInput input = ExecutionInput.newExecutionInput().query("{ greeting }") |
| 278 | + .graphQLContext(Map.of(ExecutionGraphQlRequest.CANCEL_PUBLISHER_CONTEXT_KEY, requestCancelled.asMono())).build(); |
| 279 | + |
| 280 | + CompletableFuture<ExecutionResult> asyncResult = graphQl.executeAsync(input); |
| 281 | + requestCancelled.tryEmitEmpty(); |
| 282 | + await().atMost(Duration.ofSeconds(2)).until(dataFetcherCancelled::get); |
| 283 | + } |
| 284 | + |
| 285 | + @Test |
| 286 | + void cancelFluxDataFetcherWhenRequestCancelled() throws Exception { |
| 287 | + AtomicBoolean dataFetcherCancelled = new AtomicBoolean(); |
| 288 | + GraphQL graphQl = GraphQlSetup.schemaContent(SCHEMA_CONTENT) |
| 289 | + .queryFetcher("greeting", (env) -> |
| 290 | + Flux.just("Hello") |
| 291 | + .delayElements(Duration.ofSeconds(1)) |
| 292 | + .doOnCancel(() -> dataFetcherCancelled.set(true)) |
| 293 | + ) |
| 294 | + .toGraphQl(); |
| 295 | + |
| 296 | + Sinks.Empty<Void> requestCancelled = Sinks.empty(); |
| 297 | + ExecutionInput input = ExecutionInput.newExecutionInput().query("{ greeting }") |
| 298 | + .graphQLContext(Map.of(ExecutionGraphQlRequest.CANCEL_PUBLISHER_CONTEXT_KEY, requestCancelled.asMono())).build(); |
| 299 | + |
| 300 | + CompletableFuture<ExecutionResult> asyncResult = graphQl.executeAsync(input); |
| 301 | + requestCancelled.tryEmitEmpty(); |
| 302 | + await().atMost(Duration.ofSeconds(2)).until(dataFetcherCancelled::get); |
| 303 | + } |
| 304 | + |
| 305 | + @Test |
| 306 | + void cancelFluxDataFetcherSubscriptionWhenRequestCancelled() throws Exception { |
| 307 | + AtomicBoolean dataFetcherCancelled = new AtomicBoolean(); |
| 308 | + GraphQL graphQl = GraphQlSetup.schemaContent(SCHEMA_CONTENT) |
| 309 | + .subscriptionFetcher("greetings", (env) -> |
| 310 | + Flux.just("Hi", "Bonjour", "Hola") |
| 311 | + .delayElements(Duration.ofSeconds(1)) |
| 312 | + .doOnCancel(() -> dataFetcherCancelled.set(true)) |
| 313 | + ) |
| 314 | + .toGraphQl(); |
| 315 | + Sinks.Empty<Void> requestCancelled = Sinks.empty(); |
| 316 | + ExecutionInput input = ExecutionInput.newExecutionInput().query("subscription { greetings }") |
| 317 | + .graphQLContext(Map.of(ExecutionGraphQlRequest.CANCEL_PUBLISHER_CONTEXT_KEY, requestCancelled.asMono())).build(); |
| 318 | + |
| 319 | + ExecutionResult executionResult = graphQl.executeAsync(input).get(); |
| 320 | + ResponseHelper.forSubscription(executionResult).subscribe(); |
| 321 | + |
| 322 | + requestCancelled.tryEmitEmpty(); |
| 323 | + await().atMost(Duration.ofSeconds(2)).until(dataFetcherCancelled::get); |
| 324 | + assertThat(dataFetcherCancelled).isTrue(); |
| 325 | + } |
| 326 | + |
260 | 327 | }
|
0 commit comments