Description
Library Version
What version are you using?
Latest but the bug is present in all versions since v1.
Describe the bug
TLDR we cannot modify the GraphQL types using the didGenerateGraphQLType
--- long version ---
When generating a GraphQL type we execute following logic
- execute
willGenerateGraphQLType
hook which allows you to provide custom type bypassing reflection generation (e.g. this hook is used to provide support for custom scalars) - if hook doesn't return an object we attempt to generate it using reflection
- check if object information is already cached
- else check if type is already being built (i.e. references itself) -> return GraphQL reference
- else
- generate the type
- apply
willAddGraphQLTypeToSchema
hook - cache and return the final type
- apply
didGenerateGraphQLType
hook to allow final validation (and modification)
Since graphql-java
types do not implement hashcode/equals, every time we apply didGenerateGraphQLType
hook on our internally cached type we create a new instance of it (if it modifies the type). If multiple fields are referencing the same type, we end up re-generating the same type multiple times. As a result, since those are different objects, attempting to add them to the schema throws the following exception
graphql.AssertException: All types within a GraphQL schema must have unique names. No two provided types may have the same name.
No provided type may have a name which conflicts with any built in types (including Scalar and Introspection types).
You have redefined the type 'Bar' from being a 'GraphQLObjectType' to a 'GraphQLObjectType'
To Reproduce
class TestHook {
@Test
fun `hooks applied twice`() {
val schema = toSchema(
config = SchemaGeneratorConfig(
supportedPackages = listOf("com.example.foo"),
hooks = CustomHooks(),
),
queries = listOf(TopLevelObject(Example()))
)
println(schema.print(includeDirectives = false))
}
}
class Example {
fun foo(id: String): Foo = TODO()
fun foos(): List<Foo> = TODO()
}
data class Foo(val id: String, val name: String)
class CustomHooks : SchemaGeneratorHooks {
override fun didGenerateGraphQLType(type: KType, generatedType: GraphQLType): GraphQLType {
val unwrapped = GraphQLTypeUtil.unwrapNonNull(generatedType)
return if (unwrapped is GraphQLNamedOutputType && unwrapped.name == "Foo") {
(unwrapped as GraphQLObjectType).transform {
println("updating name")
it.name("Bar")
}
} else {
super.didGenerateGraphQLType(type, generatedType)
}
}
}
Expected behavior
It should be possible to modify the types using didGenerateGraphQLType
hook.