Skip to content

Commit c1938fc

Browse files
committed
Merge branch '1.3.x'
2 parents 36bfd40 + 474fbca commit c1938fc

File tree

4 files changed

+67
-8
lines changed

4 files changed

+67
-8
lines changed

spring-graphql/src/main/java/org/springframework/graphql/client/WebSocketGraphQlTransport.java

+13-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2024 the original author or authors.
2+
* Copyright 2002-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -339,19 +339,29 @@ private void registerCloseStatusHandling(GraphQlSession graphQlSession, WebSocke
339339
if (logger.isDebugEnabled()) {
340340
logger.debug(closeStatusMessage);
341341
}
342-
graphQlSession.terminateRequests(closeStatusMessage, closeStatus);
342+
terminateGraphQlSession(graphQlSession, closeStatus, closeStatusMessage, null);
343343
})
344344
.doOnError((cause) -> {
345345
CloseStatus closeStatus = CloseStatus.NO_STATUS_CODE;
346346
String closeStatusMessage = initCloseStatusMessage(closeStatus, cause, graphQlSession);
347347
if (logger.isErrorEnabled()) {
348348
logger.error(closeStatusMessage);
349349
}
350-
graphQlSession.terminateRequests(closeStatusMessage, closeStatus);
350+
terminateGraphQlSession(graphQlSession, closeStatus, closeStatusMessage, cause);
351351
})
352352
.subscribe();
353353
}
354354

355+
private void terminateGraphQlSession(
356+
GraphQlSession session, CloseStatus closeStatus, String closeStatusMessage, @Nullable Throwable cause) {
357+
358+
if (sessionNotInitialized()) {
359+
this.graphQlSessionSink.tryEmitError(new IllegalStateException(closeStatusMessage, cause));
360+
this.graphQlSessionSink = Sinks.unsafe().one();
361+
}
362+
session.terminateRequests(closeStatusMessage, closeStatus);
363+
}
364+
355365
private String initCloseStatusMessage(CloseStatus status, @Nullable Throwable ex, GraphQlSession session) {
356366
String reason = session + " disconnected";
357367
if (isStopped()) {

spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/AnnotatedControllerExceptionResolver.java

+10-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2024 the original author or authors.
2+
* Copyright 2002-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -356,7 +356,16 @@ Method getMethod() {
356356
return this.method;
357357
}
358358

359+
@SuppressWarnings("unchecked")
359360
Mono<List<GraphQLError>> adapt(@Nullable Object result, Throwable ex) {
361+
if (result instanceof Mono<?> errorMono && this.adapter != ReturnValueAdapter.forMono) {
362+
return (Mono<List<GraphQLError>>) errorMono.onErrorMap((ex2) -> {
363+
if (logger.isWarnEnabled()) {
364+
logger.warn("Failure in @GraphQlExceptionHandler " + this.method, ex2);
365+
}
366+
return ex; // fall back to original exception
367+
});
368+
}
360369
return this.adapter.adapt(result, this.returnType, ex);
361370
}
362371

spring-graphql/src/test/java/org/springframework/graphql/client/WebSocketGraphQlTransportTests.java

+20-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2024 the original author or authors.
2+
* Copyright 2002-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -317,17 +317,34 @@ void errorOnConnect() {
317317
}
318318

319319
@Test
320-
void errorBeforeConnectionAck() {
320+
void errorBeforeConnectionAckWithStart() {
321321

322322
// Errors before GraphQL session initialized should be routed, no hanging on start
323323

324324
MockGraphQlWebSocketServer handler = new MockGraphQlWebSocketServer();
325325
handler.connectionInitHandler(initPayload -> Mono.error(new IllegalStateException("boo")));
326326

327327
TestWebSocketClient client = new TestWebSocketClient(handler);
328+
String expectedMessage = "disconnected with CloseStatus[code=1002, reason=null]";
328329

329330
StepVerifier.create(createTransport(client).start())
330-
.expectErrorMessage("boo")
331+
.expectErrorSatisfies(ex -> assertThat(ex).hasMessageEndingWith(expectedMessage))
332+
.verify(TIMEOUT);
333+
}
334+
335+
@Test // gh-1098
336+
void errorBeforeConnectionAckWithRequest() {
337+
338+
// Errors before GraphQL session initialized should be routed, no hanging on start
339+
340+
MockGraphQlWebSocketServer handler = new MockGraphQlWebSocketServer();
341+
handler.connectionInitHandler(initPayload -> Mono.error(new IllegalStateException("boo")));
342+
343+
TestWebSocketClient client = new TestWebSocketClient(session -> session.close(CloseStatus.POLICY_VIOLATION));
344+
String expectedMessage = "disconnected with CloseStatus[code=1008, reason=null]";
345+
346+
StepVerifier.create(createTransport(client).execute(new DefaultGraphQlRequest("{Query1}")))
347+
.expectErrorSatisfies(ex -> assertThat(ex).hasMessageEndingWith(expectedMessage))
331348
.verify(TIMEOUT);
332349
}
333350

spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/AnnotatedControllerExceptionResolverTests.java

+24-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2024 the original author or authors.
2+
* Copyright 2002-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -111,6 +111,19 @@ void leaveUnresolvedViaNullReturnValue() {
111111
StepVerifier.create(resolver.resolveException(ex, this.environment, controller)).verifyComplete();
112112
}
113113

114+
@Test // gh-1090
115+
void failureFromResolver() {
116+
ExceptionThrowingController controller = new ExceptionThrowingController();
117+
118+
Exception ex = new IllegalArgumentException("Bad input");
119+
AnnotatedControllerExceptionResolver resolver = exceptionResolver();
120+
resolver.registerController(controller.getClass());
121+
122+
StepVerifier.create(resolver.resolveException(ex, this.environment, controller))
123+
.expectErrorSatisfies(actual -> assertThat(actual).isSameAs(ex))
124+
.verify();
125+
}
126+
114127
@Test
115128
void resolveWithControllerAdvice() {
116129
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
@@ -304,4 +317,14 @@ public String handle(IllegalArgumentException ex) {
304317

305318
}
306319

320+
321+
private static class ExceptionThrowingController {
322+
323+
@GraphQlExceptionHandler
324+
GraphQLError handle(IllegalArgumentException ex) {
325+
throw new IllegalStateException("failure in exception handler");
326+
}
327+
328+
}
329+
307330
}

0 commit comments

Comments
 (0)