Extract ContextManager from ReactorDataFetcherAdapter

This change separates the Reactor ContextView propagation into an
independent utility class to enable internal re-use.

See gh-52
This commit is contained in:
Rossen Stoyanchev
2021-05-19 15:01:38 +01:00
parent 5d4c08a30b
commit 827c188f63
4 changed files with 60 additions and 32 deletions

View File

@@ -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);
}
}

View File

@@ -40,7 +40,7 @@ public class ExecutionGraphQlService implements GraphQlService {
public Mono<ExecutionResult> 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));
});
}

View File

@@ -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<Object> {
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> {
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<Object> {
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<Object> {
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<Object> {
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;
}

View File

@@ -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;
}