Class ExecutionStrategy
- java.lang.Object
-
- graphql.execution.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 theGraphQLFieldDefinition
. 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.
-
-
Field Summary
Fields Modifier and Type Field Description protected DataFetcherExceptionHandler
dataFetcherExceptionHandler
protected ExecutionStepInfoFactory
executionStepInfoFactory
protected FieldCollector
fieldCollector
-
Constructor Summary
Constructors Modifier Constructor Description protected
ExecutionStrategy()
The default execution strategy constructor uses theSimpleDataFetcherExceptionHandler
for data fetching errors.protected
ExecutionStrategy(DataFetcherExceptionHandler dataFetcherExceptionHandler)
The consumers of the execution strategy can pass in aDataFetcherExceptionHandler
to better decide what do when a data fetching error happens
-
Method Summary
All Methods Static Methods Instance Methods Abstract Methods Concrete Methods Modifier and Type Method 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 theGraphQLEnumType
by asking that enum type to coerce the object into a valid valueprotected 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 valueprotected 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 theGraphQLScalarType
by asking that scalar type to coerce the object into a valid valueprotected ExecutionStepInfo
createExecutionStepInfo(ExecutionContext executionContext, ExecutionStrategyParameters parameters, GraphQLFieldDefinition fieldDefinition, GraphQLObjectType fieldContainer)
Builds the type info hierarchy for the current fieldabstract 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 theDataFetcher
associated with the fieldGraphQLFieldDefinition
.protected GraphQLFieldDefinition
getFieldDef(ExecutionContext executionContext, ExecutionStrategyParameters parameters, Field field)
Called to discover the field definition give the current parameters and the ASTField
protected GraphQLFieldDefinition
getFieldDef(GraphQLSchema schema, GraphQLObjectType parentType, Field field)
Called to discover the field definition give the current parameters and the ASTField
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(MergedField mergedField)
static java.lang.String
mkNameForPath(Field currentField)
static java.lang.String
mkNameForPath(java.util.List<Field> currentField)
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 oneprotected FetchedValue
unboxPossibleDataFetcherResult(ExecutionContext executionContext, ExecutionStrategyParameters parameters, java.lang.Object result)
-
-
-
Field Detail
-
fieldCollector
protected final FieldCollector fieldCollector
-
executionStepInfoFactory
protected final ExecutionStepInfoFactory executionStepInfoFactory
-
dataFetcherExceptionHandler
protected final DataFetcherExceptionHandler dataFetcherExceptionHandler
-
-
Constructor Detail
-
ExecutionStrategy
protected ExecutionStrategy()
The default execution strategy constructor uses theSimpleDataFetcherExceptionHandler
for data fetching errors.
-
ExecutionStrategy
protected ExecutionStrategy(DataFetcherExceptionHandler dataFetcherExceptionHandler)
The consumers of the execution strategy can pass in aDataFetcherExceptionHandler
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
-
execute
public abstract java.util.concurrent.CompletableFuture<ExecutionResult> execute(ExecutionContext executionContext, ExecutionStrategyParameters parameters) throws NonNullableFieldWasNullException
This is the entry point to an execution strategy. It will be passed the fields to execute and get values for.- Parameters:
executionContext
- contains the top level execution parametersparameters
- 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
-
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 completedExecutionResult
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 parametersparameters
- 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 completedFieldValueInfo
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 parametersparameters
- contains the parameters holding the fields to be executed and source object- Returns:
- a promise to a
FieldValueInfo
- Throws:
NonNullableFieldWasNullException
- in theFieldValueInfo.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 theDataFetcher
associated with the fieldGraphQLFieldDefinition
.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 parametersparameters
- 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
-
getNormalizedField
protected java.util.function.Supplier<ExecutableNormalizedField> getNormalizedField(ExecutionContext executionContext, ExecutionStrategyParameters parameters, java.util.function.Supplier<ExecutionStepInfo> executionStepInfo)
-
unboxPossibleDataFetcherResult
protected FetchedValue unboxPossibleDataFetcherResult(ExecutionContext executionContext, ExecutionStrategyParameters parameters, java.lang.Object result)
-
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 parametersparameters
- contains the parameters holding the fields to be executed and source objectfetchedValue
- the fetched raw value- Returns:
- a
FieldValueInfo
- Throws:
NonNullableFieldWasNullException
- in theFieldValueInfo.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 parametersparameters
- 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
-
completeValueForNull
protected java.util.concurrent.CompletableFuture<ExecutionResult> completeValueForNull(ExecutionContext executionContext, ExecutionStrategyParameters parameters)
-
completeValueForList
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. This iterates the values and callscompleteValue(ExecutionContext, ExecutionStrategyParameters)
for each value.- Parameters:
executionContext
- contains the top level execution parametersparameters
- contains the parameters holding the fields to be executed and source objectresult
- the result to complete, raw result- Returns:
- a
FieldValueInfo
-
completeValueForList
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. This iterates the values and callscompleteValue(ExecutionContext, ExecutionStrategyParameters)
for each value.- Parameters:
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 null- Returns:
- a
FieldValueInfo
-
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 theGraphQLScalarType
by asking that scalar type to coerce the object into a valid value- Parameters:
executionContext
- 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 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 theGraphQLEnumType
by asking that enum type to coerce the object into a valid value- Parameters:
executionContext
- 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 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 parametersparameters
- contains the parameters holding the fields to be executed and source objectresolvedObjectType
- the resolved object typeresult
- 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
-
resolveType
protected GraphQLObjectType resolveType(ExecutionContext executionContext, ExecutionStrategyParameters parameters, GraphQLType fieldType)
-
toIterable
protected java.lang.Iterable<java.lang.Object> toIterable(ExecutionContext context, ExecutionStrategyParameters parameters, java.lang.Object result)
-
getFieldDef
protected GraphQLFieldDefinition getFieldDef(ExecutionContext executionContext, ExecutionStrategyParameters parameters, Field field)
Called to discover the field definition give the current parameters and the ASTField
- Parameters:
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 of- Returns:
- a
GraphQLFieldDefinition
-
getFieldDef
protected GraphQLFieldDefinition getFieldDef(GraphQLSchema schema, GraphQLObjectType parentType, Field field)
Called to discover the field definition give the current parameters and the ASTField
- Parameters:
schema
- the schema in playparentType
- the parent type of the fieldfield
- the field to find the definition of- Returns:
- a
GraphQLFieldDefinition
-
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
-
assertNonNullFieldPrecondition
protected void assertNonNullFieldPrecondition(NonNullableFieldWasNullException e, java.util.concurrent.CompletableFuture<?> completableFuture) throws NonNullableFieldWasNullException
- Throws:
NonNullableFieldWasNullException
-
handleNonNullException
protected ExecutionResult handleNonNullException(ExecutionContext executionContext, java.util.concurrent.CompletableFuture<ExecutionResult> result, java.lang.Throwable e)
-
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 playparameters
- contains the parameters holding the fields to be executed and source objectfieldDefinition
- the field definition to build type info forfieldContainer
- 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)
-
-