All Downloads are FREE. Search and download functionalities are using the official Maven repository.

graphql.nextgen.GraphQL Maven / Gradle / Ivy

There is a newer version: 230521-nf-execution
Show newest version
package graphql.nextgen;

import graphql.ExecutionInput;
import graphql.ExecutionResult;
import graphql.ExecutionResultImpl;
import graphql.Internal;
import graphql.ParseAndValidate;
import graphql.ParseAndValidateResult;
import graphql.execution.AbortExecutionException;
import graphql.execution.ExecutionId;
import graphql.execution.ExecutionIdProvider;
import graphql.execution.instrumentation.DocumentAndVariables;
import graphql.execution.instrumentation.InstrumentationContext;
import graphql.execution.instrumentation.InstrumentationState;
import graphql.execution.instrumentation.nextgen.Instrumentation;
import graphql.execution.instrumentation.nextgen.InstrumentationCreateStateParameters;
import graphql.execution.instrumentation.nextgen.InstrumentationExecutionParameters;
import graphql.execution.instrumentation.nextgen.InstrumentationValidationParameters;
import graphql.execution.nextgen.DefaultExecutionStrategy;
import graphql.execution.nextgen.Execution;
import graphql.execution.nextgen.ExecutionStrategy;
import graphql.execution.preparsed.NoOpPreparsedDocumentProvider;
import graphql.execution.preparsed.PreparsedDocumentEntry;
import graphql.execution.preparsed.PreparsedDocumentProvider;
import graphql.language.Document;
import graphql.schema.GraphQLSchema;
import graphql.util.LogKit;
import graphql.validation.ValidationError;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.UnaryOperator;

import static graphql.Assert.assertNotNull;

/**
 *
 * @deprecated Jan 2022 - We have decided to deprecate the NextGen engine, and it will be removed in a future release.
 */
@Deprecated
@SuppressWarnings("Duplicates")
@Internal
public class GraphQL {
    private static final Logger log = LoggerFactory.getLogger(graphql.GraphQL.class);
    private static final Logger logNotSafe = LogKit.getNotPrivacySafeLogger(ExecutionStrategy.class);

    private final GraphQLSchema graphQLSchema;
    private final ExecutionStrategy executionStrategy;
    private final ExecutionIdProvider idProvider;
    private final Instrumentation instrumentation;
    private final PreparsedDocumentProvider preparsedDocumentProvider;

    public GraphQL(Builder builder) {
        this.graphQLSchema = builder.graphQLSchema;
        this.executionStrategy = builder.executionStrategy;
        this.idProvider = builder.idProvider;
        this.preparsedDocumentProvider = builder.preparsedDocumentProvider;
        this.instrumentation = builder.instrumentation;
    }

