diff --git a/spring-graphql/src/main/java/org/springframework/graphql/execution/ContextManager.java b/spring-graphql/src/main/java/org/springframework/graphql/execution/ContextManager.java new file mode 100644 index 00000000..9408bbff --- /dev/null +++ b/spring-graphql/src/main/java/org/springframework/graphql/execution/ContextManager.java @@ -0,0 +1,54 @@ +/* + * Copyright 2002-2021 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.execution; + +import graphql.ExecutionInput; +import graphql.GraphQLContext; +import graphql.schema.DataFetchingEnvironment; +import reactor.util.context.ContextView; + +import org.springframework.lang.Nullable; + +/** + * Package private utility class for propagating a Reactor {@link ContextView} + * through the {@link ExecutionInput} and the {@link DataFetchingEnvironment} + * of a request. + */ +abstract class ContextManager { + + private static final String REACTOR_CONTEXT_KEY = + ReactorDataFetcherAdapter.class.getName() + ".REACTOR_CONTEXT"; + + + /** + * Save the given Reactor ContextView in the an {@link ExecutionInput} for + * later access through the {@link DataFetchingEnvironment}. + */ + static void setReactorContext(ContextView contextView, ExecutionInput input) { + ((GraphQLContext) input.getContext()).put(REACTOR_CONTEXT_KEY, contextView); + } + + /** + * Return the Reactor ContextView saved in the given DataFetchingEnvironment, + * or null if not present. + */ + @Nullable + static ContextView getReactorContext(DataFetchingEnvironment environment) { + GraphQLContext graphQlContext = environment.getContext(); + return graphQlContext.get(REACTOR_CONTEXT_KEY); + } + +} 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 bd800690..b3cefd08 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 @@ -40,7 +40,7 @@ public class ExecutionGraphQlService implements GraphQlService { public Mono execute(ExecutionInput input) { GraphQL graphQl = this.graphQlSource.graphQl(); return Mono.deferContextual(contextView -> { - ReactorDataFetcherAdapter.addReactorContext(input, contextView); + ContextManager.setReactorContext(contextView, input); return Mono.fromFuture(graphQl.executeAsync(input)); }); } diff --git a/spring-graphql/src/main/java/org/springframework/graphql/execution/ReactorDataFetcherAdapter.java b/spring-graphql/src/main/java/org/springframework/graphql/execution/ReactorDataFetcherAdapter.java index a397420e..d197a04e 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/execution/ReactorDataFetcherAdapter.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/execution/ReactorDataFetcherAdapter.java @@ -15,10 +15,7 @@ */ package org.springframework.graphql.execution; -import java.lang.reflect.Method; - import graphql.ExecutionInput; -import graphql.GraphQLContext; import graphql.schema.DataFetcher; import graphql.schema.DataFetchingEnvironment; import graphql.schema.GraphQLCodeRegistry; @@ -34,9 +31,7 @@ import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.util.context.ContextView; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; -import org.springframework.util.ClassUtils; /** * Adapter that can wrap a registered {@link DataFetcher} and enable it to return @@ -46,10 +41,6 @@ import org.springframework.util.ClassUtils; */ class ReactorDataFetcherAdapter implements DataFetcher { - private static final String REACTOR_CONTEXT_KEY = - ReactorDataFetcherAdapter.class.getName() + ".REACTOR_CONTEXT"; - - private final DataFetcher delegate; private final boolean subscription; @@ -67,7 +58,7 @@ class ReactorDataFetcherAdapter implements DataFetcher { Object value = this.delegate.get(environment); if (this.subscription) { - ContextView context = getReactorContext(environment); + ContextView context = ContextManager.getReactorContext(environment); return (context != null ? Flux.from((Publisher) value).contextWrite(context) : value); } @@ -77,7 +68,7 @@ class ReactorDataFetcherAdapter implements DataFetcher { if (value instanceof Mono) { Mono valueMono = (Mono) value; - ContextView reactorContext = getReactorContext(environment); + ContextView reactorContext = ContextManager.getReactorContext(environment); if (reactorContext != null) { valueMono = valueMono.contextWrite(reactorContext); } @@ -87,21 +78,6 @@ class ReactorDataFetcherAdapter implements DataFetcher { return value; } - @Nullable - private ContextView getReactorContext(DataFetchingEnvironment environment) { - GraphQLContext graphQlContext = environment.getContext(); - return graphQlContext.get(REACTOR_CONTEXT_KEY); - } - - /** - * Insert the given Reactor Context into the {@link ExecutionInput} context - * for later retrieval from the {@link DataFetchingEnvironment}. - */ - public static void addReactorContext(ExecutionInput executionInput, ContextView reactorContext) { - GraphQLContext graphQlContext = (GraphQLContext) executionInput.getContext(); - graphQlContext.put(REACTOR_CONTEXT_KEY, reactorContext); - } - /** * {@link GraphQLTypeVisitor} that wraps non-GraphQL data fetchers and @@ -121,10 +97,8 @@ class ReactorDataFetcherAdapter implements DataFetcher { return TraversalControl.CONTINUE; } - Method method = ClassUtils.getMethod(dataFetcher.getClass(), "get", DataFetchingEnvironment.class); - method = ClassUtils.getMostSpecificMethod(method, dataFetcher.getClass()); - - dataFetcher = new ReactorDataFetcherAdapter(dataFetcher, parent.getName().equals("Subscription")); + boolean handlesSubscription = parent.getName().equals("Subscription"); + dataFetcher = new ReactorDataFetcherAdapter(dataFetcher, handlesSubscription); codeRegistry.dataFetcher(parent, fieldDefinition, dataFetcher); return TraversalControl.CONTINUE; } diff --git a/spring-graphql/src/test/java/org/springframework/graphql/execution/ReactorDataFetcherAdapterTests.java b/spring-graphql/src/test/java/org/springframework/graphql/execution/ReactorDataFetcherAdapterTests.java index 8c5a3df9..a45a2b6c 100644 --- a/spring-graphql/src/test/java/org/springframework/graphql/execution/ReactorDataFetcherAdapterTests.java +++ b/spring-graphql/src/test/java/org/springframework/graphql/execution/ReactorDataFetcherAdapterTests.java @@ -108,7 +108,7 @@ public class ReactorDataFetcherAdapterTests { private ExecutionInput executionInput(String query, Context reactorContext) { ExecutionInput input = ExecutionInput.newExecutionInput().query(query).build(); - ReactorDataFetcherAdapter.addReactorContext(input, reactorContext); + ContextManager.setReactorContext(reactorContext, input); return input; }