commonMain.com.apollographql.execution.internal.introspection.kt Maven / Gradle / Ivy
The newest version!
package com.apollographql.execution.internal
import com.apollographql.apollo.api.getOrElse
import com.apollographql.apollo.ast.GQLDirectiveDefinition
import com.apollographql.apollo.ast.GQLEnumTypeDefinition
import com.apollographql.apollo.ast.GQLEnumValueDefinition
import com.apollographql.apollo.ast.GQLFieldDefinition
import com.apollographql.apollo.ast.GQLInputObjectTypeDefinition
import com.apollographql.apollo.ast.GQLInputValueDefinition
import com.apollographql.apollo.ast.GQLInterfaceTypeDefinition
import com.apollographql.apollo.ast.GQLListType
import com.apollographql.apollo.ast.GQLNamedType
import com.apollographql.apollo.ast.GQLNonNullType
import com.apollographql.apollo.ast.GQLObjectTypeDefinition
import com.apollographql.apollo.ast.GQLScalarTypeDefinition
import com.apollographql.apollo.ast.GQLType
import com.apollographql.apollo.ast.GQLTypeDefinition
import com.apollographql.apollo.ast.GQLUnionTypeDefinition
import com.apollographql.apollo.ast.Schema
import com.apollographql.apollo.ast.builtinDefinitions
import com.apollographql.apollo.ast.findDeprecationReason
import com.apollographql.apollo.ast.findSpecifiedBy
import com.apollographql.apollo.ast.toUtf8
import com.apollographql.execution.Resolver
import com.apollographql.execution.StringCoercing
private inline fun Any?.cast() = this as T
private object SchemaObject
internal val introspectionCoercings = mapOf(
"__TypeKind" to StringCoercing,
"__DirectiveLocation" to StringCoercing,
)
internal fun introspectionResolvers(schema: Schema): Map {
return mapOf(
schema.queryTypeDefinition.name to mapOf(
"__schema" to Resolver { SchemaObject },
"__type" to Resolver {
val name = it.getRequiredArgument("name").cast()
IntrospectionType(GQLNamedType(name = name), schema)
}
),
"__Schema" to mapOf(
"description" to Resolver { null },
"types" to Resolver {
schema.typeDefinitions.keys.map {
IntrospectionType(GQLNamedType(name = it), schema)
}
},
"queryType" to Resolver { IntrospectionType(GQLNamedType(name = schema.queryTypeDefinition.name), schema) },
"mutationType" to Resolver {
schema.mutationTypeDefinition?.let {
IntrospectionType(
GQLNamedType(name = it.name),
schema
)
}
},
"subscriptionType" to Resolver {
schema.subscriptionTypeDefinition?.let {
IntrospectionType(
GQLNamedType(name = it.name),
schema
)
}
},
"directives" to Resolver { schema.directiveDefinitions.values.toList() },
),
"__Type" to mapOf(
"kind" to Resolver {
val type = it.parentObject.cast()
when (type.typeDefinition) {
is GQLEnumTypeDefinition -> __TypeKind.ENUM.name
is GQLInputObjectTypeDefinition -> __TypeKind.INPUT_OBJECT.name
is GQLInterfaceTypeDefinition -> __TypeKind.INTERFACE.name
is GQLObjectTypeDefinition -> __TypeKind.OBJECT.name
is GQLScalarTypeDefinition -> __TypeKind.SCALAR.name
is GQLUnionTypeDefinition -> __TypeKind.UNION.name
null -> {
when (type.type) {
is GQLNonNullType -> __TypeKind.NON_NULL.name
is GQLListType -> __TypeKind.LIST.name
else -> null
}
}
}
},
"name" to Resolver {
val typeDefinition = it.parentObject.cast().typeDefinition
typeDefinition?.name
},
"description" to Resolver {
val typeDefinition = it.parentObject.cast().typeDefinition
typeDefinition?.description
},
"specifiedByURL" to Resolver {
val typeDefinition = it.parentObject.cast().typeDefinition
typeDefinition?.directives?.findSpecifiedBy()
},
"fields" to Resolver {
val typeDefinition = it.parentObject.cast().typeDefinition
val definitions = when (typeDefinition) {
is GQLObjectTypeDefinition -> typeDefinition.fields
is GQLInterfaceTypeDefinition -> typeDefinition.fields
else -> null
}
if (definitions == null) {
return@Resolver null
}
val includeDeprecated = it.getRequiredArgument("includeDeprecated").cast()
definitions.filter {
includeDeprecated || it.directives.findDeprecationReason() == null
}
},
"interfaces" to Resolver {
val typeDefinition = it.parentObject.cast().typeDefinition
val interfaces = when (typeDefinition) {
is GQLObjectTypeDefinition -> typeDefinition.implementsInterfaces.map { IntrospectionType(GQLNamedType(null, it), schema.typeDefinition(it)) }
is GQLInterfaceTypeDefinition -> typeDefinition.implementsInterfaces.map { IntrospectionType(GQLNamedType(null, it), schema.typeDefinition(it)) }
else -> null
}
if (interfaces == null) {
return@Resolver null
}
interfaces
},
"possibleTypes" to Resolver {
val typeDefinition = it.parentObject.cast().typeDefinition
val possibleTypes = when (typeDefinition) {
is GQLInterfaceTypeDefinition -> schema.possibleTypes(typeDefinition.name)
is GQLUnionTypeDefinition -> schema.possibleTypes(typeDefinition.name)
else -> null
}
if (possibleTypes == null) {
return@Resolver null
}
possibleTypes.map { IntrospectionType(GQLNamedType(null, it), schema.typeDefinition(it)) }
},
"enumValues" to Resolver {
val typeDefinition = it.parentObject.cast().typeDefinition
if (typeDefinition !is GQLEnumTypeDefinition) {
return@Resolver null
}
val includeDeprecated = it.getRequiredArgument("includeDeprecated").cast()
typeDefinition.enumValues.filter {
includeDeprecated || it.directives.findDeprecationReason() == null
}
},
"inputFields" to Resolver {
val typeDefinition = it.parentObject.cast().typeDefinition
if (typeDefinition !is GQLInputObjectTypeDefinition) {
return@Resolver null
}
val includeDeprecated = it.getRequiredArgument("includeDeprecated").cast()
typeDefinition.inputFields.filter {
includeDeprecated || it.directives.findDeprecationReason() == null
}
},
"ofType" to Resolver {
val type = it.parentObject.cast()
when (type.type) {
is GQLNamedType -> null
is GQLListType -> IntrospectionType(type.type.type, schema)
is GQLNonNullType -> IntrospectionType(type.type.type, schema)
}
}
),
"__Field" to mapOf(
"name" to Resolver {
val fieldDefinition = it.parentObject.cast()
fieldDefinition.name
},
"description" to Resolver {
val fieldDefinition = it.parentObject.cast()
fieldDefinition.description
},
"args" to Resolver {
val includeDeprecated = it.getRequiredArgument("includeDeprecated").cast()
val fieldDefinition = it.parentObject.cast()
fieldDefinition.arguments.filter {
includeDeprecated || it.directives.findDeprecationReason() == null
}
},
"type" to Resolver {
val fieldDefinition = it.parentObject.cast()
IntrospectionType(fieldDefinition.type, schema)
},
"isDeprecated" to Resolver {
val fieldDefinition = it.parentObject.cast()
fieldDefinition.directives.findDeprecationReason() != null
},
"deprecationReason" to Resolver {
val fieldDefinition = it.parentObject.cast()
fieldDefinition.directives.findDeprecationReason()
}
),
"__InputValue" to mapOf(
"name" to Resolver {
val inputValueDefinition = it.parentObject.cast()
inputValueDefinition.name
},
"description" to Resolver {
val inputValueDefinition = it.parentObject.cast()
inputValueDefinition.description
},
"type" to Resolver {
val inputValueDefinition = it.parentObject.cast()
IntrospectionType(inputValueDefinition.type, schema)
},
"defaultValue" to Resolver {
val inputValueDefinition = it.parentObject.cast()
inputValueDefinition.defaultValue?.toUtf8()
},
"isDeprecated" to Resolver {
val inputValueDefinition = it.parentObject.cast()
inputValueDefinition.directives.findDeprecationReason() != null
},
"deprecationReason" to Resolver {
val inputValueDefinition = it.parentObject.cast()
inputValueDefinition.directives.findDeprecationReason()
},
),
"__EnumValue" to mapOf(
"name" to Resolver {
it.parentObject.cast().name
},
"description" to Resolver {
it.parentObject.cast().description
},
"isDeprecated" to Resolver {
it.parentObject.cast().directives.findDeprecationReason() != null
},
"deprecationReason" to Resolver {
it.parentObject.cast().directives.findDeprecationReason()
},
),
"__Directive" to mapOf(
"name" to Resolver {
it.parentObject.cast().name
},
"description" to Resolver {
it.parentObject.cast().description
},
"isRepeatable" to Resolver {
it.parentObject.cast().repeatable
},
"locations" to Resolver {
it.parentObject.cast().locations.map {
it.name
}
},
"args" to Resolver {
val includeDeprecated = it.getRequiredArgument("includeDeprecated").cast()
it.parentObject.cast().arguments.filter {
includeDeprecated || it.directives.findDeprecationReason() == null
}
}
)
).entries.flatMap { (type, fields) ->
fields.entries.map { (field, resolver) ->
"$type.$field" to resolver
}
}.toMap()
}
private fun IntrospectionType(type: GQLType, schema: Schema): IntrospectionType {
return IntrospectionType(
type,
(type as? GQLNamedType)?.name?.let { schema.typeDefinition(it) }
)
}
private class IntrospectionType(
val type: GQLType,
val typeDefinition: GQLTypeDefinition?,
)
internal enum class __TypeKind {
SCALAR,
OBJECT,
INTERFACE,
UNION,
ENUM,
INPUT_OBJECT,
LIST,
NON_NULL,
}