graphql.execution.instrumentation.ChainedInstrumentation Maven / Gradle / Ivy
package graphql.execution.instrumentation;
import com.google.common.collect.ImmutableList;
import graphql.ExecutionInput;
import graphql.ExecutionResult;
import graphql.ExperimentalApi;
import graphql.PublicApi;
import graphql.execution.Async;
import graphql.execution.ExecutionContext;
import graphql.execution.FieldValueInfo;
import graphql.execution.instrumentation.parameters.InstrumentationCreateStateParameters;
import graphql.execution.instrumentation.parameters.InstrumentationExecuteOperationParameters;
import graphql.execution.instrumentation.parameters.InstrumentationExecutionParameters;
import graphql.execution.instrumentation.parameters.InstrumentationExecutionStrategyParameters;
import graphql.execution.instrumentation.parameters.InstrumentationFieldCompleteParameters;
import graphql.execution.instrumentation.parameters.InstrumentationFieldFetchParameters;
import graphql.execution.instrumentation.parameters.InstrumentationFieldParameters;
import graphql.execution.instrumentation.parameters.InstrumentationValidationParameters;
import graphql.language.Document;
import graphql.schema.DataFetcher;
import graphql.schema.GraphQLSchema;
import graphql.validation.ValidationError;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.AbstractMap;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import static graphql.Assert.assertNotNull;
/**
* This allows you to chain together a number of {@link graphql.execution.instrumentation.Instrumentation} implementations
* and run them in sequence. The list order of instrumentation objects is always guaranteed to be followed and
* the {@link graphql.execution.instrumentation.InstrumentationState} objects they create will be passed back to the originating
* implementation.
*
* @see graphql.execution.instrumentation.Instrumentation
*/
@PublicApi
public class ChainedInstrumentation implements Instrumentation {
// This class is inspired from https://github.com/leangen/graphql-spqr/blob/master/src/main/java/io/leangen/graphql/GraphQLRuntime.java#L80
protected final ImmutableList instrumentations;
public ChainedInstrumentation(List instrumentations) {
this.instrumentations = ImmutableList.copyOf(assertNotNull(instrumentations));
}
public ChainedInstrumentation(Instrumentation... instrumentations) {
this(Arrays.asList(instrumentations));
}
/**
* @return the list of instrumentations in play
*/
public List getInstrumentations() {
return instrumentations;
}
private InstrumentationContext chainedCtx(InstrumentationState state, BiFunction> mapper) {
// if we have zero or 1 instrumentations (and 1 is the most common), then we can avoid an object allocation
// of the ChainedInstrumentationContext since it won't be needed
if (instrumentations.isEmpty()) {
return SimpleInstrumentationContext.noOp();
}
ChainedInstrumentationState chainedInstrumentationState = (ChainedInstrumentationState) state;
if (instrumentations.size() == 1) {
return mapper.apply(instrumentations.get(0), chainedInstrumentationState.getState(0));
}
return new ChainedInstrumentationContext<>(chainedMapAndDropNulls(chainedInstrumentationState, mapper));
}
private T chainedInstrument(InstrumentationState state, T input, ChainedInstrumentationFunction mapper) {
ChainedInstrumentationState chainedInstrumentationState = (ChainedInstrumentationState) state;
for (int i = 0; i < instrumentations.size(); i++) {
Instrumentation instrumentation = instrumentations.get(i);
InstrumentationState specificState = chainedInstrumentationState.getState(i);
input = mapper.apply(instrumentation, specificState, input);
}
return input;
}
protected ImmutableList chainedMapAndDropNulls(InstrumentationState state, BiFunction mapper) {
ChainedInstrumentationState chainedInstrumentationState = (ChainedInstrumentationState) state;
ImmutableList.Builder result = ImmutableList.builderWithExpectedSize(instrumentations.size());
for (int i = 0; i < instrumentations.size(); i++) {
Instrumentation instrumentation = instrumentations.get(i);
InstrumentationState specificState = chainedInstrumentationState.getState(i);
T value = mapper.apply(instrumentation, specificState);
if (value != null) {
result.add(value);
}
}
return result.build();
}
protected void chainedConsume(InstrumentationState state, BiConsumer stateConsumer) {
ChainedInstrumentationState chainedInstrumentationState = (ChainedInstrumentationState) state;
for (int i = 0; i < instrumentations.size(); i++) {
Instrumentation instrumentation = instrumentations.get(i);
InstrumentationState specificState = chainedInstrumentationState.getState(i);
stateConsumer.accept(instrumentation, specificState);
}
}
@Override
public @NotNull CompletableFuture createStateAsync(InstrumentationCreateStateParameters parameters) {
return ChainedInstrumentationState.combineAll(instrumentations, parameters);
}
@Override
public InstrumentationContext beginExecution(InstrumentationExecutionParameters parameters, InstrumentationState state) {
return chainedCtx(state, (instrumentation, specificState) -> instrumentation.beginExecution(parameters, specificState));
}
@Override
public InstrumentationContext beginParse(InstrumentationExecutionParameters parameters, InstrumentationState state) {
return chainedCtx(state, (instrumentation, specificState) -> instrumentation.beginParse(parameters, specificState));
}
@Override
public InstrumentationContext> beginValidation(InstrumentationValidationParameters parameters, InstrumentationState state) {
return chainedCtx(state, (instrumentation, specificState) -> instrumentation.beginValidation(parameters, specificState));
}
@Override
public InstrumentationContext beginExecuteOperation(InstrumentationExecuteOperationParameters parameters, InstrumentationState state) {
return chainedCtx(state, (instrumentation, specificState) -> instrumentation.beginExecuteOperation(parameters, specificState));
}
@Override
public ExecutionStrategyInstrumentationContext beginExecutionStrategy(InstrumentationExecutionStrategyParameters parameters, InstrumentationState state) {
if (instrumentations.isEmpty()) {
return ExecutionStrategyInstrumentationContext.NOOP;
}
BiFunction mapper = (instrumentation, specificState) -> instrumentation.beginExecutionStrategy(parameters, specificState);
ChainedInstrumentationState chainedInstrumentationState = (ChainedInstrumentationState) state;
if (instrumentations.size() == 1) {
return mapper.apply(instrumentations.get(0), chainedInstrumentationState.getState(0));
}
return new ChainedExecutionStrategyInstrumentationContext(chainedMapAndDropNulls(chainedInstrumentationState, mapper));
}
@Override
public @Nullable ExecuteObjectInstrumentationContext beginExecuteObject(InstrumentationExecutionStrategyParameters parameters, InstrumentationState state) {
if (instrumentations.isEmpty()) {
return ExecuteObjectInstrumentationContext.NOOP;
}
BiFunction mapper = (instrumentation, specificState) -> instrumentation.beginExecuteObject(parameters, specificState);
ChainedInstrumentationState chainedInstrumentationState = (ChainedInstrumentationState) state;
if (instrumentations.size() == 1) {
return mapper.apply(instrumentations.get(0), chainedInstrumentationState.getState(0));
}
return new ChainedExecuteObjectInstrumentationContext(chainedMapAndDropNulls(chainedInstrumentationState, mapper));
}
@ExperimentalApi
@Override
public InstrumentationContext
© 2015 - 2024 Weber Informatics LLC | Privacy Policy