diff --git a/spring-graphql/src/main/java/org/springframework/graphql/client/AbstractGraphQlClientBuilder.java b/spring-graphql/src/main/java/org/springframework/graphql/client/AbstractGraphQlClientBuilder.java
index 9ecb158a..b1dc8ba9 100644
--- a/spring-graphql/src/main/java/org/springframework/graphql/client/AbstractGraphQlClientBuilder.java
+++ b/spring-graphql/src/main/java/org/springframework/graphql/client/AbstractGraphQlClientBuilder.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2022 the original author or authors.
+ * Copyright 2002-2024 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.
@@ -16,6 +16,7 @@
package org.springframework.graphql.client;
+import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -66,6 +67,9 @@ public abstract class AbstractGraphQlClientBuilder jsonDecoder;
+ @Nullable
+ private Duration blockingTimeout;
+
/**
* Default constructor for use from subclasses.
@@ -82,7 +86,6 @@ public abstract class AbstractGraphQlClientBuilder T self() {
return (T) this;
@@ -169,8 +178,8 @@ public abstract class AbstractGraphQlClientBuilder transport.execute(request).map(response ->
- new DefaultClientGraphQlResponse(request, response, getEncoder(), getDecoder()));
-
- return this.interceptors.stream()
- .reduce(GraphQlClientInterceptor::andThen)
- .map(interceptor -> (Chain) (request) -> interceptor.intercept(request, chain))
- .orElse(chain);
- }
-
- private SubscriptionChain createExecuteSubscriptionChain(GraphQlTransport transport) {
-
- SubscriptionChain chain = request -> transport.executeSubscription(request)
+ Chain chain = request -> transport
+ .execute(request)
.map(response -> new DefaultClientGraphQlResponse(request, response, getEncoder(), getDecoder()));
return this.interceptors.stream()
.reduce(GraphQlClientInterceptor::andThen)
- .map(interceptor -> (SubscriptionChain) (request) -> interceptor.interceptSubscription(request, chain))
+ .map(i -> (Chain) (request) -> i.intercept(request, chain))
+ .orElse(chain);
+ }
+
+ private SubscriptionChain createSubscriptionChain(GraphQlTransport transport) {
+
+ SubscriptionChain chain = request -> transport
+ .executeSubscription(request)
+ .map(response -> new DefaultClientGraphQlResponse(request, response, getEncoder(), getDecoder()));
+
+ return this.interceptors.stream()
+ .reduce(GraphQlClientInterceptor::andThen)
+ .map(i -> (SubscriptionChain) (request) -> i.interceptSubscription(request, chain))
.orElse(chain);
}
diff --git a/spring-graphql/src/main/java/org/springframework/graphql/client/AbstractGraphQlClientSyncBuilder.java b/spring-graphql/src/main/java/org/springframework/graphql/client/AbstractGraphQlClientSyncBuilder.java
new file mode 100644
index 00000000..ce5b3c2e
--- /dev/null
+++ b/spring-graphql/src/main/java/org/springframework/graphql/client/AbstractGraphQlClientSyncBuilder.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright 2002-2024 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.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.graphql.client;
+
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.function.Consumer;
+
+import reactor.core.scheduler.Scheduler;
+import reactor.core.scheduler.Schedulers;
+
+import org.springframework.core.codec.Decoder;
+import org.springframework.core.codec.Encoder;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.graphql.GraphQlResponse;
+import org.springframework.graphql.client.SyncGraphQlClientInterceptor.Chain;
+import org.springframework.graphql.support.CachingDocumentSource;
+import org.springframework.graphql.support.DocumentSource;
+import org.springframework.graphql.support.ResourceDocumentSource;
+import org.springframework.http.converter.HttpMessageConverter;
+import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
+import org.springframework.lang.Nullable;
+import org.springframework.util.Assert;
+import org.springframework.util.ClassUtils;
+
+
+/**
+ * Abstract, base class for transport specific {@link GraphQlClient.SyncBuilder}
+ * implementations.
+ *
+ *
Subclasses must implement {@link #build()} and call
+ * {@link #buildGraphQlClient(SyncGraphQlTransport)} to obtain a default, transport
+ * agnostic {@code GraphQlClient}. A transport specific extension can then wrap
+ * this default tester by extending {@link AbstractDelegatingGraphQlClient}.
+ *
+ * @author Rossen Stoyanchev
+ * @since 1.3
+ * @see AbstractDelegatingGraphQlClient
+ */
+public abstract class AbstractGraphQlClientSyncBuilder>
+ implements GraphQlClient.SyncBuilder {
+
+ protected static final boolean jackson2Present = ClassUtils.isPresent(
+ "com.fasterxml.jackson.databind.ObjectMapper", AbstractGraphQlClientSyncBuilder.class.getClassLoader());
+
+
+ private final List interceptors = new ArrayList<>();
+
+ private DocumentSource documentSource;
+
+ @Nullable
+ private HttpMessageConverter