Class ExecutionStrategy

  • Direct Known Subclasses:
    AbstractAsyncExecutionStrategy, SubscriptionExecutionStrategy

    @PublicSpi
    public abstract class ExecutionStrategy
    extends java.lang.Object
    An execution strategy is give a list of fields from the graphql query to execute and find values for using a recursive strategy.
         query {
              friends {
                  id
                  name
                  friends {
                      id
                      name
                  }
              }
              enemies {
                  id
                  name
                  allies {
                      id
                      name
                  }
              }
         }
    
     

    Given the graphql query above, an execution strategy will be called for the top level fields 'friends' and 'enemies' and it will be asked to find an object to describe them. Because they are both complex object types, it needs to descend down that query and start fetching and completing fields such as 'id','name' and other complex fields such as 'friends' and 'allies', by recursively calling to itself to execute these lower field layers

    The execution of a field has two phases, first a raw object must be fetched for a field via a DataFetcher which is defined on the GraphQLFieldDefinition. This object must then be 'completed' into a suitable value, either as a scalar/enum type via coercion or if it's a complex object type by recursively calling the execution strategy for the lower level fields.

    The first phase (data fetching) is handled by the method fetchField(ExecutionContext, ExecutionStrategyParameters)

    The second phase (value completion) is handled by the methods completeField(ExecutionContext, ExecutionStrategyParameters, FetchedValue) and the other "completeXXX" methods.

    The order of fields fetching and completion is up to the execution strategy. As the graphql specification https://spec.graphql.org/October2021/#sec-Normal-and-Serial-Execution says:

    Normally the executor can execute the entries in a grouped field set in whatever order it chooses (often in parallel). Because the resolution of fields other than top-level mutation fields must always be side effect-free and idempotent, the execution order must not affect the result, and hence the server has the freedom to execute the field entries in whatever order it deems optimal.

    So in the case above you could execute the fields depth first ('friends' and its sub fields then do 'enemies' and its sub fields or it could do breadth first ('fiends' and 'enemies' data fetch first and then all the sub fields) or in parallel via asynchronous facilities like CompletableFutures.

    execute(ExecutionContext, ExecutionStrategyParameters) is the entry point of the execution strategy.

    • Constructor Detail

      • ExecutionStrategy

        protected ExecutionStrategy​(DataFetcherExceptionHandler dataFetcherExceptionHandler)
        The consumers of the execution strategy can pass in a DataFetcherExceptionHandler to better decide what do when a data fetching error happens
        Parameters:
        dataFetcherExceptionHandler - the callback invoked if an exception happens during data fetching
    • Method Detail

      • resolveField

        protected java.util.concurrent.CompletableFuture<ExecutionResult> resolveField​(ExecutionContext executionContext,
                                                                                       ExecutionStrategyParameters parameters)
        Called to fetch a value for a field and resolve it further in terms of the graphql query. This will call #fetchField followed by #completeField and the completed ExecutionResult is returned.

        An execution strategy can iterate the fields to be executed and call this method for each one

        Graphql fragments mean that for any give logical field can have one or more Field values associated with it in the query, hence the fieldList. However the first entry is representative of the field for most purposes.

        Parameters:
        executionContext - contains the top level execution parameters
        parameters - contains the parameters holding the fields to be executed and source object
        Returns:
        a promise to an ExecutionResult
        Throws:
        NonNullableFieldWasNullException - in the future if a non null field resolves to a null value
      • resolveFieldWithInfo

        protected java.util.concurrent.CompletableFuture<FieldValueInfo> resolveFieldWithInfo​(ExecutionContext executionContext,
                                                                                              ExecutionStrategyParameters parameters)
        Called to fetch a value for a field and its extra runtime info and resolve it further in terms of the graphql query. This will call #fetchField followed by #completeField and the completed FieldValueInfo is returned.

        An execution strategy can iterate the fields to be executed and call this method for each one

        Graphql fragments mean that for any give logical field can have one or more Field values associated with it in the query, hence the fieldList. However the first entry is representative of the field for most purposes.

        Parameters:
        executionContext - contains the top level execution parameters
        parameters - contains the parameters holding the fields to be executed and source object
        Returns:
        a promise to a FieldValueInfo
        Throws:
        NonNullableFieldWasNullException - in the FieldValueInfo.getFieldValue() future if a non null field resolves to a null value
      • fetchField

        protected java.util.concurrent.CompletableFuture<FetchedValue> fetchField​(ExecutionContext executionContext,
                                                                                  ExecutionStrategyParameters parameters)
        Called to fetch a value for a field from the DataFetcher associated with the field GraphQLFieldDefinition.

        Graphql fragments mean that for any give logical field can have one or more Field values associated with it in the query, hence the fieldList. However the first entry is representative of the field for most purposes.

        Parameters:
        executionContext - contains the top level execution parameters
        parameters - contains the parameters holding the fields to be executed and source object
        Returns:
        a promise to a fetched object
        Throws:
        NonNullableFieldWasNullException - in the future if a non null field resolves to a null value
      • handleFetchingException

        protected <T> java.util.concurrent.CompletableFuture<T> handleFetchingException​(ExecutionContext executionContext,
                                                                                        DataFetchingEnvironment environment,
                                                                                        java.lang.Throwable e)
      • completeField

        protected FieldValueInfo completeField​(ExecutionContext executionContext,
                                               ExecutionStrategyParameters parameters,
                                               FetchedValue fetchedValue)
        Called to complete a field based on the type of the field.

        If the field is a scalar type, then it will be coerced and returned. However if the field type is an complex object type, then the execution strategy will be called recursively again to execute the fields of that type before returning.

        Graphql fragments mean that for any give logical field can have one or more Field values associated with it in the query, hence the fieldList. However the first entry is representative of the field for most purposes.

        Parameters:
        executionContext - contains the top level execution parameters
        parameters - contains the parameters holding the fields to be executed and source object
        fetchedValue - the fetched raw value
        Returns:
        a FieldValueInfo
        Throws:
        NonNullableFieldWasNullException - in the FieldValueInfo.getFieldValue() future if a non null field resolves to a null value
      • completeValue

        protected FieldValueInfo completeValue​(ExecutionContext executionContext,
                                               ExecutionStrategyParameters parameters)
                                        throws NonNullableFieldWasNullException
        Called to complete a value for a field based on the type of the field.

        If the field is a scalar type, then it will be coerced and returned. However if the field type is an complex object type, then the execution strategy will be called recursively again to execute the fields of that type before returning.

        Graphql fragments mean that for any give logical field can have one or more Field values associated with it in the query, hence the fieldList. However the first entry is representative of the field for most purposes.

        Parameters:
        executionContext - contains the top level execution parameters
        parameters - contains the parameters holding the fields to be executed and source object
        Returns:
        a FieldValueInfo
        Throws:
        NonNullableFieldWasNullException - if a non null field resolves to a null value
      • completeValueForScalar

        protected java.util.concurrent.CompletableFuture<ExecutionResult> completeValueForScalar​(ExecutionContext executionContext,
                                                                                                 ExecutionStrategyParameters parameters,
                                                                                                 GraphQLScalarType scalarType,
                                                                                                 java.lang.Object result)
        Called to turn an object into a scalar value according to the GraphQLScalarType by asking that scalar type to coerce the object into a valid value
        Parameters:
        executionContext - contains the top level execution parameters
        parameters - contains the parameters holding the fields to be executed and source object
        scalarType - the type of the scalar
        result - the result to be coerced
        Returns:
        a promise to an ExecutionResult
      • completeValueForEnum

        protected java.util.concurrent.CompletableFuture<ExecutionResult> completeValueForEnum​(ExecutionContext executionContext,
                                                                                               ExecutionStrategyParameters parameters,
                                                                                               GraphQLEnumType enumType,
                                                                                               java.lang.Object result)
        Called to turn an object into a enum value according to the GraphQLEnumType by asking that enum type to coerce the object into a valid value
        Parameters:
        executionContext - contains the top level execution parameters
        parameters - contains the parameters holding the fields to be executed and source object
        enumType - the type of the enum
        result - the result to be coerced
        Returns:
        a promise to an ExecutionResult
      • completeValueForObject

        protected java.util.concurrent.CompletableFuture<ExecutionResult> completeValueForObject​(ExecutionContext executionContext,
                                                                                                 ExecutionStrategyParameters parameters,
                                                                                                 GraphQLObjectType resolvedObjectType,
                                                                                                 java.lang.Object result)
        Called to turn a java object value into an graphql object value
        Parameters:
        executionContext - contains the top level execution parameters
        parameters - contains the parameters holding the fields to be executed and source object
        resolvedObjectType - the resolved object type
        result - the result to be coerced
        Returns:
        a promise to an ExecutionResult
      • toIterable

        protected java.lang.Iterable<java.lang.Object> toIterable​(java.lang.Object result)
        Converts an object that is known to should be an Iterable into one
        Parameters:
        result - the result object
        Returns:
        an Iterable from that object
        Throws:
        java.lang.ClassCastException - if it's not an Iterable
      • assertNonNullFieldPrecondition

        protected void assertNonNullFieldPrecondition​(NonNullableFieldWasNullException e)
                                               throws NonNullableFieldWasNullException
        See (...),

        If a non nullable child field type actually resolves to a null value and the parent type is nullable then the parent must in fact become null so we use exceptions to indicate this special case. However if the parent is in fact a non nullable type itself then we need to bubble that upwards again until we get to the root in which case the result is meant to be null.

        Parameters:
        e - this indicates that a null value was returned for a non null field, which needs to cause the parent field to become null OR continue on as an exception
        Throws:
        NonNullableFieldWasNullException - if a non null field resolves to a null value
      • createExecutionStepInfo

        protected ExecutionStepInfo createExecutionStepInfo​(ExecutionContext executionContext,
                                                            ExecutionStrategyParameters parameters,
                                                            GraphQLFieldDefinition fieldDefinition,
                                                            GraphQLObjectType fieldContainer)
        Builds the type info hierarchy for the current field
        Parameters:
        executionContext - the execution context in play
        parameters - contains the parameters holding the fields to be executed and source object
        fieldDefinition - the field definition to build type info for
        fieldContainer - the field container
        Returns:
        a new type info
      • mkNameForPath

        public static java.lang.String mkNameForPath​(Field currentField)
      • mkNameForPath

        public static java.lang.String mkNameForPath​(MergedField mergedField)
      • mkNameForPath

        public static java.lang.String mkNameForPath​(java.util.List<Field> currentField)