Skip to content

Commit 296ac4b

Browse files
authored
Merge pull request #911 from graphql-java-kickstart/feature/889-throw-missing-parameter
Throw when a resolver method is missing a parameter for an argument
2 parents 9c9ef9e + cc7dc94 commit 296ac4b

3 files changed

Lines changed: 91 additions & 57 deletions

File tree

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import graphql.kickstart.tools.ScanDictionary.InitialDictionaryEntry
66
import graphql.kickstart.tools.ScanDictionary.UnionMemberReference
77
import graphql.kickstart.tools.ScanDictionary.UnusedTypeReference
88
import graphql.kickstart.tools.resolver.FieldResolver
9+
import graphql.kickstart.tools.resolver.FieldResolverError
910
import graphql.kickstart.tools.resolver.FieldResolverScanner
1011
import graphql.kickstart.tools.resolver.MethodFieldResolver
1112
import graphql.kickstart.tools.resolver.PropertyFieldResolver
@@ -457,7 +458,7 @@ internal class SchemaClassScanner(
457458
fieldResolver.scanForMatches().forEach { potentialMatch ->
458459
dictionary.handleFoundType(typeClassMatcher.match(potentialMatch))
459460
}
460-
warnIfParameterMissing(type, fieldResolver)
461+
requireParameterForEachArgument(type, fieldResolver)
461462
}
462463
}
463464

@@ -482,8 +483,7 @@ internal class SchemaClassScanner(
482483
)
483484
}
484485

