graphql.nadel.enginekt.transform.query.NadelFieldToService.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of nadel-engine-nextgen Show documentation
Show all versions of nadel-engine-nextgen Show documentation
Nadel is a Java library that combines multiple GrahpQL services together into one API.
The newest version!
package graphql.nadel.enginekt.transform.query
import graphql.introspection.Introspection
import graphql.nadel.Service
import graphql.nadel.enginekt.blueprint.IntrospectionService
import graphql.nadel.enginekt.blueprint.NadelIntrospectionRunnerFactory
import graphql.nadel.enginekt.blueprint.NadelOverallExecutionBlueprint
import graphql.nadel.enginekt.transform.query.NadelNamespacedFields.isNamespacedField
import graphql.nadel.enginekt.util.copyWithChildren
import graphql.nadel.enginekt.util.makeFieldCoordinates
import graphql.nadel.util.NamespacedUtil.serviceOwnsNamespacedField
import graphql.normalized.ExecutableNormalizedField
import graphql.normalized.ExecutableNormalizedOperation
import graphql.schema.GraphQLSchema
internal class NadelFieldToService(
private val querySchema: GraphQLSchema,
private val overallExecutionBlueprint: NadelOverallExecutionBlueprint,
introspectionRunnerFactory: NadelIntrospectionRunnerFactory,
private val dynamicServiceResolution: DynamicServiceResolution,
private val services: Map,
) {
private val introspectionService = IntrospectionService(querySchema, introspectionRunnerFactory)
fun getServicesForTopLevelFields(query: ExecutableNormalizedOperation): List {
return query.topLevelFields.flatMap { topLevelField ->
if (isNamespacedField(topLevelField)) {
getServicePairsForNamespacedFields(topLevelField)
} else {
listOf(getServicePairFor(field = topLevelField))
}
}
}
/**
* Returns the dynamically resolved service for the field, if it is annotated with @dynamicServiceResolution,
* otherwise returns the originalService.
*/
fun resolveDynamicService(
field: ExecutableNormalizedField,
originalService: Service,
): Service {
return if (dynamicServiceResolution.needsDynamicServiceResolution(field)) {
dynamicServiceResolution.resolveServiceForField(field)
} else {
originalService
}
}
private fun getServicePairsForNamespacedFields(
topLevelField: ExecutableNormalizedField,
): List {
return topLevelField.children
.groupBy(::getServiceForNamespacedField)
.map { (service, childTopLevelFields) ->
NadelFieldAndService(
field = topLevelField.copyWithChildren(childTopLevelFields),
service = service,
)
}
}
private fun getServicePairFor(field: ExecutableNormalizedField): NadelFieldAndService {
return NadelFieldAndService(
field = field,
service = getService(field),
)
}
private fun getServiceForNamespacedField(overallField: ExecutableNormalizedField): Service {
if (overallField.name == Introspection.TypeNameMetaFieldDef.name) {
// TODO: replace this logic with internal handling via IntrospectionService
// See https://github.com/atlassian-labs/nadel/pull/324
val operationTypeName = overallField.objectTypeNames.single()
return services.values.first { service ->
serviceOwnsNamespacedField(operationTypeName, service)
}
}
return getService(overallField)
}
private fun getService(overallField: ExecutableNormalizedField): Service {
if (overallField.name.startsWith("__")) {
return introspectionService
}
val operationTypeName = overallField.objectTypeNames.single()
val fieldCoordinates = makeFieldCoordinates(operationTypeName, overallField.name)
return overallExecutionBlueprint.getServiceOwning(fieldCoordinates)
?: error("Unable to find service for field at: $fieldCoordinates")
}
private fun isNamespacedField(field: ExecutableNormalizedField): Boolean {
return isNamespacedField(field, overallExecutionBlueprint.engineSchema)
}
}
data class NadelFieldAndService(
val field: ExecutableNormalizedField,
val service: Service,
)