Skip to content

Commit 327a82b

Browse files
committed
Fix scan looping logic to actually exhaust all possibilities for finding objects (fixes graphql-java-kickstart#86)
1 parent afeedee commit 327a82b

File tree

3 files changed

+72
-59
lines changed

3 files changed

+72
-59
lines changed

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

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -78,24 +78,34 @@ internal class SchemaClassScanner(initialDictionary: BiMap<String, Class<*>>, al
7878
handleRootType(rootTypeHolder.mutation)
7979
handleRootType(rootTypeHolder.subscription)
8080

81-
// Loop over all objects scanning each one only once for more objects to discover.
82-
while(queue.isNotEmpty()) {
83-
while (queue.isNotEmpty()) {
84-
while (queue.isNotEmpty()) {
85-
scanQueueItemForPotentialMatches(queue.iterator().run { val item = next(); remove(); item })
86-
}
81+
scanQueue()
8782

83+
// Loop over all objects scanning each one only once for more objects to discover.
84+
do {
85+
do {
8886
// Require all implementors of discovered interfaces to be discovered or provided.
89-
handleInterfaceOrUnionSubTypes(getAllObjectTypesImplementingDiscoveredInterfaces(), { "Object type '${it.name}' implements a known interface, but no class was found for that type name. Please pass a class for type '${it.name}' in the parser's dictionary." })
90-
}
87+
handleInterfaceOrUnionSubTypes(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." })
88+
} while (scanQueue())
9189

9290
// Require all members of discovered unions to be discovered.
93-
handleInterfaceOrUnionSubTypes(getAllObjectTypeMembersOfDiscoveredUnions(), { "Object type '${it.name}' is a member of a known union, but no class was found for that type name. Please pass a class for type '${it.name}' in the parser's dictionary." })
94-
}
91+
handleInterfaceOrUnionSubTypes(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." })
92+
} while (scanQueue())
9593

9694
return validateAndCreateResult(rootTypeHolder)
9795
}
9896

97+
private fun scanQueue(): Boolean {
98+
if(queue.isEmpty()) {
99+
return false
100+
}
101+
102+
while (queue.isNotEmpty()) {
103+
scanQueueItemForPotentialMatches(queue.iterator().run { val item = next(); remove(); item })
104+
}
105+
106+
return true
107+
}
108+
99109
/**
100110
* Adds all root resolvers for a type to the list of classes to scan
101111
*/
@@ -182,7 +192,7 @@ internal class SchemaClassScanner(initialDictionary: BiMap<String, Class<*>>, al
182192

183193
private fun handleInterfaceOrUnionSubTypes(types: List<ObjectTypeDefinition>, failureMessage: (ObjectTypeDefinition) -> String) {
184194
types.forEach { type ->
185-
if(!dictionary.containsKey(type)) {
195+
if(!unvalidatedTypes.contains(type) && !dictionary.containsKey(type)) {
186196
val initialEntry = initialDictionary[type.name] ?: throw SchemaClassScannerError(failureMessage(type))
187197
handleFoundType(type, initialEntry.get(), DictionaryReference())
188198
}

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

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.coxautodev.graphql.tools
22

33
import graphql.language.InputObjectTypeDefinition
44
import graphql.language.InterfaceTypeDefinition
5+
import graphql.language.ObjectTypeDefinition
56
import graphql.language.ScalarTypeDefinition
67
import graphql.schema.Coercing
78
import graphql.schema.GraphQLScalarType
@@ -319,4 +320,54 @@ class SchemaClassScannerSpec extends Specification {
319320
class Pojo {
320321
String name
321322
}
323+
324+
def "scanner should handle nested types in input types"() {
325+
when:
326+
ScannedSchemaObjects objects = SchemaParser.newParser()
327+
.schemaString('''
328+
schema {
329+
query: Query
330+
}
331+
332+
type Query {
333+
animal: Animal
334+
}
335+
336+
interface Animal {
337+
type: ComplexType
338+
}
339+
340+
type Dog implements Animal {
341+
type: ComplexType
342+
}
343+
344+
type ComplexType {
345+
id: String
346+
}
347+
''')
348+
.resolvers(new NestedInterfaceTypeQuery())
349+
.dictionary(NestedInterfaceTypeQuery.Dog)
350+
.scan()
351+
352+
then:
353+
objects.definitions.findAll { it instanceof ObjectTypeDefinition }.size() == 3
354+
355+
}
356+
357+
class NestedInterfaceTypeQuery implements GraphQLQueryResolver {
358+
Animal animal() { null }
359+
360+
interface Animal {
361+
ComplexType type()
362+
}
363+
364+
class Dog implements Animal {
365+
@Override
366+
ComplexType type() { null }
367+
}
368+
369+
class ComplexType {
370+
String id
371+
}
372+
}
322373
}

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

Lines changed: 0 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -167,54 +167,6 @@ class SchemaParserSpec extends Specification {
167167
then:
168168
noExceptionThrown()
169169
}
170-
171-
def "parser should parse interface with complex type"() {
172-
when:
173-
SchemaParser.newParser()
174-
.schemaString('''
175-
schema {
176-
query: Query
177-
}
178-
179-
type Query {
180-
animal: Animal
181-
}
182-
183-
interface Animal {
184-
type: ComplexType
185-
}
186-
187-
type Dog implements Animal {
188-
type: ComplexType
189-
}
190-
191-
type ComplexType {
192-
id: String
193-
}
194-
''')
195-
.resolvers(new ComplexQuery())
196-
.build()
197-
.makeExecutableSchema()
198-
199-
then:
200-
noExceptionThrown()
201-
202-
}
203-
204-
}
205-
206-
class ComplexQuery implements GraphQLQueryResolver {
207-
Animal animal() { null }
208-
209-
interface Animal {
210-
ComplexType type = null
211-
}
212-
213-
class Dog implements Animal { }
214-
215-
class ComplexType {
216-
String id
217-
}
218170
}
219171

220172
class Filter {

0 commit comments

Comments
 (0)