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. + *

+ *

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) -> {