Skip to content

Commit ad4ad8a

Browse files
authored
Merge pull request #887 from graphql-java-kickstart/bugfix/866
Build unions that are only members of other unions
2 parents 5a73d9a + 8021f50 commit ad4ad8a

2 files changed

Lines changed: 27 additions & 4 deletions

File tree

‎src/main/kotlin/graphql/kickstart/tools/SchemaClassScanner.kt‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,9 @@ internal class SchemaClassScanner(
113113
handleDictionaryTypes(getAllObjectTypesImplementingDiscoveredInterfaces()) { "Object type '${it.name}' implements a known interface, but no class could be found for that type name. Please pass a class for type '${it.name}' in the parser's dictionary." }
114114
if (scanQueue()) continue
115115

116+
// Discover unions listed as members of discovered unions, even when no field returns them.
117+
if (handleNestedUnions()) continue
118+
116119
// Require all members of discovered unions to be discovered.
117120
handleDictionaryTypes(getAllObjectTypeMembersOfDiscoveredUnions()) { "Object type '${it.name}' is a member of a known union, but no class could be found for that type name. Please pass a class for type '${it.name}' in the parser's dictionary." }
118121
if (scanQueue()) continue
@@ -359,6 +362,22 @@ internal class SchemaClassScanner(
359362
}.flatten().distinct()
360363
}
361364

365+
/**
366+
* Unions can list other unions as members. Returns whether any new unions were found.
367+
*/
368+
private fun handleNestedUnions(): Boolean {
369+
val newUnions = dictionary.keys.filterIsInstance<UnionTypeDefinition>().flatMap { union ->
370+
union.withExtensions(typeExtensions).flatMap { it.memberTypes }.filterIsInstance<TypeName>()
371+
.map { definitionsByName[it.name] }
372+
.filterIsInstance<UnionTypeDefinition>()
373+
.filter { !dictionary.containsKey(it) }
374+
.map { it to UnionMemberReference(union) }
375+
}
376+
377+
newUnions.forEach { (type, reference) -> handleFoundType(type, null, reference) }
378+
return newUnions.isNotEmpty()
379+
}
380+
362381
/**
363382
* Implementations can narrow an interface field to a member of its union or an implementor of its interface, so such a
364383
* union or interface may not be reachable from any object field. Returns whether any new types were found.
@@ -755,6 +774,10 @@ internal class SchemaClassScanner(
755774
override fun getDescription() = "interface declarations of ${type.name}"
756775
}
757776

777+
private class UnionMemberReference(private val union: UnionTypeDefinition) : Reference() {
778+
override fun getDescription() = "member of union ${union.name}"
779+
}
780+
758781
private class SealedSubclassReference(private val sealedType: JavaType) : Reference() {
759782
override fun getDescription() = "subclass of sealed type ${sealedType.typeName}"
760783
}

‎src/test/kotlin/graphql/kickstart/tools/TypeExtensionTest.kt‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -92,17 +92,17 @@ class TypeExtensionTest {
9292
}
9393

9494
@Test
95-
fun `union extensions can add nested unions`() {
95+
fun `union extensions can add nested unions that no field returns`() {
9696
val schema = parser(
9797
"""
9898
type Query {
9999
fooBar: FooBar
100-
barBaz: BarBaz
101100
}
102101
103102
union FooBar = Foo
104103
extend union FooBar = BarBaz
105-
union BarBaz = Bar | Baz
104+
union BarBaz = Bar | BazUnion
105+
union BazUnion = Baz
106106
107107
$fooBarBaz
108108
""") {
@@ -111,6 +111,7 @@ class TypeExtensionTest {
111111
.assertEveryFieldResolves()
112112

113113
assertEquals((schema.getType("FooBar") as GraphQLUnionType).types.map { it.name }, listOf("Foo", "Bar", "Baz"))
114+
assertEquals((schema.getType("BarBaz") as GraphQLUnionType).types.map { it.name }, listOf("Bar", "Baz"))
114115
}
115116

116117
@Test
@@ -475,7 +476,6 @@ class TypeExtensionTest {
475476

476477
class Query : GraphQLQueryResolver {
477478
fun fooBar(): FooBar = Baz("baz")
478-
fun barBaz(): FooBar? = null
479479
fun color(): Color = Color.GREEN
480480
fun node(): Node = Item("1", "item")
481481
fun search(filter: Filter?): String? = null

0 commit comments

Comments
 (0)