Skip to content

Commit

Permalink
Merge execution hooks (#471)
Browse files Browse the repository at this point in the history
  • Loading branch information
gnawf committed Nov 13, 2023
1 parent dd056c3 commit fa44500
Show file tree
Hide file tree
Showing 16 changed files with 122 additions and 150 deletions.
10 changes: 5 additions & 5 deletions lib/src/main/java/graphql/nadel/Nadel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import graphql.language.Document
import graphql.nadel.engine.blueprint.NadelDefaultIntrospectionRunner
import graphql.nadel.engine.blueprint.NadelIntrospectionRunnerFactory
import graphql.nadel.engine.transform.NadelTransform
import graphql.nadel.hooks.ServiceExecutionHooks
import graphql.nadel.hooks.NadelExecutionHooks
import graphql.nadel.instrumentation.NadelInstrumentation
import graphql.nadel.instrumentation.parameters.NadelInstrumentationCreateStateParameters
import graphql.nadel.instrumentation.parameters.NadelInstrumentationQueryExecutionParameters
Expand Down Expand Up @@ -227,7 +227,7 @@ class Nadel private constructor(

class Builder {
private var instrumentation: NadelInstrumentation = object : NadelInstrumentation {}
private var serviceExecutionHooks: ServiceExecutionHooks = object : ServiceExecutionHooks {}
private var executionHooks: NadelExecutionHooks = object : NadelExecutionHooks {}
private var preparsedDocumentProvider: PreparsedDocumentProvider = NoOpPreparsedDocumentProvider.INSTANCE
private var executionIdProvider = ExecutionIdProvider.DEFAULT_EXECUTION_ID_PROVIDER
private var transforms = emptyList<NadelTransform<out Any>>()
Expand Down Expand Up @@ -327,8 +327,8 @@ class Nadel private constructor(
return this
}

fun serviceExecutionHooks(serviceExecutionHooks: ServiceExecutionHooks): Builder {
this.serviceExecutionHooks = serviceExecutionHooks
fun executionHooks(executionHooks: NadelExecutionHooks): Builder {
this.executionHooks = executionHooks
return this
}

Expand Down Expand Up @@ -357,7 +357,7 @@ class Nadel private constructor(
engineSchema = engineSchema,
querySchema = querySchema,
instrumentation = instrumentation,
serviceExecutionHooks = serviceExecutionHooks,
executionHooks = executionHooks,
executionIdProvider = executionIdProvider,
services = services,
transforms = transforms,
Expand Down
8 changes: 4 additions & 4 deletions lib/src/main/java/graphql/nadel/NextgenEngine.kt
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import graphql.nadel.engine.util.newServiceExecutionResult
import graphql.nadel.engine.util.provide
import graphql.nadel.engine.util.singleOfType
import graphql.nadel.engine.util.strictAssociateBy
import graphql.nadel.hooks.ServiceExecutionHooks
import graphql.nadel.hooks.NadelExecutionHooks
import graphql.nadel.instrumentation.NadelInstrumentation
import graphql.nadel.instrumentation.parameters.ErrorData
import graphql.nadel.instrumentation.parameters.ErrorType.ServiceExecutionError
Expand Down Expand Up @@ -63,7 +63,7 @@ internal class NextgenEngine(
private val engineSchema: GraphQLSchema,
private val querySchema: GraphQLSchema,
private val instrumentation: NadelInstrumentation,
private val serviceExecutionHooks: ServiceExecutionHooks,
private val executionHooks: NadelExecutionHooks,
private val executionIdProvider: ExecutionIdProvider,
maxQueryDepth: Int,
services: List<Service>,
Expand All @@ -84,7 +84,7 @@ internal class NextgenEngine(
private val resultTransformer = NadelResultTransformer(overallExecutionBlueprint)
private val dynamicServiceResolution = DynamicServiceResolution(
engineSchema = engineSchema,
serviceExecutionHooks = serviceExecutionHooks,
executionHooks = executionHooks,
services = services,
)
private val fieldToService = NadelFieldToService(
Expand Down Expand Up @@ -149,7 +149,7 @@ internal class NextgenEngine(
val executionContext = NadelExecutionContext(
executionInput,
query,
serviceExecutionHooks,
executionHooks,
executionHints,
instrumentationState,
timer,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@ import graphql.nadel.NadelExecutionHints
import graphql.nadel.Service
import graphql.nadel.engine.instrumentation.NadelInstrumentationTimer
import graphql.nadel.hooks.CreateServiceContextParams
import graphql.nadel.hooks.ServiceExecutionHooks
import graphql.nadel.hooks.NadelExecutionHooks
import graphql.normalized.ExecutableNormalizedOperation
import java.util.concurrent.CompletableFuture
import java.util.concurrent.ConcurrentHashMap

data class NadelExecutionContext internal constructor(
val executionInput: ExecutionInput,
val query: ExecutableNormalizedOperation,
internal val hooks: ServiceExecutionHooks,
internal val hooks: NadelExecutionHooks,
val hints: NadelExecutionHints,
val instrumentationState: InstrumentationState?,
internal val timer: NadelInstrumentationTimer,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import graphql.nadel.engine.util.deepClone
import graphql.nadel.engine.util.resolveObjectTypes
import graphql.nadel.engine.util.toBuilder
import graphql.nadel.engine.util.unwrapAll
import graphql.nadel.hooks.ServiceExecutionHooks
import graphql.nadel.hooks.NadelExecutionHooks
import graphql.normalized.ExecutableNormalizedField
import graphql.normalized.NormalizedInputValue

Expand Down Expand Up @@ -49,7 +49,7 @@ internal object NadelHydrationFieldsBuilder {
aliasHelper: NadelAliasHelper,
hydratedField: ExecutableNormalizedField,
parentNodes: List<JsonNode>,
hooks: ServiceExecutionHooks,
hooks: NadelExecutionHooks,
userContext: Any?,
): List<ExecutableNormalizedField> {
val argBatches = NadelBatchHydrationInputBuilder.getInputValueBatches(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import graphql.nadel.NextgenEngine
import graphql.nadel.Service
import graphql.nadel.ServiceExecutionHydrationDetails
import graphql.nadel.ServiceExecutionResult
import graphql.nadel.engine.NadelEngineExecutionHooks
import graphql.nadel.engine.NadelExecutionContext
import graphql.nadel.engine.blueprint.NadelGenericHydrationInstruction
import graphql.nadel.engine.blueprint.NadelHydrationFieldInstruction
Expand All @@ -29,7 +28,7 @@ import graphql.nadel.engine.transform.result.json.JsonNodes
import graphql.nadel.engine.util.emptyOrSingle
import graphql.nadel.engine.util.queryPath
import graphql.nadel.engine.util.toBuilder
import graphql.nadel.hooks.ServiceExecutionHooks
import graphql.nadel.hooks.NadelExecutionHooks
import graphql.normalized.ExecutableNormalizedField
import graphql.schema.FieldCoordinates
import kotlinx.coroutines.Deferred
Expand Down Expand Up @@ -277,26 +276,14 @@ internal class NadelHydrationTransform(
private fun getHydrationFieldInstruction(
state: State,
instructions: List<NadelHydrationFieldInstruction>,
hooks: ServiceExecutionHooks,
hooks: NadelExecutionHooks,
parentNode: JsonNode,
): NadelHydrationFieldInstruction? {
return when (instructions.size) {
1 -> instructions.single()
else -> {
if (hooks is NadelEngineExecutionHooks) {
hooks.getHydrationInstruction(
instructions,
parentNode,
state.aliasHelper,
state.executionContext.userContext
)
} else {
error(
"Cannot decide which hydration instruction should be used. Provided ServiceExecutionHooks has " +
"to be of type NadelEngineExecutionHooks"
)
}
}
}
return hooks.getHydrationInstruction(
instructions,
parentNode,
state.aliasHelper,
state.executionContext.userContext
)
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package graphql.nadel.engine.transform.hydration.batch

import graphql.nadel.engine.NadelEngineExecutionHooks
import graphql.nadel.engine.blueprint.NadelBatchHydrationFieldInstruction
import graphql.nadel.engine.blueprint.hydration.NadelBatchHydrationMatchStrategy
import graphql.nadel.engine.blueprint.hydration.NadelHydrationActorInputDef
Expand All @@ -12,7 +11,7 @@ import graphql.nadel.engine.util.flatten
import graphql.nadel.engine.util.javaValueToAstValue
import graphql.nadel.engine.util.makeNormalizedInputValue
import graphql.nadel.engine.util.mapFrom
import graphql.nadel.hooks.ServiceExecutionHooks
import graphql.nadel.hooks.NadelExecutionHooks
import graphql.normalized.ExecutableNormalizedField
import graphql.normalized.NormalizedInputValue
import graphql.schema.GraphQLTypeUtil
Expand All @@ -29,7 +28,7 @@ internal object NadelBatchHydrationInputBuilder {
instruction: NadelBatchHydrationFieldInstruction,
hydrationField: ExecutableNormalizedField,
parentNodes: List<JsonNode>,
hooks: ServiceExecutionHooks,
hooks: NadelExecutionHooks,
userContext: Any?,
): List<Map<NadelHydrationActorInputDef, NormalizedInputValue>> {
val nonBatchArgs = getNonBatchInputValues(instruction, hydrationField)
Expand Down Expand Up @@ -73,7 +72,7 @@ internal object NadelBatchHydrationInputBuilder {
instruction: NadelBatchHydrationFieldInstruction,
parentNodes: List<JsonNode>,
aliasHelper: NadelAliasHelper,
hooks: ServiceExecutionHooks,
hooks: NadelExecutionHooks,
userContext: Any?,
): List<Pair<NadelHydrationActorInputDef, NormalizedInputValue>> {
val batchSize = instruction.batchSize
Expand All @@ -83,10 +82,7 @@ internal object NadelBatchHydrationInputBuilder {

val args = getFieldResultValues(batchInputValueSource, parentNodes, aliasHelper)

val partitionArgumentList = when (hooks) {
is NadelEngineExecutionHooks -> hooks.partitionBatchHydrationArgumentList(args, instruction, userContext)
else -> listOf(args)
}
val partitionArgumentList = hooks.partitionBatchHydrationArgumentList(args, instruction, userContext)

return partitionArgumentList.flatMap { it.chunked(size = batchSize) }
.map { chunk ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package graphql.nadel.engine.transform.hydration.batch
import graphql.nadel.NextgenEngine
import graphql.nadel.ServiceExecutionHydrationDetails
import graphql.nadel.ServiceExecutionResult
import graphql.nadel.engine.NadelEngineExecutionHooks
import graphql.nadel.engine.blueprint.NadelBatchHydrationFieldInstruction
import graphql.nadel.engine.blueprint.NadelOverallExecutionBlueprint
import graphql.nadel.engine.blueprint.hydration.NadelBatchHydrationMatchStrategy
Expand Down Expand Up @@ -166,12 +165,6 @@ internal class NadelBatchHydrator(
instructions: List<NadelBatchHydrationFieldInstruction>,
parentNode: JsonNode,
): NadelBatchHydrationFieldInstruction? {
if (state.executionContext.hooks !is NadelEngineExecutionHooks) {
error(
"Cannot decide which hydration instruction should be used. " +
"Provided ServiceExecutionHooks has to be of type NadelEngineExecutionHooks"
)
}
return state.executionContext.hooks.getHydrationInstruction(
instructions,
parentNode,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ import graphql.nadel.Service
import graphql.nadel.engine.util.queryPath
import graphql.nadel.engine.util.toGraphQLErrorException
import graphql.nadel.engine.util.unwrapNonNull
import graphql.nadel.hooks.ServiceExecutionHooks
import graphql.nadel.hooks.NadelExecutionHooks
import graphql.nadel.schema.NadelDirectives.dynamicServiceDirectiveDefinition
import graphql.normalized.ExecutableNormalizedField
import graphql.schema.GraphQLInterfaceType
import graphql.schema.GraphQLSchema

internal class DynamicServiceResolution(
private val engineSchema: GraphQLSchema,
private val serviceExecutionHooks: ServiceExecutionHooks,
private val executionHooks: NadelExecutionHooks,
private val services: List<Service>,
) {

Expand All @@ -39,7 +39,7 @@ internal class DynamicServiceResolution(
* Resolves the service for a field
*/
fun resolveServiceForField(field: ExecutableNormalizedField): Service {
val serviceOrError = serviceExecutionHooks.resolveServiceForField(services, field)
val serviceOrError = executionHooks.resolveServiceForField(services, field)
?: throw GraphqlErrorException.newErrorException()
.message("Could not resolve service for field '${field.name}'")
.path(field.queryPath.segments)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,55 @@
package graphql.nadel.engine
package graphql.nadel.hooks

import graphql.nadel.Service
import graphql.nadel.engine.blueprint.NadelBatchHydrationFieldInstruction
import graphql.nadel.engine.blueprint.NadelGenericHydrationInstruction
import graphql.nadel.engine.transform.artificial.NadelAliasHelper
import graphql.nadel.engine.transform.result.json.JsonNode
import graphql.nadel.hooks.ServiceExecutionHooks
import graphql.normalized.ExecutableNormalizedField
import java.util.concurrent.CompletableFuture

/**
* These hooks allow you to change the way service execution happens
*/
interface NadelExecutionHooks {
/**
* Called per top level field for a service. This allows you to create a "context" object that will be passed into further calls.
*
* @param params the parameters to this call
* @return an async context object of your choosing
*/
fun createServiceContext(params: CreateServiceContextParams): CompletableFuture<Any?> {
return CompletableFuture.completedFuture(null)
}

/**
* Called to resolve the service that should be used to fetch data for a field that uses dynamic service resolution.
*
*
* There are 2 versions of this method. One passing an [ExecutionStepInfo], which is used by the CurrentGen
* engine, and another passing [ExecutableNormalizedField], used by the NextGen engine. During the transition
* between Current and NextGen, implementations of [NadelExecutionHooks] will have to implement both
* versions of this method.
*
* @param services a collection of all services registered on Nadel
* @param executableNormalizedField object containing data about the field being executed
* @return the Service that should be used to fetch data for that field or an error that was raised when trying to resolve the service.
*/
fun resolveServiceForField(
services: List<Service>,
executableNormalizedField: ExecutableNormalizedField,
): ServiceOrError? {
return null
}

interface NadelEngineExecutionHooks : ServiceExecutionHooks {
fun <T : NadelGenericHydrationInstruction> getHydrationInstruction(
instructions: List<T>,
parentNode: JsonNode,
aliasHelper: NadelAliasHelper,
userContext: Any?,
): T?
): T? {
return instructions.single()
}

/**
* This method should be used when the list of hydration arguments needs to be split in batches. The batches will be
Expand Down
40 changes: 0 additions & 40 deletions lib/src/main/java/graphql/nadel/hooks/ServiceExecutionHooks.kt

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
package graphql.nadel.tests.hooks

import graphql.nadel.Nadel
import graphql.nadel.engine.NadelEngineExecutionHooks
import graphql.nadel.engine.blueprint.NadelBatchHydrationFieldInstruction
import graphql.nadel.engine.blueprint.NadelGenericHydrationInstruction
import graphql.nadel.engine.transform.artificial.NadelAliasHelper
import graphql.nadel.engine.transform.result.json.JsonNode
import graphql.nadel.hooks.NadelExecutionHooks
import graphql.nadel.tests.EngineTestHook
import graphql.nadel.tests.UseHook

private class BatchHydrationHooks : NadelEngineExecutionHooks {
private class BatchHydrationHooks : NadelExecutionHooks {
override fun <T : NadelGenericHydrationInstruction> getHydrationInstruction(
instructions: List<T>,
parentNode: JsonNode,
Expand All @@ -33,6 +33,6 @@ private class BatchHydrationHooks : NadelEngineExecutionHooks {
@UseHook
class `batching-of-hydration-list-with-partition` : EngineTestHook {
override fun makeNadel(builder: Nadel.Builder): Nadel.Builder {
return builder.serviceExecutionHooks(BatchHydrationHooks())
return builder.executionHooks(BatchHydrationHooks())
}
}
Loading

0 comments on commit fa44500

Please sign in to comment.