@PublicSpi public abstract class ExecutionStrategy extends java.lang.Object
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 CompletableFuture
s.
execute(ExecutionContext, ExecutionStrategyParameters)
is the entry point of the execution strategy.
Modifier and Type | Field and Description |
---|---|
protected DataFetcherExceptionHandler |
dataFetcherExceptionHandler |
protected ExecutionStepInfoFactory |
executionStepInfoFactory |
protected FieldCollector |
fieldCollector |
Modifier | Constructor and Description |
---|---|
protected |
ExecutionStrategy()
The default execution strategy constructor uses the
SimpleDataFetcherExceptionHandler
for data fetching errors. |
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 |
Modifier and Type | Method and Description |
---|---|
protected void |
assertNonNullFieldPrecondition(NonNullableFieldWasNullException e)
See (...),
|
protected void |
assertNonNullFieldPrecondition(NonNullableFieldWasNullException e,
java.util.concurrent.CompletableFuture<?> completableFuture) |
protected FieldValueInfo |
completeField(ExecutionContext executionContext,
ExecutionStrategyParameters parameters,
FetchedValue fetchedValue)
Called to complete a field based on the type of the field.
|
protected FieldValueInfo |
completeValue(ExecutionContext executionContext,
ExecutionStrategyParameters parameters)
Called to complete a value for a field based on the type of the field.
|
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 |
protected FieldValueInfo |
completeValueForList(ExecutionContext executionContext,
ExecutionStrategyParameters parameters,
java.lang.Iterable<java.lang.Object> iterableValues)
Called to complete a list of value for a field based on a list type.
|
protected FieldValueInfo |
completeValueForList(ExecutionContext executionContext,
ExecutionStrategyParameters parameters,
java.lang.Object result)
Called to complete a list of value for a field based on a list type.
|
protected java.util.concurrent.CompletableFuture<ExecutionResult> |
completeValueForNull(ExecutionContext executionContext,
ExecutionStrategyParameters parameters) |
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
|
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 |
protected ExecutionStepInfo |
createExecutionStepInfo(ExecutionContext executionContext,
ExecutionStrategyParameters parameters,
GraphQLFieldDefinition fieldDefinition,
GraphQLObjectType fieldContainer)
Builds the type info hierarchy for the current field
|
abstract java.util.concurrent.CompletableFuture<ExecutionResult> |
execute(ExecutionContext executionContext,
ExecutionStrategyParameters parameters)
This is the entry point to an execution strategy.
|
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 . |
protected GraphQLFieldDefinition |
getFieldDef(ExecutionContext executionContext,
ExecutionStrategyParameters parameters,
Field field)
Called to discover the field definition give the current parameters and the AST
Field |
protected GraphQLFieldDefinition |
getFieldDef(GraphQLSchema schema,
GraphQLObjectType parentType,
Field field)
Called to discover the field definition give the current parameters and the AST
Field |
protected java.util.function.Supplier<ExecutableNormalizedField> |
getNormalizedField(ExecutionContext executionContext,
ExecutionStrategyParameters parameters,
java.util.function.Supplier<ExecutionStepInfo> executionStepInfo) |
protected <T> java.util.concurrent.CompletableFuture<T> |
handleFetchingException(ExecutionContext executionContext,
DataFetchingEnvironment environment,
java.lang.Throwable e) |
protected ExecutionResult |
handleNonNullException(ExecutionContext executionContext,
java.util.concurrent.CompletableFuture<ExecutionResult> result,
java.lang.Throwable e) |
static java.lang.String |
mkNameForPath(Field currentField) |
static java.lang.String |
mkNameForPath(java.util.List<Field> currentField) |
static java.lang.String |
mkNameForPath(MergedField mergedField) |
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.
|
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.
|
protected GraphQLObjectType |
resolveType(ExecutionContext executionContext,
ExecutionStrategyParameters parameters,
GraphQLType fieldType) |
protected java.lang.Iterable<java.lang.Object> |
toIterable(ExecutionContext context,
ExecutionStrategyParameters parameters,
java.lang.Object result) |
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
|
protected FetchedValue |
unboxPossibleDataFetcherResult(ExecutionContext executionContext,
ExecutionStrategyParameters parameters,
java.lang.Object result) |
protected final FieldCollector fieldCollector
protected final ExecutionStepInfoFactory executionStepInfoFactory
protected final DataFetcherExceptionHandler dataFetcherExceptionHandler
protected ExecutionStrategy()
SimpleDataFetcherExceptionHandler
for data fetching errors.protected ExecutionStrategy(DataFetcherExceptionHandler dataFetcherExceptionHandler)
DataFetcherExceptionHandler
to better
decide what do when a data fetching error happensdataFetcherExceptionHandler
- the callback invoked if an exception happens during data fetchingpublic abstract java.util.concurrent.CompletableFuture<ExecutionResult> execute(ExecutionContext executionContext, ExecutionStrategyParameters parameters) throws NonNullableFieldWasNullException
executionContext
- contains the top level execution parametersparameters
- contains the parameters holding the fields to be executed and source objectExecutionResult
NonNullableFieldWasNullException
- in the future if a non null field resolves to a null valueprotected java.util.concurrent.CompletableFuture<ExecutionResult> resolveField(ExecutionContext executionContext, ExecutionStrategyParameters parameters)
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.
executionContext
- contains the top level execution parametersparameters
- contains the parameters holding the fields to be executed and source objectExecutionResult
NonNullableFieldWasNullException
- in the future if a non null field resolves to a null valueprotected java.util.concurrent.CompletableFuture<FieldValueInfo> resolveFieldWithInfo(ExecutionContext executionContext, ExecutionStrategyParameters parameters)
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.
executionContext
- contains the top level execution parametersparameters
- contains the parameters holding the fields to be executed and source objectFieldValueInfo
NonNullableFieldWasNullException
- in the FieldValueInfo.getFieldValue()
future if a non null field resolves to a null valueprotected java.util.concurrent.CompletableFuture<FetchedValue> fetchField(ExecutionContext executionContext, ExecutionStrategyParameters parameters)
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.
executionContext
- contains the top level execution parametersparameters
- contains the parameters holding the fields to be executed and source objectNonNullableFieldWasNullException
- in the future if a non null field resolves to a null valueprotected java.util.function.Supplier<ExecutableNormalizedField> getNormalizedField(ExecutionContext executionContext, ExecutionStrategyParameters parameters, java.util.function.Supplier<ExecutionStepInfo> executionStepInfo)
protected FetchedValue unboxPossibleDataFetcherResult(ExecutionContext executionContext, ExecutionStrategyParameters parameters, java.lang.Object result)
protected <T> java.util.concurrent.CompletableFuture<T> handleFetchingException(ExecutionContext executionContext, DataFetchingEnvironment environment, java.lang.Throwable e)
protected FieldValueInfo completeField(ExecutionContext executionContext, ExecutionStrategyParameters parameters, FetchedValue fetchedValue)
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.
executionContext
- contains the top level execution parametersparameters
- contains the parameters holding the fields to be executed and source objectfetchedValue
- the fetched raw valueFieldValueInfo
NonNullableFieldWasNullException
- in the FieldValueInfo.getFieldValue()
future if a non null field resolves to a null valueprotected FieldValueInfo completeValue(ExecutionContext executionContext, ExecutionStrategyParameters parameters) throws NonNullableFieldWasNullException
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.
executionContext
- contains the top level execution parametersparameters
- contains the parameters holding the fields to be executed and source objectFieldValueInfo
NonNullableFieldWasNullException
- if a non null field resolves to a null valueprotected java.util.concurrent.CompletableFuture<ExecutionResult> completeValueForNull(ExecutionContext executionContext, ExecutionStrategyParameters parameters)
protected FieldValueInfo completeValueForList(ExecutionContext executionContext, ExecutionStrategyParameters parameters, java.lang.Object result)
completeValue(ExecutionContext, ExecutionStrategyParameters)
for each value.executionContext
- contains the top level execution parametersparameters
- contains the parameters holding the fields to be executed and source objectresult
- the result to complete, raw resultFieldValueInfo
protected FieldValueInfo completeValueForList(ExecutionContext executionContext, ExecutionStrategyParameters parameters, java.lang.Iterable<java.lang.Object> iterableValues)
completeValue(ExecutionContext, ExecutionStrategyParameters)
for each value.executionContext
- contains the top level execution parametersparameters
- contains the parameters holding the fields to be executed and source objectiterableValues
- the values to complete, can't be nullFieldValueInfo
protected java.util.concurrent.CompletableFuture<ExecutionResult> completeValueForScalar(ExecutionContext executionContext, ExecutionStrategyParameters parameters, GraphQLScalarType scalarType, java.lang.Object result)
GraphQLScalarType
by asking that scalar type to coerce the object
into a valid valueexecutionContext
- contains the top level execution parametersparameters
- contains the parameters holding the fields to be executed and source objectscalarType
- the type of the scalarresult
- the result to be coercedExecutionResult
protected java.util.concurrent.CompletableFuture<ExecutionResult> completeValueForEnum(ExecutionContext executionContext, ExecutionStrategyParameters parameters, GraphQLEnumType enumType, java.lang.Object result)
GraphQLEnumType
by asking that enum type to coerce the object into a valid valueexecutionContext
- contains the top level execution parametersparameters
- contains the parameters holding the fields to be executed and source objectenumType
- the type of the enumresult
- the result to be coercedExecutionResult
protected java.util.concurrent.CompletableFuture<ExecutionResult> completeValueForObject(ExecutionContext executionContext, ExecutionStrategyParameters parameters, GraphQLObjectType resolvedObjectType, java.lang.Object result)
executionContext
- contains the top level execution parametersparameters
- contains the parameters holding the fields to be executed and source objectresolvedObjectType
- the resolved object typeresult
- the result to be coercedExecutionResult
protected java.lang.Iterable<java.lang.Object> toIterable(java.lang.Object result)
result
- the result objectjava.lang.ClassCastException
- if it's not an Iterableprotected GraphQLObjectType resolveType(ExecutionContext executionContext, ExecutionStrategyParameters parameters, GraphQLType fieldType)
protected java.lang.Iterable<java.lang.Object> toIterable(ExecutionContext context, ExecutionStrategyParameters parameters, java.lang.Object result)
protected GraphQLFieldDefinition getFieldDef(ExecutionContext executionContext, ExecutionStrategyParameters parameters, Field field)
Field
executionContext
- contains the top level execution parametersparameters
- contains the parameters holding the fields to be executed and source objectfield
- the field to find the definition ofGraphQLFieldDefinition
protected GraphQLFieldDefinition getFieldDef(GraphQLSchema schema, GraphQLObjectType parentType, Field field)
Field
schema
- the schema in playparentType
- the parent type of the fieldfield
- the field to find the definition ofGraphQLFieldDefinition
protected void assertNonNullFieldPrecondition(NonNullableFieldWasNullException e) throws NonNullableFieldWasNullException
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.
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 exceptionNonNullableFieldWasNullException
- if a non null field resolves to a null valueprotected void assertNonNullFieldPrecondition(NonNullableFieldWasNullException e, java.util.concurrent.CompletableFuture<?> completableFuture) throws NonNullableFieldWasNullException
NonNullableFieldWasNullException
protected ExecutionResult handleNonNullException(ExecutionContext executionContext, java.util.concurrent.CompletableFuture<ExecutionResult> result, java.lang.Throwable e)
protected ExecutionStepInfo createExecutionStepInfo(ExecutionContext executionContext, ExecutionStrategyParameters parameters, GraphQLFieldDefinition fieldDefinition, GraphQLObjectType fieldContainer)
executionContext
- the execution context in playparameters
- contains the parameters holding the fields to be executed and source objectfieldDefinition
- the field definition to build type info forfieldContainer
- the field containerpublic static java.lang.String mkNameForPath(Field currentField)
public static java.lang.String mkNameForPath(MergedField mergedField)
public static java.lang.String mkNameForPath(java.util.List<Field> currentField)