All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
graphql.nadel.enginekt.transform.hydration.batch.NadelBatchHydrationInputBuilder.kt Maven / Gradle / Ivy
Go to download
Nadel is a Java library that combines multiple GrahpQL services together into one API.
package graphql.nadel.enginekt.transform.hydration.batch
import graphql.nadel.enginekt.NadelEngineExecutionHooks
import graphql.nadel.enginekt.blueprint.NadelBatchHydrationFieldInstruction
import graphql.nadel.enginekt.blueprint.hydration.NadelBatchHydrationMatchStrategy
import graphql.nadel.enginekt.blueprint.hydration.NadelHydrationActorInputDef
import graphql.nadel.enginekt.transform.artificial.NadelAliasHelper
import graphql.nadel.enginekt.transform.result.json.JsonNode
import graphql.nadel.enginekt.transform.result.json.JsonNodeExtractor
import graphql.nadel.enginekt.util.emptyOrSingle
import graphql.nadel.enginekt.util.flatten
import graphql.nadel.enginekt.util.javaValueToAstValue
import graphql.nadel.enginekt.util.mapFrom
import graphql.nadel.hooks.ServiceExecutionHooks
import graphql.normalized.ExecutableNormalizedField
import graphql.normalized.NormalizedInputValue
import graphql.schema.GraphQLTypeUtil
/**
* README
*
* Please ensure that the batch arguments are ordered according to the input.
* This is required for [NadelBatchHydrationMatchStrategy.MatchIndex].
*/
internal object NadelBatchHydrationInputBuilder {
fun getInputValueBatches(
aliasHelper: NadelAliasHelper,
instruction: NadelBatchHydrationFieldInstruction,
hydrationField: ExecutableNormalizedField,
parentNodes: List,
hooks: ServiceExecutionHooks,
): List> {
val nonBatchArgs = getNonBatchInputValues(instruction, hydrationField)
val batchArgs = getBatchInputValues(instruction, parentNodes, aliasHelper, hooks)
return batchArgs.map { nonBatchArgs + it }
}
private fun getNonBatchInputValues(
instruction: NadelBatchHydrationFieldInstruction,
hydrationField: ExecutableNormalizedField,
): Map {
return mapFrom(
instruction.actorInputValueDefs.mapNotNull { actorFieldArg ->
when (val valueSource = actorFieldArg.valueSource) {
is NadelHydrationActorInputDef.ValueSource.ArgumentValue -> {
when (val argValue = hydrationField.normalizedArguments[valueSource.argumentName]) {
null -> null
else -> actorFieldArg to argValue
}
}
// These are batch values, ignore them
is NadelHydrationActorInputDef.ValueSource.FieldResultValue -> null
}
},
)
}
private fun getBatchInputValues(
instruction: NadelBatchHydrationFieldInstruction,
parentNodes: List,
aliasHelper: NadelAliasHelper,
hooks: ServiceExecutionHooks,
): List> {
val batchSize = instruction.batchSize
val (batchInputDef, batchInputValueSource) = getBatchInputDef(instruction) ?: return emptyList()
val actorBatchArgDef = instruction.actorFieldDef.getArgument(batchInputDef.name)
val args = getFieldResultValues(batchInputValueSource, parentNodes, aliasHelper)
val partitionArgumentList = when (hooks) {
is NadelEngineExecutionHooks -> hooks.partitionBatchHydrationArgumentList(args, instruction)
else -> listOf(args)
}
return partitionArgumentList.flatMap { it.chunked(size = batchSize) }
.map { chunk ->
batchInputDef to NormalizedInputValue(
GraphQLTypeUtil.simplePrint(actorBatchArgDef.type),
javaValueToAstValue(chunk),
)
}
}
/**
* TODO: this should really be baked into the [instruction] and also be mandatory…
*
* Get the input def that is collated together to form the batch input.
*
* e.g. for a schema
*
* ```graphql
* type User {
* friendId: [ID]
* friend(acquaintances: Boolean! = false): User @hydrated(
* from: "usersByIds",
* arguments: [
* {name: "userIds", valueFromField: "friendId"}
* {name: "acquaintances", valueFromArgument: "acquaintances"}
* ],
* )
* }
* ```
*
* then the input def would be the `userIds`.
*/
internal fun getBatchInputDef(
instruction: NadelBatchHydrationFieldInstruction,
): Pair? {
return instruction.actorInputValueDefs
.asSequence()
.mapNotNull {
when (val valueSource = it.valueSource) {
is NadelHydrationActorInputDef.ValueSource.FieldResultValue -> it to valueSource
else -> null
}
}
.emptyOrSingle()
}
private fun getFieldResultValues(
valueSource: NadelHydrationActorInputDef.ValueSource.FieldResultValue,
parentNodes: List,
aliasHelper: NadelAliasHelper,
): List {
return parentNodes.flatMap { parentNode ->
getFieldResultValues(
valueSource = valueSource,
parentNode = parentNode,
aliasHelper = aliasHelper,
filterNull = true,
)
}
}
internal fun getFieldResultValues(
valueSource: NadelHydrationActorInputDef.ValueSource.FieldResultValue,
parentNode: JsonNode,
aliasHelper: NadelAliasHelper,
filterNull: Boolean,
): List {
val nodes = JsonNodeExtractor.getNodesAt(
rootNode = parentNode,
queryPath = aliasHelper.getQueryPath(valueSource.queryPathToField),
flatten = true,
)
return nodes
.asSequence()
.map { it.value }
.flatten(recursively = true)
.let {
if (filterNull) {
it.filterNotNull()
} else {
it
}
}
.toList()
}
}