From 1cd6e038d40fa2fb6f386d7952f3dea947f345ac Mon Sep 17 00:00:00 2001
From: Dariusz Kuc <9501705+dariuszkuc@users.noreply.github.com>
Date: Fri, 17 May 2024 00:30:55 -0500
Subject: [PATCH] fix: enable spring subscription callback module using RC
version (#397)
---
gradle.properties | 4 +--
.../caching/CacheControlInstrumentation.java | 6 ++--
settings.gradle.kts | 6 ++--
spring-subscription-callback/build.gradle.kts | 8 ++++-
.../webflux/CallbackGraphQlHttpHandler.java | 1 +
.../webmvc/CallbackGraphQlHttpHandler.java | 30 +---------------
.../SubscriptionCallbackHandlerTest.java | 34 ++++++++++---------
7 files changed, 34 insertions(+), 55 deletions(-)
diff --git a/gradle.properties b/gradle.properties
index e85bc033..95d5009e 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -7,8 +7,8 @@ graphQLJavaVersion = 22.0
mockWebServerVersion = 4.12.0
protobufVersion = 4.26.1
slf4jVersion = 2.0.13
-springBootVersion = 3.2.5
-springGraphQLVersion = 1.2.6
+springBootVersion = 3.3.0-RC1
+springGraphQLVersion = 1.3.0-RC1
reactorVersion = 3.6.6
# test dependencies
diff --git a/graphql-java-support/src/main/java/com/apollographql/federation/graphqljava/caching/CacheControlInstrumentation.java b/graphql-java-support/src/main/java/com/apollographql/federation/graphqljava/caching/CacheControlInstrumentation.java
index fa803f3d..e3559c1d 100644
--- a/graphql-java-support/src/main/java/com/apollographql/federation/graphqljava/caching/CacheControlInstrumentation.java
+++ b/graphql-java-support/src/main/java/com/apollographql/federation/graphqljava/caching/CacheControlInstrumentation.java
@@ -18,9 +18,9 @@
* A GraphQL Java Instrumentation that computes a max age for an operation based on @cacheControl
* directives.
*
- * By default, this instrumentation will only set the `Cache-Control` `max-age` value IF positive int
- * value is provided. If you would rather want to return explicit `max-age=0` values, you need to
- * explicitly opt-in to this behavior by specifying `allowZeroMaxAge=true` constructor value.
+ *
By default, this instrumentation will only set the `Cache-Control` `max-age` value IF positive
+ * int value is provided. If you would rather want to return explicit `max-age=0` values, you need
+ * to explicitly opt-in to this behavior by specifying `allowZeroMaxAge=true` constructor value.
*
*
You can retrieve the "max-age=..." header value with a {@link GraphQLContext}:
* String cacheControlHeader = CacheControlInstrumentation.cacheControlContext(context);
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 040a2883..989efd1b 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -1,9 +1,7 @@
rootProject.name = "federation-jvm"
include(":federation-graphql-java-support")
-// TODO: disabling spring-subscription-callback module until Spring Boot 3.3 is released
-//include(":federation-spring-subscription-callback")
+include(":federation-spring-subscription-callback")
project(":federation-graphql-java-support").projectDir = file("graphql-java-support")
-// TODO: disabling spring-subscription-callback module until Spring Boot 3.3 is released
-//project(":federation-spring-subscription-callback").projectDir = file("spring-subscription-callback")
+project(":federation-spring-subscription-callback").projectDir = file("spring-subscription-callback")
diff --git a/spring-subscription-callback/build.gradle.kts b/spring-subscription-callback/build.gradle.kts
index 78971fca..4576e3ee 100644
--- a/spring-subscription-callback/build.gradle.kts
+++ b/spring-subscription-callback/build.gradle.kts
@@ -4,6 +4,13 @@ plugins {
id("com.apollographql.federation.java-conventions")
}
+repositories {
+ mavenCentral()
+ maven {
+ url = uri("https://repo.spring.io/milestone")
+ }
+}
+
val annotationsVersion: String by project
val graphQLJavaVersion: String by project
val mockWebServerVersion: String by project
@@ -27,7 +34,6 @@ dependencies {
testImplementation("org.springframework.boot", "spring-boot-starter-websocket", springBootVersion)
testImplementation("org.springframework.graphql", "spring-graphql-test", springGraphQLVersion)
testImplementation("io.projectreactor", "reactor-test", reactorVersion)
-
}
java {
diff --git a/spring-subscription-callback/src/main/java/com/apollographql/subscription/webflux/CallbackGraphQlHttpHandler.java b/spring-subscription-callback/src/main/java/com/apollographql/subscription/webflux/CallbackGraphQlHttpHandler.java
index 4d538ce0..579dba23 100644
--- a/spring-subscription-callback/src/main/java/com/apollographql/subscription/webflux/CallbackGraphQlHttpHandler.java
+++ b/spring-subscription-callback/src/main/java/com/apollographql/subscription/webflux/CallbackGraphQlHttpHandler.java
@@ -69,6 +69,7 @@ public Mono handleRequest(@NotNull ServerRequest serverRequest)
serverRequest.uri(),
serverRequest.headers().asHttpHeaders(),
serverRequest.cookies(),
+ serverRequest.remoteAddress().orElse(null),
serverRequest.attributes(),
body,
serverRequest.exchange().getRequest().getId(),
diff --git a/spring-subscription-callback/src/main/java/com/apollographql/subscription/webmvc/CallbackGraphQlHttpHandler.java b/spring-subscription-callback/src/main/java/com/apollographql/subscription/webmvc/CallbackGraphQlHttpHandler.java
index a7942dad..3f6132c5 100644
--- a/spring-subscription-callback/src/main/java/com/apollographql/subscription/webmvc/CallbackGraphQlHttpHandler.java
+++ b/spring-subscription-callback/src/main/java/com/apollographql/subscription/webmvc/CallbackGraphQlHttpHandler.java
@@ -7,8 +7,6 @@
import com.apollographql.subscription.callback.SubscriptionCallbackHandler;
import graphql.ExecutionResult;
import jakarta.servlet.ServletException;
-import jakarta.servlet.http.Cookie;
-import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
@@ -20,13 +18,9 @@
import org.springframework.graphql.server.WebGraphQlHandler;
import org.springframework.graphql.server.WebGraphQlRequest;
import org.springframework.graphql.server.webmvc.GraphQlHttpHandler;
-import org.springframework.http.HttpCookie;
import org.springframework.http.MediaType;
import org.springframework.util.AlternativeJdkIdGenerator;
import org.springframework.util.IdGenerator;
-import org.springframework.util.LinkedMultiValueMap;
-import org.springframework.util.MultiValueMap;
-import org.springframework.web.server.ServerWebInputException;
import org.springframework.web.servlet.function.ServerRequest;
import org.springframework.web.servlet.function.ServerResponse;
import reactor.core.publisher.Mono;
@@ -79,6 +73,7 @@ public CallbackGraphQlHttpHandler(
serverRequest.uri(),
serverRequest.headers().asHttpHeaders(),
initCookies(serverRequest),
+ serverRequest.remoteAddress().orElse(null),
serverRequest.attributes(),
readBody(serverRequest),
this.idGenerator.generateId().toString(),
@@ -145,29 +140,6 @@ public CallbackGraphQlHttpHandler(
}
}
- private static MultiValueMap initCookies(ServerRequest serverRequest) {
- MultiValueMap source = serverRequest.cookies();
- MultiValueMap target = new LinkedMultiValueMap<>(source.size());
- source
- .values()
- .forEach(
- cookieList ->
- cookieList.forEach(
- cookie -> {
- HttpCookie httpCookie = new HttpCookie(cookie.getName(), cookie.getValue());
- target.add(cookie.getName(), httpCookie);
- }));
- return target;
- }
-
- private static Map readBody(ServerRequest request) throws ServletException {
- try {
- return request.body(MAP_PARAMETERIZED_TYPE_REF);
- } catch (IOException ex) {
- throw new ServerWebInputException("I/O error while reading request body", null, ex);
- }
- }
-
private static MediaType selectResponseMediaType(ServerRequest serverRequest) {
for (MediaType accepted : serverRequest.headers().accept()) {
if (SUPPORTED_MEDIA_TYPES.contains(accepted)) {
diff --git a/spring-subscription-callback/src/test/java/com/apollographql/subscription/callback/SubscriptionCallbackHandlerTest.java b/spring-subscription-callback/src/test/java/com/apollographql/subscription/callback/SubscriptionCallbackHandlerTest.java
index 08754d0a..1fb2a512 100644
--- a/spring-subscription-callback/src/test/java/com/apollographql/subscription/callback/SubscriptionCallbackHandlerTest.java
+++ b/spring-subscription-callback/src/test/java/com/apollographql/subscription/callback/SubscriptionCallbackHandlerTest.java
@@ -25,10 +25,12 @@
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
+import org.springframework.graphql.ExecutionGraphQlRequest;
+import org.springframework.graphql.ExecutionGraphQlResponse;
+import org.springframework.graphql.ExecutionGraphQlService;
import org.springframework.graphql.server.WebGraphQlHandler;
import org.springframework.graphql.server.WebGraphQlRequest;
import org.springframework.graphql.server.WebGraphQlResponse;
-import org.springframework.graphql.server.WebSocketGraphQlInterceptor;
import org.springframework.graphql.support.DefaultExecutionGraphQlResponse;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
@@ -39,21 +41,16 @@
public class SubscriptionCallbackHandlerTest {
- static class MockWebHandler implements WebGraphQlHandler {
+ static class MockExecutionEngine implements ExecutionGraphQlService {
private final Flux subscriptionFlux;
- public MockWebHandler(Flux subscriptionFlux) {
+ public MockExecutionEngine(Flux subscriptionFlux) {
this.subscriptionFlux = subscriptionFlux;
}
@Override
- public WebSocketGraphQlInterceptor getWebSocketInterceptor() {
- return null;
- }
-
- @Override
- public @NotNull Mono handleRequest(@NotNull WebGraphQlRequest request) {
+ public Mono execute(ExecutionGraphQlRequest request) {
var executionResult = ExecutionResult.newExecutionResult().data(subscriptionFlux).build();
var executionResponse =
new DefaultExecutionGraphQlResponse(request.toExecutionInput(), executionResult);
@@ -62,6 +59,10 @@ public WebSocketGraphQlInterceptor getWebSocketInterceptor() {
}
}
+ static WebGraphQlHandler mockHandler(Flux subscriptionFlux) {
+ return WebGraphQlHandler.builder(new MockExecutionEngine(subscriptionFlux)).build();
+ }
+
@Test
public void init_successful() {
var capturedRequests = new ArrayList();
@@ -81,7 +82,7 @@ public MockResponse dispatch(@NotNull RecordedRequest recordedRequest) {
var data =
Flux.just(1, 2)
.map((i) -> ExecutionResult.newExecutionResult().data(Map.of("counter", i)).build());
- var handler = new SubscriptionCallbackHandler(new MockWebHandler(data));
+ var handler = new SubscriptionCallbackHandler(mockHandler(data));
var subscriptionId = UUID.randomUUID().toString();
var callbackUrl = server.url("/callback/" + subscriptionId).toString();
@@ -132,7 +133,7 @@ public MockResponse dispatch(@NotNull RecordedRequest recordedRequest) {
var data =
Flux.just(1, 2)
.map((i) -> ExecutionResult.newExecutionResult().data(Map.of("counter", i)).build());
- var handler = new SubscriptionCallbackHandler(new MockWebHandler(data));
+ var handler = new SubscriptionCallbackHandler(mockHandler(data));
var subscriptionId = UUID.randomUUID().toString();
var callbackUrl = server.url("/callback/" + subscriptionId).toString();
@@ -184,7 +185,7 @@ public MockResponse dispatch(@NotNull RecordedRequest recordedRequest) {
Flux.just(1, 2)
.delayElements(Duration.ofMillis(3000))
.map((i) -> ExecutionResult.newExecutionResult().data(Map.of("counter", i)).build());
- var handler = new SubscriptionCallbackHandler(new MockWebHandler(data));
+ var handler = new SubscriptionCallbackHandler(mockHandler(data));
// note: heartbeat goes into infinite recursion and does not emit value
// TODO update to use virtual timer
@@ -249,7 +250,7 @@ public MockResponse dispatch(@NotNull RecordedRequest recordedRequest) {
Flux.just(1, 2)
.delayElements(Duration.ofMillis(3000))
.map((i) -> ExecutionResult.newExecutionResult().data(Map.of("counter", i)).build());
- var handler = new SubscriptionCallbackHandler(new MockWebHandler(data));
+ var handler = new SubscriptionCallbackHandler(mockHandler(data));
// note: heartbeat goes into infinite recursion and does not emit value
// TODO update to use virtual timer
@@ -280,7 +281,7 @@ public void subscription_success() {
Flux.just(1, 2)
.delayElements(Duration.ofMillis(50))
.map((i) -> ExecutionResult.newExecutionResult().data(Map.of("counter", i)).build());
- var handler = new SubscriptionCallbackHandler(new MockWebHandler(data));
+ var handler = new SubscriptionCallbackHandler(mockHandler(data));
var subscriptionId = UUID.randomUUID().toString();
var callbackUrl = server.url("/callback/" + subscriptionId).toString();
@@ -309,7 +310,7 @@ public void subscription_success_without_heartbeats() {
Flux.just(1, 2)
.delayElements(Duration.ofMillis(50))
.map((i) -> ExecutionResult.newExecutionResult().data(Map.of("counter", i)).build());
- var handler = new SubscriptionCallbackHandler(new MockWebHandler(data));
+ var handler = new SubscriptionCallbackHandler(mockHandler(data));
var subscriptionId = UUID.randomUUID().toString();
var callbackUrl = server.url("/callback/" + subscriptionId).toString();
@@ -339,7 +340,7 @@ public void subscription_exception() {
.delayElements(Duration.ofMillis(50))
.map((i) -> ExecutionResult.newExecutionResult().data(Map.of("counter", i)).build())
.concatWith(Mono.error(new RuntimeException("JUNIT_FAILURE")));
- var handler = new SubscriptionCallbackHandler(new MockWebHandler(data));
+ var handler = new SubscriptionCallbackHandler(mockHandler(data));
var subscriptionId = UUID.randomUUID().toString();
var callbackUrl = server.url("/callback/" + subscriptionId).toString();
@@ -385,6 +386,7 @@ private WebGraphQlRequest stubWebGraphQLRequest(String subscriptionId, String ca
URI.create(callbackUrl),
HttpHeaders.EMPTY,
null,
+ null,
Collections.emptyMap(),
createMockGraphQLRequest(subscriptionId, callbackUrl),
UUID.randomUUID().toString(),