    /**
     * Executes the graphql query using the provided input object builder
     * 

* This will return a completed {@link ExecutionResult} * which is the result of executing the provided query. * * @param executionInputBuilder {@link ExecutionInput.Builder} * * @return an {@link ExecutionResult} which can include errors */ public ExecutionResult execute(ExecutionInput.Builder executionInputBuilder) { return executeAsync(executionInputBuilder.build()).join(); } /** * Executes the graphql query using the provided input object builder *

* This will return a completed {@link ExecutionResult} * which is the result of executing the provided query. *

* This allows a lambda style like : *

     * {@code
     *    ExecutionResult result = graphql.execute(input -> input.query("{hello}").root(startingObj).context(contextObj));
     * }
     * 
* * @param builderFunction a function that is given a {@link ExecutionInput.Builder} * * @return a promise to an {@link ExecutionResult} which can include errors */ public CompletableFuture execute(UnaryOperator builderFunction) { return executeAsync(builderFunction.apply(ExecutionInput.newExecutionInput()).build()); } /** * Executes the graphql query using the provided input object *

* This will return a completed {@link ExecutionResult} * which is the result of executing the provided query. * * @param executionInput {@link ExecutionInput} * * @return a promise to an {@link ExecutionResult} which can include errors */ public ExecutionResult execute(ExecutionInput executionInput) { return executeAsync(executionInput).join(); } /** * Executes the graphql query using the provided input object builder *

* This will return a promise (aka {@link CompletableFuture}) to provide a {@link ExecutionResult} * which is the result of executing the provided query. * * @param executionInputBuilder {@link ExecutionInput.Builder} * * @return a promise to an {@link ExecutionResult} which can include errors */ public CompletableFuture executeAsync(ExecutionInput.Builder executionInputBuilder) { return executeAsync(executionInputBuilder.build()); } /** * Executes the graphql query using the provided input object builder *

* This will return a promise (aka {@link CompletableFuture}) to provide a {@link ExecutionResult} * which is the result of executing the provided query. *

* This allows a lambda style like : *

     * {@code
     *    ExecutionResult result = graphql.executeAsync(input -> input.query("{hello}").root(startingObj).context(contextObj));
     * }
     * 
* * @param builderFunction a function that is given a {@link ExecutionInput.Builder} * * @return a promise to an {@link ExecutionResult} which can include errors */ public CompletableFuture executeAsync(UnaryOperator builderFunction) { return executeAsync(builderFunction.apply(ExecutionInput.newExecutionInput()).build()); } /** * Executes the graphql query using the provided input object *

* This will return a promise (aka {@link CompletableFuture}) to provide a {@link ExecutionResult} * which is the result of executing the provided query. * * @param executionInput {@link ExecutionInput} * * @return a promise to an {@link ExecutionResult} which can include errors */ public CompletableFuture executeAsync(ExecutionInput executionInput) { try { if (logNotSafe.isDebugEnabled()) { logNotSafe.debug("Executing request. operation name: '{}'. query: '{}'. variables '{}'", executionInput.getOperationName(), executionInput.getQuery(), executionInput.getVariables()); } InstrumentationState instrumentationState = instrumentation.createState(new InstrumentationCreateStateParameters(this.graphQLSchema, executionInput)); InstrumentationExecutionParameters inputInstrumentationParameters = new InstrumentationExecutionParameters(executionInput, this.graphQLSchema, instrumentationState); executionInput = instrumentation.instrumentExecutionInput(executionInput, inputInstrumentationParameters); InstrumentationExecutionParameters instrumentationParameters = new InstrumentationExecutionParameters(executionInput, this.graphQLSchema, instrumentationState); InstrumentationContext executionInstrumentation = instrumentation.beginExecution(instrumentationParameters); GraphQLSchema graphQLSchema = instrumentation.instrumentSchema(this.graphQLSchema, instrumentationParameters); CompletableFuture executionResult = parseValidateAndExecute(executionInput, graphQLSchema, instrumentationState); // // finish up instrumentation executionResult = executionResult.whenComplete(executionInstrumentation::onCompleted); // // allow instrumentation to tweak the result executionResult = executionResult.thenApply(result -> instrumentation.instrumentExecutionResult(result, instrumentationParameters)); return executionResult; } catch (AbortExecutionException abortException) { return CompletableFuture.completedFuture(abortException.toExecutionResult()); } } private CompletableFuture parseValidateAndExecute(ExecutionInput executionInput, GraphQLSchema graphQLSchema, InstrumentationState instrumentationState) { AtomicReference executionInputRef = new AtomicReference<>(executionInput); Function computeFunction = transformedInput -> { // if they change the original query in the pre-parser, then we want to see it downstream from then on executionInputRef.set(transformedInput); return parseAndValidate(executionInputRef, graphQLSchema, instrumentationState); }; CompletableFuture preparsedDoc = preparsedDocumentProvider.getDocumentAsync(executionInput, computeFunction); return preparsedDoc.thenCompose(preparsedDocumentEntry -> { if (preparsedDocumentEntry.hasErrors()) { return CompletableFuture.completedFuture(new ExecutionResultImpl(preparsedDocumentEntry.getErrors())); } try { return execute(executionInputRef.get(), preparsedDocumentEntry.getDocument(), graphQLSchema, instrumentationState); } catch (AbortExecutionException e) { return CompletableFuture.completedFuture(e.toExecutionResult()); } }); } private PreparsedDocumentEntry parseAndValidate(AtomicReference executionInputRef, GraphQLSchema graphQLSchema, InstrumentationState instrumentationState) { ExecutionInput executionInput = executionInputRef.get(); String query = executionInput.getQuery(); if (logNotSafe.isDebugEnabled()) { logNotSafe.debug("Parsing query: '{}'...", query); } ParseAndValidateResult parseResult = parse(executionInput, graphQLSchema, instrumentationState); if (parseResult.isFailure()) { logNotSafe.warn("Query did not parse : '{}'", executionInput.getQuery()); return new PreparsedDocumentEntry(parseResult.getSyntaxException().toInvalidSyntaxError()); } else { final Document document = parseResult.getDocument(); // they may have changed the document and the variables via instrumentation so update the reference to it executionInput = executionInput.transform(builder -> builder.variables(parseResult.getVariables())); executionInputRef.set(executionInput); if (logNotSafe.isDebugEnabled()) { logNotSafe.debug("Validating query: '{}'", query); } final List errors = validate(executionInput, document, graphQLSchema, instrumentationState); if (!errors.isEmpty()) { logNotSafe.warn("Query did not validate : '{}'", query); return new PreparsedDocumentEntry(errors); } return new PreparsedDocumentEntry(document); } } private ParseAndValidateResult parse(ExecutionInput executionInput, GraphQLSchema graphQLSchema, InstrumentationState instrumentationState) { InstrumentationExecutionParameters parameters = new InstrumentationExecutionParameters(executionInput, graphQLSchema, instrumentationState); InstrumentationContext parseInstrumentation = instrumentation.beginParse(parameters); CompletableFuture documentCF = new CompletableFuture<>(); parseInstrumentation.onDispatched(documentCF); ParseAndValidateResult parseResult = ParseAndValidate.parse(executionInput); if (parseResult.isFailure()) { parseInstrumentation.onCompleted(null, parseResult.getSyntaxException()); return parseResult; } else { documentCF.complete(parseResult.getDocument()); parseInstrumentation.onCompleted(parseResult.getDocument(), null); DocumentAndVariables documentAndVariables = parseResult.getDocumentAndVariables(); documentAndVariables = instrumentation.instrumentDocumentAndVariables(documentAndVariables, parameters); return ParseAndValidateResult.newResult() .document(documentAndVariables.getDocument()).variables(documentAndVariables.getVariables()).build(); } } private List validate(ExecutionInput executionInput, Document document, GraphQLSchema graphQLSchema, InstrumentationState instrumentationState) { InstrumentationContext> validationCtx = instrumentation.beginValidation(new InstrumentationValidationParameters(executionInput, document, graphQLSchema, instrumentationState)); CompletableFuture> cf = new CompletableFuture<>(); validationCtx.onDispatched(cf); Predicate> validationRulePredicate = executionInput.getGraphQLContext().getOrDefault(ParseAndValidate.INTERNAL_VALIDATION_PREDICATE_HINT, r -> true); List validationErrors = ParseAndValidate.validate(graphQLSchema, document, validationRulePredicate); validationCtx.onCompleted(validationErrors, null); cf.complete(validationErrors); return validationErrors; } private CompletableFuture execute(ExecutionInput executionInput, Document document, GraphQLSchema graphQLSchema, InstrumentationState instrumentationState) { String query = executionInput.getQuery(); String operationName = executionInput.getOperationName(); Object context = executionInput.getGraphQLContext(); Execution execution = new Execution(); ExecutionId executionId = idProvider.provide(query, operationName, context); if (logNotSafe.isDebugEnabled()) { logNotSafe.debug("Executing '{}'. operation name: '{}'. query: '{}'. variables '{}'", executionId, executionInput.getOperationName(), executionInput.getQuery(), executionInput.getVariables()); } CompletableFuture future = execution.execute(executionStrategy, document, graphQLSchema, executionId, executionInput, instrumentationState); future = future.whenComplete((result, throwable) -> { if (throwable != null) { log.error(String.format("Execution '%s' threw exception when executing : query : '%s'. variables '%s'", executionId, executionInput.getQuery(), executionInput.getVariables()), throwable); } else { if (log.isDebugEnabled()) { int errorCount = result.getErrors().size(); if (errorCount > 0) { log.debug("Execution '{}' completed with '{}' errors", executionId, errorCount); } else { log.debug("Execution '{}' completed with zero errors", executionId); } } } }); return future; } /** * Helps you build a GraphQL object ready to execute queries * * @param graphQLSchema the schema to use * * @return a builder of GraphQL objects */ public static Builder newGraphQL(GraphQLSchema graphQLSchema) { return new Builder(graphQLSchema); } /** * This helps you transform the current GraphQL object into another one by starting a builder with all * the current values and allows you to transform it how you want. * * @param builderConsumer the consumer code that will be given a builder to transform * * @return a new GraphQL object based on calling build on that builder */ public GraphQL transform(Consumer builderConsumer) { Builder builder = new Builder(this); builderConsumer.accept(builder); return builder.build(); } public static class Builder { private GraphQLSchema graphQLSchema; private ExecutionStrategy executionStrategy = new DefaultExecutionStrategy(); private ExecutionIdProvider idProvider = ExecutionIdProvider.DEFAULT_EXECUTION_ID_PROVIDER; private Instrumentation instrumentation = new Instrumentation() { }; private PreparsedDocumentProvider preparsedDocumentProvider = NoOpPreparsedDocumentProvider.INSTANCE; public Builder(GraphQLSchema graphQLSchema) { this.graphQLSchema = graphQLSchema; } public Builder(GraphQL graphQL) { this.graphQLSchema = graphQL.graphQLSchema; this.executionStrategy = graphQL.executionStrategy; this.idProvider = graphQL.idProvider; this.instrumentation = graphQL.instrumentation; } public Builder schema(GraphQLSchema graphQLSchema) { this.graphQLSchema = assertNotNull(graphQLSchema, () -> "GraphQLSchema must be non null"); return this; } public Builder executionStrategy(ExecutionStrategy executionStrategy) { this.executionStrategy = assertNotNull(executionStrategy, () -> "ExecutionStrategy must be non null"); return this; } public Builder instrumentation(Instrumentation instrumentation) { this.instrumentation = assertNotNull(instrumentation, () -> "Instrumentation must be non null"); return this; } public Builder preparsedDocumentProvider(PreparsedDocumentProvider preparsedDocumentProvider) { this.preparsedDocumentProvider = assertNotNull(preparsedDocumentProvider, () -> "PreparsedDocumentProvider must be non null"); return this; } public Builder executionIdProvider(ExecutionIdProvider executionIdProvider) { this.idProvider = assertNotNull(executionIdProvider, () -> "ExecutionIdProvider must be non null"); return this; } public GraphQL build() { assertNotNull(graphQLSchema, () -> "graphQLSchema must be non null"); return new GraphQL(this); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy