Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge execution hooks #471

Merged
merged 1 commit into from
Nov 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading