diff --git a/spring-graphql/src/main/java/org/springframework/graphql/RequestInput.java b/spring-graphql/src/main/java/org/springframework/graphql/RequestInput.java
index 29d8f159..dbc47b50 100644
--- a/spring-graphql/src/main/java/org/springframework/graphql/RequestInput.java
+++ b/spring-graphql/src/main/java/org/springframework/graphql/RequestInput.java
@@ -32,10 +32,10 @@ import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
/**
- * Common representation for GraphQL request input. This can be converted to
- * {@link ExecutionInput} via {@link #toExecutionInput(boolean)} and the
- * {@code ExecutionInput} further customized via
- * {@link #configureExecutionInput(BiFunction)}.
+ * Common, server-side representation of GraphQL request input independent of
+ * the underlying transport. This can be converted to {@link ExecutionInput}
+ * via {@link #toExecutionInput()} while the resulting {@code ExecutionInput}
+ * can be customized via {@link #configureExecutionInput(BiFunction)} callbacks.
*
* @author Rossen Stoyanchev
* @author Brian Clozel
@@ -60,6 +60,7 @@ public class RequestInput {
@Nullable
private ExecutionId executionId;
+
/**
* Create an instance.
* @param query the query, mutation, or subscription for the request
@@ -83,17 +84,37 @@ public class RequestInput {
/**
- * Return an identifier for the request. This id can be later propagated
- * as the {@link ExecutionId} if {@link #executionId(ExecutionId) none has been set}.
- *
For web transports, this identifier can be used to correlate
- * request and response messages on a multiplexed connection.
- * @return the request id.
+ * Return the id for the request selected by the transport handler.
+ *
+ * - For Spring MVC, the id is generated via
+ * {@link org.springframework.util.AlternativeJdkIdGenerator}, which is more
+ * efficient than {@code UUID.randomUUID()}.
+ *
- For WebFlux the id is from the {@code ServerHttpRequest}, which is
+ * useful to correlate to WebFlux log messages.
+ *
- For WebSocket, the id is from the {@code Subscribe} message of the
+ * GraphQL over WebSocket protocol, which is useful to correlate to
+ * WebSocket messages.
+ *
+ * By default, the transport id becomes the
+ * {@link ExecutionInput.Builder#executionId(ExecutionId) executionId} for
+ * the GraphQL request. You can override this via
+ * {@link #executionId(ExecutionId)} or by configuring an
+ * {@link graphql.execution.ExecutionIdProvider} on {@link graphql.GraphQL}.
+ * @return the request id
* @see GraphQL over WebSocket Protocol
*/
public String getId() {
return this.id;
}
+ /**
+ * Return the {@code executionId} configured via {@link #executionId(ExecutionId)}.
+ */
+ @Nullable
+ public ExecutionId getExecutionId() {
+ return this.executionId;
+ }
+
/**
* Return the query, mutation, or subscription for the request.
* @return the query, a non-empty string.
@@ -129,8 +150,10 @@ public class RequestInput {
}
/**
- * Set an {@link ExecutionId} to be used for the {@link ExecutionInput}
- * @param executionId the execution id to use with the {@link ExecutionInput}.
+ * Configure the {@link ExecutionId} to use for the GraphQL request, which
+ * is set on the {@link ExecutionInput}. This option overrides the
+ * {@link #getId() id} selected by the transport handler.
+ * @param executionId the execution id to set on the {@link ExecutionInput}.
*/
public void executionId(ExecutionId executionId) {
Assert.notNull(executionId, "executionId should not be null");
@@ -154,26 +177,21 @@ public class RequestInput {
* populated from {@link #getQuery()}, {@link #getOperationName()}, and
* {@link #getVariables()}, and is then further customized through
* {@link #configureExecutionInput(BiFunction)}.
- * @param useRequestId whether the {@link #getId()} should be used as a fallback for {@link ExecutionId}.
* @return the execution input
*/
- public ExecutionInput toExecutionInput(boolean useRequestId) {
+ public ExecutionInput toExecutionInput() {
ExecutionInput.Builder inputBuilder = ExecutionInput.newExecutionInput()
.query(this.query)
.operationName(this.operationName)
.variables(this.variables)
- .locale(this.locale);
- if (this.executionId != null) {
- inputBuilder.executionId(this.executionId);
- }
- else if (useRequestId) {
- inputBuilder.executionId(ExecutionId.from(this.id));
- }
+ .locale(this.locale)
+ .executionId(this.executionId != null ? this.executionId : ExecutionId.from(this.id));
+
ExecutionInput executionInput = inputBuilder.build();
for (BiFunction configurer : this.executionInputConfigurers) {
ExecutionInput current = executionInput;
- executionInput = executionInput.transform((builder) -> configurer.apply(current, builder));
+ executionInput = executionInput.transform(builder -> configurer.apply(current, builder));
}
return executionInput;
diff --git a/spring-graphql/src/main/java/org/springframework/graphql/execution/ExecutionGraphQlService.java b/spring-graphql/src/main/java/org/springframework/graphql/execution/ExecutionGraphQlService.java
index 04ac7d30..fd6afdaf 100644
--- a/spring-graphql/src/main/java/org/springframework/graphql/execution/ExecutionGraphQlService.java
+++ b/spring-graphql/src/main/java/org/springframework/graphql/execution/ExecutionGraphQlService.java
@@ -18,6 +18,7 @@ package org.springframework.graphql.execution;
import java.util.ArrayList;
import java.util.List;
+import java.util.function.BiFunction;
import graphql.ExecutionInput;
import graphql.GraphQL;
@@ -39,16 +40,21 @@ import org.springframework.graphql.RequestOutput;
*/
public class ExecutionGraphQlService implements GraphQlService {
+ private static final BiFunction RESET_EXECUTION_ID_CONFIGURER =
+ (executionInput, builder) -> builder.executionId(null).build();
+
+
private final GraphQlSource graphQlSource;
private final List dataLoaderRegistrars = new ArrayList<>();
- private final boolean hasDefaultExecutionIdProvider;
+ private final boolean isDefaultExecutionIdProvider;
public ExecutionGraphQlService(GraphQlSource graphQlSource) {
this.graphQlSource = graphQlSource;
- this.hasDefaultExecutionIdProvider = ExecutionIdProvider.DEFAULT_EXECUTION_ID_PROVIDER == graphQlSource.graphQl().getIdProvider();
+ this.isDefaultExecutionIdProvider =
+ (graphQlSource.graphQl().getIdProvider() == ExecutionIdProvider.DEFAULT_EXECUTION_ID_PROVIDER);
}
@@ -65,7 +71,10 @@ public class ExecutionGraphQlService implements GraphQlService {
@Override
public final Mono execute(RequestInput requestInput) {
return Mono.deferContextual((contextView) -> {
- ExecutionInput executionInput = requestInput.toExecutionInput(this.hasDefaultExecutionIdProvider);
+ if (!this.isDefaultExecutionIdProvider && requestInput.getExecutionId() == null) {
+ requestInput.configureExecutionInput(RESET_EXECUTION_ID_CONFIGURER);
+ }
+ ExecutionInput executionInput = requestInput.toExecutionInput();
ReactorContextManager.setReactorContext(contextView, executionInput);
ExecutionInput updatedExecutionInput = registerDataLoaders(executionInput);
return Mono.fromFuture(this.graphQlSource.graphQl().executeAsync(updatedExecutionInput))
diff --git a/spring-graphql/src/test/java/org/springframework/graphql/RequestInputTests.java b/spring-graphql/src/test/java/org/springframework/graphql/RequestInputTests.java
index 3a923fed..d5162664 100644
--- a/spring-graphql/src/test/java/org/springframework/graphql/RequestInputTests.java
+++ b/spring-graphql/src/test/java/org/springframework/graphql/RequestInputTests.java
@@ -28,23 +28,19 @@ import static org.assertj.core.api.Assertions.assertThat;
*/
class RequestInputTests {
- private RequestInput requestInput = new RequestInput("greeting", "Greeting", null, null, "id");
+ private final RequestInput requestInput = new RequestInput("greeting", "Greeting", null, null, "id");
+
@Test
- void shouldUseCustomExecutionIdIfPresent() {
+ void shouldUseRequestId() {
+ assertThat(this.requestInput.toExecutionInput().getExecutionId()).isEqualTo(ExecutionId.from("id"));
+ }
+
+ @Test
+ void shouldUseExecutionId() {
ExecutionId customId = ExecutionId.from("customId");
this.requestInput.executionId(customId);
- assertThat(this.requestInput.toExecutionInput(true).getExecutionId()).isEqualTo(customId);
- assertThat(this.requestInput.toExecutionInput(false).getExecutionId()).isEqualTo(customId);
+ assertThat(this.requestInput.toExecutionInput().getExecutionId()).isEqualTo(customId);
}
- @Test
- void executionIdShouldFallBackToRequestId() {
- assertThat(this.requestInput.toExecutionInput(true).getExecutionId()).isEqualTo(ExecutionId.from("id"));
- }
-
- @Test
- void executionIdShouldFallBackToProvider() {
- assertThat(this.requestInput.toExecutionInput(false).getExecutionId()).isNull();
- }
}
\ No newline at end of file
diff --git a/spring-graphql/src/test/java/org/springframework/graphql/web/WebInterceptorTests.java b/spring-graphql/src/test/java/org/springframework/graphql/web/WebInterceptorTests.java
index 20e3831d..e048c93a 100644
--- a/spring-graphql/src/test/java/org/springframework/graphql/web/WebInterceptorTests.java
+++ b/spring-graphql/src/test/java/org/springframework/graphql/web/WebInterceptorTests.java
@@ -79,7 +79,7 @@ public class WebInterceptorTests {
WebGraphQlHandler handler = WebGraphQlHandler
.builder((input) -> {
- actualName.set(input.toExecutionInput(true).getOperationName());
+ actualName.set(input.toExecutionInput().getOperationName());
return emptyExecutionResult(input);
})
.interceptor((webInput, next) -> {