485-
// TODO throw a FieldResolverError instead in 15.0.0 (#889)
486-
private fun warnIfParameterMissing(type: ObjectTypeDefinition, fieldResolver: FieldResolver) {
486+
private fun requireParameterForEachArgument(type: ObjectTypeDefinition, fieldResolver: FieldResolver) {
487487
if (fieldResolver !is MethodFieldResolver) {
488488
return
489489
}
@@ -495,7 +495,7 @@ internal class SchemaClassScanner(
495495
}
496496

497497
val method = fieldResolver.method
498-
log.warn(
498+
throw FieldResolverError(
499499
"Field '${type.name}.${fieldResolver.field.name}' has no parameter for its argument '${argument.name}: " +
500500
"${TypeUtil.simplePrint(argument.type)}' in ${method.declaringClass.name}.${method.name}, so the ${parameter.simpleName} " +
501501
"parameter gets the argument instead. Add a parameter for each argument, in order, before the ${parameter.simpleName} parameter."

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

Lines changed: 69 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,12 @@ import graphql.language.Value
1111
import graphql.schema.Coercing
1212
import graphql.schema.CoercingParseLiteralException
1313
import graphql.schema.CoercingSerializeException
14+
import graphql.kickstart.tools.resolver.FieldResolverError
15+
import graphql.kickstart.tools.resolver.MissingResolverDataFetcherProvider
16+
import graphql.schema.DataFetcher
1417
import graphql.schema.DataFetchingEnvironment
1518
import graphql.schema.GraphQLScalarType
19+
import org.junit.Assert.assertThrows
1620
import org.junit.Test
1721
import java.lang.reflect.InvocationHandler
1822
import java.lang.reflect.Method
@@ -311,45 +315,77 @@ class MethodFieldResolverTest {
311315
}
312316

313317
@Test
314-
fun `should warn when a resolver method is missing a parameter for an argument`() {
315-
val (_, warnings) = withWarnings {
316-
SchemaParser.newParser()
317-
.schemaString(
318-
"""
319-
type Query {
320-
user(id: ID!, active: Boolean!): String
321-
users(active: Boolean, first: Int): String
322-
image: Image
323-
search(filter: Filter): String
324-
count(active: Boolean): String
325-
}
326-
327-
type Image {
328-
thumbnail(size: Int): String
329-
}
330-
331-
input Filter {
332-
name: String
333-
}
334-
""")
335-
.resolvers(MissingParameterQuery(), ImageResolver())
336-
.options(SchemaParserOptions.newOptions().contextClass(Map::class).build())
337-
.build()
338-
.makeExecutableSchema()
339-
}
340-
318+
fun `should fail the build when a resolver method is missing a parameter for an argument`() {
341319
val query = MissingParameterQuery::class.java.name
342-
assertEquals(warnings, listOf(
343-
"Field 'Query.user' has no parameter for its argument 'active: Boolean!' in $query.user, so the Map parameter gets the " +
320+
val messagesByQueryField = mapOf(
321+
"user(id: ID!, active: Boolean!): String" to
322+
"Field 'Query.user' has no parameter for its argument 'active: Boolean!' in $query.user, so the Map parameter gets the " +
344323
"argument instead. Add a parameter for each argument, in order, before the Map parameter.",
345-
"Field 'Query.users' has no parameter for its argument 'active: Boolean' in $query.users, so the GraphQLContext parameter " +
324+
"users(active: Boolean, first: Int): String" to
325+
"Field 'Query.users' has no parameter for its argument 'active: Boolean' in $query.users, so the GraphQLContext parameter " +
346326
"gets the argument instead. Add a parameter for each argument, in order, before the GraphQLContext parameter.",
347-
"Field 'Image.thumbnail' has no parameter for its argument 'size: Int' in ${ImageResolver::class.java.name}.thumbnail, so the " +
348-
"DataFetchingEnvironment parameter gets the argument instead. Add a parameter for each argument, in order, before the " +
327+
"image: Image" to
328+
"Field 'Image.thumbnail' has no parameter for its argument 'size: Int' in ${ImageResolver::class.java.name}.thumbnail, so " +
329+
"the DataFetchingEnvironment parameter gets the argument instead. Add a parameter for each argument, in order, before the " +
349330
"DataFetchingEnvironment parameter."
350-
))
331+
)
332+
333+
messagesByQueryField.forEach { (queryField, message) ->
334+
val error = assertThrows(FieldResolverError::class.java) {
335+
buildMissingParameterSchema(queryField, SchemaParserOptions.newOptions())
336+
}
337+
338+
assertEquals(error.message, message)
339+
}
340+
}
341+
342+
@Test
343+
fun `should fail the build for a missing parameter when unimplemented resolvers are allowed`() {
344+
val options = listOf(
345+
SchemaParserOptions.newOptions().allowUnimplementedResolvers(true),
346+
SchemaParserOptions.newOptions().missingResolverDataFetcherProvider(MissingResolverDataFetcherProvider { _, _ -> DataFetcher { "provided" } })
347+
)
348+
349+
options.forEach {
350+
val error = assertThrows(FieldResolverError::class.java) {
351+
buildMissingParameterSchema("users(active: Boolean, first: Int): String", it)
352+
}
353+
354+
assert("Field 'Query.users' has no parameter for its argument 'active: Boolean'" in error.message!!) { error.message!! }
355+
}
356+
}
357+
358+
@Test
359+
fun `should build when the context class is an argument type`() {
360+
val schema = buildMissingParameterSchema(
361+
"search(filter: Filter): String\ncount(active: Boolean): String",
362+
SchemaParserOptions.newOptions()
363+
)
364+
365+
assertEquals(schema.queryType.fieldDefinitions.map { it.name }, listOf("search", "count"))
351366
}
352367

368+
private fun buildMissingParameterSchema(queryField: String, options: SchemaParserOptions.Builder) =
369+
SchemaParser.newParser()
370+
.schemaString(
371+
"""
372+
type Query {
373+
$queryField
374+
}
375+
376+
type Image {
377+
thumbnail(size: Int): String
378+
}
379+
380+
input Filter {
381+
name: String
382+
}
383+
""")
384+
.resolvers(MissingParameterQuery(), ImageResolver())
385+
.options(options.contextClass(Map::class).build())
386+
.build()
387+
.makeExecutableSchema()
388+
353389
class MissingParameterQuery : GraphQLQueryResolver {
354390
fun user(id: String, context: Map<String, Any>) = "$id $context"
355391
fun users(context: GraphQLContext?, first: Int?) = "$context $first"

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

Lines changed: 18 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -432,29 +432,27 @@ class SourcelessResolverTest {
432432
}
433433

434434
@Test
435-
fun `no argument warning for a method without the source`() {
436-
val (_, warnings) = withWarnings {
437-
SchemaParser.newParser()
438-
.schemaString(
439-
"""
440-
type Query {
441-
folder: Folder!
442-
}
435+
fun `a method without the source needs no parameter for the source`() {
436+
val schema = SchemaParser.newParser()
437+
.schemaString(
438+
"""
439+
type Query {
440+
folder: Folder!
441+
}
443442
444-
type Folder {
445-
images: [Image!]!
446-
}
443+
type Folder {
444+
images: [Image!]!
445+
}
447446
448-
type Image {
449-
thumbnail(size: Int!): String!
450-
}
451-
""")
452-
.resolvers(FolderQuery(), FolderResolver(), SourcelessImageResolver())
453-
.build()
454-
.makeExecutableSchema()
455-
}
447+
type Image {
448+
thumbnail(size: Int!): String!
449+
}
450+
""")
451+
.resolvers(FolderQuery(), FolderResolver(), SourcelessImageResolver())
452+
.build()
453+
.makeExecutableSchema()
456454

457-
assertEquals(warnings.filter { "has no parameter for its argument" in it }, listOf())
455+
assertEquals(schema.getObjectType("Image")!!.getFieldDefinition("thumbnail").arguments.map { it.name }, listOf("size"))
458456
}
459457

460458
@Test

0 commit comments

Comments
 (0)