Skip to content

Commit a004243

Browse files
authored
Merge pull request #404 from graphql-java-kickstart/388-fix-optional-argument-handling
388 fix optional argument handling
2 parents 66326e2 + d0e79c9 commit a004243

File tree

3 files changed

+113
-9
lines changed

3 files changed

+113
-9
lines changed

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

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,11 +92,10 @@ internal class MethodFieldResolver(
9292
}
9393

9494
if (value == null && isOptional) {
95-
if (environment.containsArgument(definition.name)) {
96-
return@add Optional.empty<Any>()
97-
} else {
95+
if (options.inputArgumentOptionalDetectOmission && !environment.containsArgument(definition.name)) {
9896
return@add null
9997
}
98+
return@add Optional.empty<Any>()
10099
}
101100

102101
if (value != null

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

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ data class SchemaParserOptions internal constructor(
2525
val allowUnimplementedResolvers: Boolean,
2626
val objectMapperProvider: PerFieldObjectMapperProvider,
2727
val proxyHandlers: List<ProxyHandler>,
28+
val inputArgumentOptionalDetectOmission: Boolean,
2829
val preferGraphQLResolver: Boolean,
2930
val introspectionEnabled: Boolean,
3031
val coroutineContextProvider: CoroutineContextProvider,
@@ -50,6 +51,7 @@ data class SchemaParserOptions internal constructor(
5051
private var allowUnimplementedResolvers = false
5152
private var objectMapperProvider: PerFieldObjectMapperProvider = PerFieldConfiguringObjectMapperProvider()
5253
private val proxyHandlers: MutableList<ProxyHandler> = mutableListOf(Spring4AopProxyHandler(), GuiceAopProxyHandler(), JavassistProxyHandler(), WeldProxyHandler())
54+
private var inputArgumentOptionalDetectOmission = false
5355
private var preferGraphQLResolver = false
5456
private var introspectionEnabled = true
5557
private var coroutineContextProvider: CoroutineContextProvider? = null
@@ -80,6 +82,10 @@ data class SchemaParserOptions internal constructor(
8082
this.allowUnimplementedResolvers = allowUnimplementedResolvers
8183
}
8284

85+
fun inputArgumentOptionalDetectOmission(inputArgumentOptionalDetectOmission: Boolean) = this.apply {
86+
this.inputArgumentOptionalDetectOmission = inputArgumentOptionalDetectOmission
87+
}
88+
8389
fun preferGraphQLResolver(preferGraphQLResolver: Boolean) = this.apply {
8490
this.preferGraphQLResolver = preferGraphQLResolver
8591
}
@@ -146,9 +152,18 @@ data class SchemaParserOptions internal constructor(
146152
genericWrappers
147153
}
148154

149-
return SchemaParserOptions(contextClass, wrappers, allowUnimplementedResolvers, objectMapperProvider,
150-
proxyHandlers, preferGraphQLResolver, introspectionEnabled, coroutineContextProvider,
151-
typeDefinitionFactories, fieldVisibility
155+
return SchemaParserOptions(
156+
contextClass,
157+
wrappers,
158+
allowUnimplementedResolvers,
159+
objectMapperProvider,
160+
proxyHandlers,
161+
inputArgumentOptionalDetectOmission,
162+
preferGraphQLResolver,
163+
introspectionEnabled,
164+
coroutineContextProvider,
165+
typeDefinitionFactories,
166+
fieldVisibility
152167
)
153168
}
154169
}

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

Lines changed: 93 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,101 @@ import org.junit.Test
1010
import java.lang.reflect.InvocationHandler
1111
import java.lang.reflect.Method
1212
import java.lang.reflect.Proxy
13+
import java.util.*
1314

1415
class MethodFieldResolverTest {
1516

1617
@Test
17-
fun shouldHandleScalarTypesAsMethodInputArgument() {
18+
fun `should handle Optional type as method input argument`() {
19+
val schema = SchemaParser.newParser()
20+
.schemaString("""
21+
type Query {
22+
testValue(input: String): String
23+
testOmitted(input: String): String
24+
testNull(input: String): String
25+
}
26+
"""
27+
)
28+
.scalars(customScalarType)
29+
.resolvers(object : GraphQLQueryResolver {
30+
fun testValue(input: Optional<String>) = input.toString()
31+
fun testOmitted(input: Optional<String>) = input.toString()
32+
fun testNull(input: Optional<String>) = input.toString()
33+
})
34+
.build()
35+
.makeExecutableSchema()
36+
37+
val gql = GraphQL.newGraphQL(schema).build()
38+
39+
val result = gql
40+
.execute(ExecutionInput.newExecutionInput()
41+
.query("""
42+
query {
43+
testValue(input: "test-value")
44+
testOmitted
45+
testNull(input: null)
46+
}
47+
""")
48+
.context(Object())
49+
.root(Object()))
50+
51+
val expected = mapOf(
52+
"testValue" to "Optional[test-value]",
53+
"testOmitted" to "Optional.empty",
54+
"testNull" to "Optional.empty"
55+
)
56+
57+
Assert.assertEquals(expected, result.getData())
58+
}
59+
60+
@Test
61+
fun `should handle Optional type as method input argument with omission detection`() {
62+
val schema = SchemaParser.newParser()
63+
.schemaString("""
64+
type Query {
65+
testValue(input: String): String
66+
testOmitted(input: String): String
67+
testNull(input: String): String
68+
}
69+
"""
70+
)
71+
.scalars(customScalarType)
72+
.resolvers(object : GraphQLQueryResolver {
73+
fun testValue(input: Optional<String>) = input.toString()
74+
fun testOmitted(input: Optional<String>?) = input.toString()
75+
fun testNull(input: Optional<String>) = input.toString()
76+
})
77+
.options(SchemaParserOptions.newOptions()
78+
.inputArgumentOptionalDetectOmission(true)
79+
.build())
80+
.build()
81+
.makeExecutableSchema()
82+
83+
val gql = GraphQL.newGraphQL(schema).build()
84+
85+
val result = gql
86+
.execute(ExecutionInput.newExecutionInput()
87+
.query("""
88+
query {
89+
testValue(input: "test-value")
90+
testOmitted
91+
testNull(input: null)
92+
}
93+
""")
94+
.context(Object())
95+
.root(Object()))
96+
97+
val expected = mapOf(
98+
"testValue" to "Optional[test-value]",
99+
"testOmitted" to "null",
100+
"testNull" to "Optional.empty"
101+
)
102+
103+
Assert.assertEquals(expected, result.getData())
104+
}
105+
106+
@Test
107+
fun `should handle scalar types as method input argument`() {
18108
val schema = SchemaParser.newParser()
19109
.schemaString("""
20110
scalar CustomScalar
@@ -47,7 +137,7 @@ class MethodFieldResolverTest {
47137
}
48138

49139
@Test
50-
fun shouldHandleListsOfScalarTypes() {
140+
fun `should handle lists of scalar types`() {
51141
val schema = SchemaParser.newParser()
52142
.schemaString("""
53143
scalar CustomScalar
@@ -80,7 +170,7 @@ class MethodFieldResolverTest {
80170
}
81171

82172
@Test
83-
fun shouldHandleProxies() {
173+
fun `should handle proxies`() {
84174
val invocationHandler = object : InvocationHandler {
85175
override fun invoke(proxy: Any, method: Method, args: Array<out Any>): Any {
86176
return when (method.name) {

0 commit comments

Comments
 (0)