Repository: graphql-java-kickstart/graphql-java-tools
Pull request #480 (2021-02-15 17:47:06)

Allowing nested inputs defined in extensions to be parsed as well
## Checklist
<!-- Change [ ] to [x] to indicate you acknowledge the check. -->
- [x] Pull requests follows the [contribution guide](https://github.com/graphql-java-kickstart/graphql-java-tools/wiki/Contribution-guide)
- [x] New or modified functionality is covered by tests

## Description

Fields declared in extension would not be parsed, this PR fixes that issue with corresponding UT.


Patch:
diff --git a/src/main/kotlin/graphql/kickstart/tools/SchemaClassScanner.kt b/src/main/kotlin/graphql/kickstart/tools/SchemaClassScanner.kt
index b45ada0..b6f7d34 100644
--- a/src/main/kotlin/graphql/kickstart/tools/SchemaClassScanner.kt
+++ b/src/main/kotlin/graphql/kickstart/tools/SchemaClassScanner.kt
@@ -333,26 +333,29 @@ internal class SchemaClassScanner(
             }
 
             is InputObjectTypeDefinition -> {
-                graphQLType.inputValueDefinitions.forEach { inputValueDefinition ->
-                    val inputGraphQLType = inputValueDefinition.type.unwrap()
-                    if (inputGraphQLType is TypeName && !ScalarInfo.GRAPHQL_SPECIFICATION_SCALARS_DEFINITIONS.containsKey(inputGraphQLType.name)) {
-                        val inputValueJavaType = findInputValueType(inputValueDefinition.name, inputGraphQLType, javaType.unwrap())
-                        if (inputValueJavaType != null) {
-                            handleFoundType(typeClassMatcher.match(TypeClassMatcher.PotentialMatch.parameterType(
-                                inputValueDefinition.type,
-                                inputValueJavaType,
-                                GenericType(javaType, options).relativeToType(inputValueJavaType),
-                                InputObjectReference(inputValueDefinition)
-                            )))
-                        } else {
-                            var mappingAdvice = "Try adding it manually to the dictionary"
-                            if (javaType.unwrap().name.contains("Map")) {
-                                mappingAdvice = " or add a class to represent your input type instead of a Map."
+                val inputObjectTypes = listOf(graphQLType) + inputExtensionDefinitions.filter { it.name == graphQLType.name }
+                inputObjectTypes
+                    .flatMap { it.inputValueDefinitions }
+                    .forEach { inputValueDefinition ->
+                        val inputGraphQLType = inputValueDefinition.type.unwrap()
+                        if (inputGraphQLType is TypeName && !ScalarInfo.GRAPHQL_SPECIFICATION_SCALARS_DEFINITIONS.containsKey(inputGraphQLType.name)) {
+                            val inputValueJavaType = findInputValueType(inputValueDefinition.name, inputGraphQLType, javaType.unwrap())
+                            if (inputValueJavaType != null) {
+                                handleFoundType(typeClassMatcher.match(TypeClassMatcher.PotentialMatch.parameterType(
+                                    inputValueDefinition.type,
+                                    inputValueJavaType,
+                                    GenericType(javaType, options).relativeToType(inputValueJavaType),
+                                    InputObjectReference(inputValueDefinition)
+                                )))
+                            } else {
+                                var mappingAdvice = "Try adding it manually to the dictionary"
+                                if (javaType.unwrap().name.contains("Map")) {
+                                    mappingAdvice = " or add a class to represent your input type instead of a Map."
+                                }
+                                log.warn("Cannot find definition for field '${inputValueDefinition.name}: ${inputGraphQLType.name}' on input type '${graphQLType.name}' -> ${javaType.unwrap().name}. $mappingAdvice")
                             }
-                            log.warn("Cannot find definition for field '${inputValueDefinition.name}: ${inputGraphQLType.name}' on input type '${graphQLType.name}' -> ${javaType.unwrap().name}. $mappingAdvice")
                         }
                     }
-                }
             }
         }
     }


Test patch:
diff --git a/src/test/groovy/graphql/kickstart/tools/NestedInputTypesSpec.groovy b/src/test/groovy/graphql/kickstart/tools/NestedInputTypesSpec.groovy
index 54be370..d0ce1a6 100644
--- a/src/test/groovy/graphql/kickstart/tools/NestedInputTypesSpec.groovy
+++ b/src/test/groovy/graphql/kickstart/tools/NestedInputTypesSpec.groovy
@@ -53,6 +53,55 @@ class NestedInputTypesSpec extends Specification {
         data.materials == []
     }
 
+    def "nested input in extensions are parsed"() {
+        when:
+        GraphQLSchema schema = SchemaParser.newParser().schemaString('''\
+                        type Query {
+                            materials(filter: MaterialFilter): [Material!]!
+                        }
+                        
+                        input MaterialFilter {
+                            title: String
+                        }
+                        
+                        extend input MaterialFilter {
+                            requestFilter: RequestFilter
+                        }
+                        
+                        input RequestFilter {
+                            and: [RequestFilter!]
+                            or: [RequestFilter!]
+                            discountTypeFilter: DiscountTypeFilter
+                        }
+                        
+                        input DiscountTypeFilter {
+                            name: String
+                        }
+                        
+                        type Material {
+                            id: ID!
+                        }
+                    ''').resolvers(new QueryResolver())
+                .build()
+                .makeExecutableSchema()
+        GraphQL gql = GraphQL.newGraphQL(schema)
+                .queryExecutionStrategy(new AsyncExecutionStrategy())
+                .build()
+        def data = Utils.assertNoGraphQlErrors(gql, [filter: [title: "title", requestFilter: [discountTypeFilter: [name: "discount"]]]]) {
+            '''
+                query materials($filter: MaterialFilter!) {
+                    materials(filter: $filter) {
+                        id
+                    }
+                }
+                '''
+        }
+
+        then:
+        noExceptionThrown()
+        data.materials == []
+    }
+
     class QueryResolver implements GraphQLQueryResolver {
         List<Material> materials(MaterialFilter filter) { Collections.emptyList() }
     }