Skip to content

Commit e6f6f4d

Browse files
committed
Directly use GraphQLCodeRegistry instead of private reflection
1 parent dc95d2e commit e6f6f4d

File tree

3 files changed

+36
-43
lines changed

3 files changed

+36
-43
lines changed

src/main/kotlin/com/coxautodev/graphql/tools/SchemaObjects.kt

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,32 +9,29 @@ import graphql.schema.visibility.NoIntrospectionGraphqlFieldVisibility
99
/**
1010
* @author Andrew Potter
1111
*/
12-
data class SchemaObjects(val query: GraphQLObjectType, val mutation: GraphQLObjectType?, val subscription: GraphQLObjectType?, val dictionary: Set<GraphQLType>) {
12+
data class SchemaObjects(val query: GraphQLObjectType, val mutation: GraphQLObjectType?, val subscription: GraphQLObjectType?, val dictionary: Set<GraphQLType>, val codeRegistryBuilder: GraphQLCodeRegistry.Builder) {
1313

1414
/**
1515
* Makes a GraphQLSchema with query, mutation and subscription.
1616
*/
1717
fun toSchema(introspectionEnabled: Boolean): GraphQLSchema {
18-
val builder = GraphQLSchema.newSchema()
18+
if (!introspectionEnabled) {
19+
codeRegistryBuilder.fieldVisibility(NoIntrospectionGraphqlFieldVisibility.NO_INTROSPECTION_FIELD_VISIBILITY)
20+
}
21+
22+
return GraphQLSchema.newSchema()
1923
.query(query)
2024
.mutation(mutation)
2125
.subscription(subscription)
2226
.additionalTypes(dictionary)
23-
24-
if (!introspectionEnabled) {
25-
builder.codeRegistry(
26-
GraphQLCodeRegistry.newCodeRegistry()
27-
.fieldVisibility(NoIntrospectionGraphqlFieldVisibility.NO_INTROSPECTION_FIELD_VISIBILITY)
28-
.build()
29-
)
30-
}
31-
32-
return builder.build()
27+
.codeRegistry(codeRegistryBuilder.build())
28+
.build()
3329
}
3430

3531
/**
3632
* Makes a GraphQLSchema with query but without mutation and subscription.
3733
*/
34+
@Suppress("unused")
3835
fun toReadOnlySchema(): GraphQLSchema = GraphQLSchema.newSchema()
3936
.query(query)
4037
.additionalTypes(dictionary)

src/main/kotlin/com/coxautodev/graphql/tools/SchemaParser.kt

Lines changed: 26 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ import graphql.language.TypeDefinition
2323
import graphql.language.TypeName
2424
import graphql.language.UnionTypeDefinition
2525
import graphql.language.Value
26+
import graphql.schema.FieldCoordinates
2627
import graphql.schema.GraphQLArgument
28+
import graphql.schema.GraphQLCodeRegistry
2729
import graphql.schema.GraphQLDirective
2830
import graphql.schema.GraphQLEnumType
2931
import graphql.schema.GraphQLEnumValueDefinition
@@ -40,15 +42,12 @@ import graphql.schema.GraphQLSchema
4042
import graphql.schema.GraphQLType
4143
import graphql.schema.GraphQLTypeReference
4244
import graphql.schema.GraphQLUnionType
43-
import graphql.schema.TypeResolverProxy
4445
import graphql.schema.idl.DirectiveBehavior
4546
import graphql.schema.idl.RuntimeWiring
4647
import graphql.schema.idl.ScalarInfo
4748
import graphql.schema.idl.SchemaGeneratorHelper
4849
import java.util.*
4950
import kotlin.reflect.KClass
50-
import kotlin.reflect.full.memberProperties
51-
import kotlin.reflect.jvm.isAccessible
5251

5352
/**
5453
* Parses a GraphQL Schema and maps object fields to provided class methods.
@@ -95,6 +94,8 @@ class SchemaParser internal constructor(scanResult: ScannedSchemaObjects, privat
9594
private val schemaGeneratorHelper = SchemaGeneratorHelper()
9695
private val directiveGenerator = DirectiveBehavior()
9796

97+
private val codeRegistryBuilder = GraphQLCodeRegistry.newCodeRegistry()
98+
9899
/**
99100
* Parses the given schema with respect to the given dictionary and returns GraphQL objects.
100101
*/
@@ -107,16 +108,9 @@ class SchemaParser internal constructor(scanResult: ScannedSchemaObjects, privat
107108
val inputObjects = inputObjectDefinitions.map { createInputObject(it) }
108109
val enums = enumDefinitions.map { createEnumObject(it) }
109110

110-
// Unfortunately, since graphql-java 12, the getTypeResolver method has been made package-protected,
111-
// so we need reflection to access the 'typeResolver' field on GraphQLInterfaceType and GraphQLUnionType
112-
val interfaceTypeResolverField = GraphQLInterfaceType::class.memberProperties.find { it.name == "typeResolver" }
113-
interfaceTypeResolverField!!.isAccessible = true
114-
val unionTypeResolverField = GraphQLUnionType::class.memberProperties.find { it.name == "typeResolver" }
115-
unionTypeResolverField!!.isAccessible = true
116-
117111
// Assign type resolver to interfaces now that we know all of the object types
118-
interfaces.forEach { (interfaceTypeResolverField.get(it) as TypeResolverProxy).typeResolver = InterfaceTypeResolver(dictionary.inverse(), it, objects) }
119-
unions.forEach { (unionTypeResolverField.get(it) as TypeResolverProxy).typeResolver = UnionTypeResolver(dictionary.inverse(), it, objects) }
112+
interfaces.forEach { codeRegistryBuilder.typeResolver(it, InterfaceTypeResolver(dictionary.inverse(), it, objects)) }
113+
unions.forEach { codeRegistryBuilder.typeResolver(it, UnionTypeResolver(dictionary.inverse(), it, objects)) }
120114

121115
// Find query type and mutation/subscription type (if mutation/subscription type exists)
122116
val queryName = rootInfo.getQueryName()
@@ -130,7 +124,7 @@ class SchemaParser internal constructor(scanResult: ScannedSchemaObjects, privat
130124
val subscription = objects.find { it.name == subscriptionName }
131125
?: if (rootInfo.isSubscriptionRequired()) throw SchemaError("Expected a Subscription object with name '$subscriptionName' but found none!") else null
132126

133-
return SchemaObjects(query, mutation, subscription, (objects + inputObjects + enums + interfaces + unions).toSet())
127+
return SchemaObjects(query, mutation, subscription, (objects + inputObjects + enums + interfaces + unions).toSet(), codeRegistryBuilder)
134128
}
135129

136130
/**
@@ -141,29 +135,33 @@ class SchemaParser internal constructor(scanResult: ScannedSchemaObjects, privat
141135
/**
142136
* Returns any unused type definitions that were found in the schema
143137
*/
138+
@Suppress("unused")
144139
fun getUnusedDefinitions(): Set<TypeDefinition<*>> = unusedDefinitions
145140

146-
private fun createObject(definition: ObjectTypeDefinition, interfaces: List<GraphQLInterfaceType>): GraphQLObjectType {
147-
val name = definition.name
141+
private fun createObject(objectDefinition: ObjectTypeDefinition, interfaces: List<GraphQLInterfaceType>): GraphQLObjectType {
142+
val name = objectDefinition.name
148143
val builder = GraphQLObjectType.newObject()
149144
.name(name)
150-
.definition(definition)
151-
.description(if (definition.description != null) definition.description.content else getDocumentation(definition))
145+
.definition(objectDefinition)
146+
.description(if (objectDefinition.description != null) objectDefinition.description.content else getDocumentation(objectDefinition))
152147

153-
builder.withDirectives(*buildDirectives(definition.directives, setOf(), Introspection.DirectiveLocation.OBJECT))
148+
builder.withDirectives(*buildDirectives(objectDefinition.directives, setOf(), Introspection.DirectiveLocation.OBJECT))
154149

155-
definition.implements.forEach { implementsDefinition ->
150+
objectDefinition.implements.forEach { implementsDefinition ->
156151
val interfaceName = (implementsDefinition as TypeName).name
157152
builder.withInterface(interfaces.find { it.name == interfaceName }
158153
?: throw SchemaError("Expected interface type with name '$interfaceName' but found none!"))
159154
}
160155

161-
definition.getExtendedFieldDefinitions(extensionDefinitions).forEach { fieldDefinition ->
156+
objectDefinition.getExtendedFieldDefinitions(extensionDefinitions).forEach { fieldDefinition ->
162157
fieldDefinition.description
163158
builder.field { field ->
164159
createField(field, fieldDefinition)
165-
field.dataFetcher(fieldResolversByType[definition]?.get(fieldDefinition)?.createDataFetcher()
166-
?: throw SchemaError("No resolver method found for object type '${definition.name}' and field '${fieldDefinition.name}', this is most likely a bug with graphql-java-tools"))
160+
codeRegistryBuilder.dataFetcher(
161+
FieldCoordinates.coordinates(objectDefinition.name, fieldDefinition.name),
162+
fieldResolversByType[objectDefinition]?.get(fieldDefinition)?.createDataFetcher()
163+
?: throw SchemaError("No resolver method found for object type '${objectDefinition.name}' and field '${fieldDefinition.name}', this is most likely a bug with graphql-java-tools")
164+
)
167165

168166
val wiredField = directiveGenerator.onField(field.build(), DirectiveBehavior.Params(runtimeWiring))
169167
GraphQLFieldDefinition.Builder(wiredField)
@@ -246,17 +244,16 @@ class SchemaParser internal constructor(scanResult: ScannedSchemaObjects, privat
246244
return directiveGenerator.onEnum(builder.build(), DirectiveBehavior.Params(runtimeWiring))
247245
}
248246

249-
private fun createInterfaceObject(definition: InterfaceTypeDefinition): GraphQLInterfaceType {
250-
val name = definition.name
247+
private fun createInterfaceObject(interfaceDefinition: InterfaceTypeDefinition): GraphQLInterfaceType {
248+
val name = interfaceDefinition.name
251249
val builder = GraphQLInterfaceType.newInterface()
252250
.name(name)
253-
.definition(definition)
254-
.description(if (definition.description != null) definition.description.content else getDocumentation(definition))
255-
.typeResolver(TypeResolverProxy())
251+
.definition(interfaceDefinition)
252+
.description(if (interfaceDefinition.description != null) interfaceDefinition.description.content else getDocumentation(interfaceDefinition))
256253

257-
builder.withDirectives(*buildDirectives(definition.directives, setOf(), Introspection.DirectiveLocation.INTERFACE))
254+
builder.withDirectives(*buildDirectives(interfaceDefinition.directives, setOf(), Introspection.DirectiveLocation.INTERFACE))
258255

259-
definition.fieldDefinitions.forEach { fieldDefinition ->
256+
interfaceDefinition.fieldDefinitions.forEach { fieldDefinition ->
260257
builder.field { field -> createField(field, fieldDefinition) }
261258
}
262259

@@ -269,7 +266,6 @@ class SchemaParser internal constructor(scanResult: ScannedSchemaObjects, privat
269266
.name(name)
270267
.definition(definition)
271268
.description(if (definition.description != null) definition.description.content else getDocumentation(definition))
272-
.typeResolver(TypeResolverProxy())
273269

274270
builder.withDirectives(*buildDirectives(definition.directives, setOf(), Introspection.DirectiveLocation.UNION))
275271

src/test/groovy/com/coxautodev/graphql/tools/RelayConnectionSpec.groovy

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ class RelayConnectionSpec extends Specification {
8888
noExceptionThrown()
8989
data.users.edges.size == 1
9090
data.users.edges[0].node.id == "1"
91-
data.users.edges[0].node.name == "NAME"
91+
data.users.edges[0].node.name == "name"
9292
data.otherTypes.edges.size == 1
9393
data.otherTypes.edges[0].node.echo == "echo"
9494
}

0 commit comments

Comments
 (0)