Skip to content

Commit 66326e2

Browse files
authored
Merge pull request #398 from lburja/master
Handle `extended input`
2 parents e4690db + 7421495 commit 66326e2

File tree

5 files changed

+83
-12
lines changed

5 files changed

+83
-12
lines changed

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,9 @@ internal class SchemaClassScanner(
3232

3333
private val initialDictionary = initialDictionary.mapValues { InitialDictionaryEntry(it.value) }
3434
private val extensionDefinitions = allDefinitions.filterIsInstance<ObjectTypeExtensionDefinition>()
35+
private val inputExtensionDefinitions = allDefinitions.filterIsInstance<InputObjectTypeExtensionDefinition>()
3536

36-
private val definitionsByName = (allDefinitions.filterIsInstance<TypeDefinition<*>>() - extensionDefinitions).associateBy { it.name }
37+
private val definitionsByName = (allDefinitions.filterIsInstance<TypeDefinition<*>>() - extensionDefinitions - inputExtensionDefinitions).associateBy { it.name }
3738
private val objectDefinitions = (allDefinitions.filterIsInstance<ObjectTypeDefinition>() - extensionDefinitions)
3839
private val objectDefinitionsByName = objectDefinitions.associateBy { it.name }
3940
private val interfaceDefinitionsByName = allDefinitions.filterIsInstance<InterfaceTypeDefinition>().associateBy { it.name }
@@ -173,7 +174,7 @@ internal class SchemaClassScanner(
173174
validateRootResolversWereUsed(rootTypeHolder.mutation, fieldResolvers)
174175
validateRootResolversWereUsed(rootTypeHolder.subscription, fieldResolvers)
175176

176-
return ScannedSchemaObjects(dictionary, observedDefinitions + extensionDefinitions, scalars, rootInfo, fieldResolversByType.toMap(), unusedDefinitions)
177+
return ScannedSchemaObjects(dictionary, observedDefinitions + extensionDefinitions + inputExtensionDefinitions, scalars, rootInfo, fieldResolversByType.toMap(), unusedDefinitions)
177178
}
178179

179180
private fun validateRootResolversWereUsed(rootType: RootType?, fieldResolvers: List<FieldResolver>) {

src/main/kotlin/graphql/kickstart/tools/SchemaParser.kt

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,10 @@ class SchemaParser internal constructor(
4545
private val unusedDefinitions = scanResult.unusedDefinitions
4646

4747
private val extensionDefinitions = definitions.filterIsInstance<ObjectTypeExtensionDefinition>()
48+
private val inputExtensionDefinitions = definitions.filterIsInstance<InputObjectTypeExtensionDefinition>()
4849

4950
private val objectDefinitions = (definitions.filterIsInstance<ObjectTypeDefinition>() - extensionDefinitions)
50-
private val inputObjectDefinitions = definitions.filterIsInstance<InputObjectTypeDefinition>()
51+
private val inputObjectDefinitions = (definitions.filterIsInstance<InputObjectTypeDefinition>() - inputExtensionDefinitions)
5152
private val enumDefinitions = definitions.filterIsInstance<EnumTypeDefinition>()
5253
private val interfaceDefinitions = definitions.filterIsInstance<InterfaceTypeDefinition>()
5354

@@ -173,22 +174,27 @@ class SchemaParser internal constructor(
173174
}
174175

175176
private fun createInputObject(definition: InputObjectTypeDefinition, inputObjects: List<GraphQLInputObjectType>): GraphQLInputObjectType {
177+
val extensionDefinitions = inputExtensionDefinitions.filter { it.name == definition.name }
178+
176179
val builder = GraphQLInputObjectType.newInputObject()
177180
.name(definition.name)
178181
.definition(definition)
182+
.extensionDefinitions(extensionDefinitions)
179183
.description(if (definition.description != null) definition.description.content else getDocumentation(definition))
180184

181185
builder.withDirectives(*buildDirectives(definition.directives, setOf(), Introspection.DirectiveLocation.INPUT_OBJECT))
182186

183-
definition.inputValueDefinitions.forEach { inputDefinition ->
184-
val fieldBuilder = GraphQLInputObjectField.newInputObjectField()
185-
.name(inputDefinition.name)
186-
.definition(inputDefinition)
187-
.description(if (inputDefinition.description != null) inputDefinition.description.content else getDocumentation(inputDefinition))
188-
.defaultValue(buildDefaultValue(inputDefinition.defaultValue))
189-
.type(determineInputType(inputDefinition.type, inputObjects))
190-
.withDirectives(*buildDirectives(inputDefinition.directives, setOf(), Introspection.DirectiveLocation.INPUT_FIELD_DEFINITION))
191-
builder.field(fieldBuilder.build())
187+
(extensionDefinitions + definition).forEach {
188+
it.inputValueDefinitions.forEach { inputDefinition ->
189+
val fieldBuilder = GraphQLInputObjectField.newInputObjectField()
190+
.name(inputDefinition.name)
191+
.definition(inputDefinition)
192+
.description(if (inputDefinition.description != null) inputDefinition.description.content else getDocumentation(inputDefinition))
193+
.defaultValue(buildDefaultValue(inputDefinition.defaultValue))
194+
.type(determineInputType(inputDefinition.type, inputObjects))
195+
.withDirectives(*buildDirectives(inputDefinition.directives, setOf(), Introspection.DirectiveLocation.INPUT_FIELD_DEFINITION))
196+
builder.field(fieldBuilder.build())
197+
}
192198
}
193199

194200
return schemaGeneratorDirectiveHelper.onInputObjectType(builder.build(), schemaDirectiveParameters)

src/test/groovy/graphql/kickstart/tools/EndToEndSpec.groovy

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -509,6 +509,20 @@ class EndToEndSpec extends Specification {
509509
]
510510
}
511511
512+
def "generated schema should handle extended input types"() {
513+
when:
514+
def data = Utils.assertNoGraphQlErrors(gql) {
515+
'''
516+
mutation {
517+
saveUser(input: {name: "John", password: "secret"})
518+
}
519+
'''
520+
}
521+
522+
then:
523+
data.saveUser == "John/secret"
524+
}
525+
512526
def "generated schema supports generic properties"() {
513527
when:
514528
def data = Utils.assertNoGraphQlErrors(gql) {

src/test/groovy/graphql/kickstart/tools/SchemaClassScannerSpec.groovy

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

33
import graphql.execution.batched.Batched
44
import graphql.language.InputObjectTypeDefinition
5+
import graphql.language.InputObjectTypeExtensionDefinition
56
import graphql.language.InterfaceTypeDefinition
67
import graphql.language.ObjectTypeDefinition
78
import graphql.language.ScalarTypeDefinition
@@ -159,6 +160,37 @@ class SchemaClassScannerSpec extends Specification {
159160
}
160161
}
161162

163+
def "scanner handles input types extensions"() {
164+
when:
165+
ScannedSchemaObjects objects = SchemaParser.newParser()
166+
.schemaString('''
167+
type Query { }
168+
169+
type Mutation {
170+
save(input: UserInput!): Boolean
171+
}
172+
173+
input UserInput {
174+
name: String
175+
}
176+
177+
extend input UserInput {
178+
password: String
179+
}
180+
''')
181+
.resolvers(
182+
new GraphQLMutationResolver() {
183+
boolean save(Map map) { true }
184+
},
185+
new GraphQLQueryResolver() {}
186+
)
187+
.scan()
188+
189+
then:
190+
objects.definitions.findAll { (it.class == InputObjectTypeExtensionDefinition.class) }.size() == 1
191+
objects.definitions.findAll { (it.class == InputObjectTypeDefinition.class) }.size() == 1
192+
}
193+
162194
def "scanner allows multiple return types for custom scalars"() {
163195
when:
164196
ScannedSchemaObjects objects = SchemaParser.newParser()

src/test/kotlin/graphql/kickstart/tools/EndToEndSpecHelper.kt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ input ComplexInputTypeTwo {
111111
type Mutation {
112112
addItem(newItem: NewItemInput!): Item!
113113
echoFiles(fileParts: [Upload!]!): [String!]!
114+
saveUser(input: UserInput!): String
114115
}
115116
116117
type Subscription {
@@ -119,6 +120,14 @@ type Subscription {
119120
onItemCreatedCoroutineChannelAndSuspendFunction: Item!
120121
}
121122
123+
input UserInput {
124+
name: String
125+
}
126+
127+
extend input UserInput {
128+
password: String
129+
}
130+
122131
input ItemSearchInput {
123132
# The item name to look for
124133
name: String!
@@ -321,6 +330,15 @@ class Mutation : GraphQLMutationResolver {
321330
fun addItem(input: NewItemInput): Item {
322331
return Item(items.size, input.name, input.type, UUID.randomUUID(), listOf()) // Don't actually add the item to the list, since we want the test to be deterministic
323332
}
333+
334+
fun saveUser(userInput: UserInput): String {
335+
return userInput.name + "/" + userInput.password;
336+
}
337+
338+
class UserInput {
339+
var name: String = ""
340+
var password: String = ""
341+
}
324342
}
325343

326344
class OnItemCreatedContext(val newItem: Item)

0 commit comments

Comments
 (0)