Skip to content

Commit 9f0f2f7

Browse files
authored
Merge pull request #566 from graphql-java-kickstart/deprecate-context-option
Update custom context class option to be compatible with new GraphQLContext class
2 parents 334c182 + 71175ec commit 9f0f2f7

File tree

12 files changed

+95
-81
lines changed

12 files changed

+95
-81
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -386,7 +386,7 @@ internal class SchemaClassScanner(
386386
val methods = clazz.methods
387387

388388
val filteredMethods = methods.filter {
389-
it.name == name || it.name == "get${name.capitalize()}"
389+
it.name == name || it.name == "get${name.replaceFirstChar(Char::titlecase)}"
390390
}.sortedBy { it.name.length }
391391
return filteredMethods.find {
392392
!it.isSynthetic

src/main/kotlin/graphql/kickstart/tools/resolver/FieldResolverScanner.kt

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package graphql.kickstart.tools.resolver
22

3+
import graphql.GraphQLContext
34
import graphql.Scalars
45
import graphql.kickstart.tools.ResolverInfo
56
import graphql.kickstart.tools.RootResolverInfo
@@ -25,7 +26,7 @@ internal class FieldResolverScanner(val options: SchemaParserOptions) {
2526

2627
private val log = LoggerFactory.getLogger(javaClass)
2728

28-
private val allowedLastArgumentTypes = listOfNotNull(DataFetchingEnvironment::class.java, options.contextClass)
29+
private val allowedLastArgumentTypes = listOfNotNull(DataFetchingEnvironment::class.java, GraphQLContext::class.java, options.contextClass)
2930

3031
fun findFieldResolver(field: FieldDefinition, resolverInfo: ResolverInfo): FieldResolver {
3132
val searches = resolverInfo.getFieldSearches()
@@ -72,7 +73,7 @@ internal class FieldResolverScanner(val options: SchemaParserOptions) {
7273
}
7374
}
7475

75-
private fun findResolverMethod(field: FieldDefinition, search: Search): java.lang.reflect.Method? {
76+
private fun findResolverMethod(field: FieldDefinition, search: Search): Method? {
7677
val methods = getAllMethods(search.type)
7778
val argumentCount = field.inputValueDefinitions.size + if (search.requiredFirstParameterType != null) 1 else 0
7879
val name = field.name
@@ -113,7 +114,7 @@ internal class FieldResolverScanner(val options: SchemaParserOptions) {
113114

114115
private fun isBoolean(type: GraphQLLangType) = type.unwrap().let { it is TypeName && it.name == Scalars.GraphQLBoolean.name }
115116

116-
private fun verifyMethodArguments(method: java.lang.reflect.Method, requiredCount: Int, search: Search): Boolean {
117+
private fun verifyMethodArguments(method: Method, requiredCount: Int, search: Search): Boolean {
117118
val appropriateFirstParameter = if (search.requiredFirstParameterType != null) {
118119
method.genericParameterTypes.firstOrNull()?.let {
119120
it == search.requiredFirstParameterType || method.declaringClass.typeParameters.contains(it)
@@ -130,15 +131,15 @@ internal class FieldResolverScanner(val options: SchemaParserOptions) {
130131
return correctParameterCount && appropriateFirstParameter
131132
}
132133

133-
private fun getMethodParameterCount(method: java.lang.reflect.Method): Int {
134+
private fun getMethodParameterCount(method: Method): Int {
134135
return try {
135136
method.kotlinFunction?.valueParameters?.size ?: method.parameterCount
136137
} catch (e: InternalError) {
137138
method.parameterCount
138139
}
139140
}
140141

141-
private fun getMethodLastParameter(method: java.lang.reflect.Method): Type? {
142+
private fun getMethodLastParameter(method: Method): Type? {
142143
return try {
143144
method.kotlinFunction?.valueParameters?.lastOrNull()?.type?.javaType
144145
?: method.parameterTypes.lastOrNull()

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

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

33
import com.fasterxml.jackson.core.type.TypeReference
4+
import graphql.GraphQLContext
45
import graphql.TrivialDataFetcher
56
import graphql.kickstart.tools.*
67
import graphql.kickstart.tools.SchemaParserOptions.GenericWrapper
@@ -13,6 +14,7 @@ import graphql.schema.DataFetcher
1314
import graphql.schema.DataFetchingEnvironment
1415
import graphql.schema.GraphQLTypeUtil.isScalar
1516
import kotlinx.coroutines.future.future
17+
import org.slf4j.LoggerFactory
1618
import java.lang.reflect.Method
1719
import java.util.*
1820
import kotlin.coroutines.intrinsics.suspendCoroutineUninterceptedOrReturn
@@ -30,9 +32,12 @@ internal class MethodFieldResolver(
3032
val method: Method
3133
) : FieldResolver(field, search, options, search.type) {
3234

35+
private val log = LoggerFactory.getLogger(javaClass)
36+
3337
private val additionalLastArgument =
3438
try {
35-
method.kotlinFunction?.valueParameters?.size ?: method.parameterCount == (field.inputValueDefinitions.size + getIndexOffset() + 1)
39+
(method.kotlinFunction?.valueParameters?.size
40+
?: method.parameterCount) == (field.inputValueDefinitions.size + getIndexOffset() + 1)
3641
} catch (e: InternalError) {
3742
method.parameterCount == (field.inputValueDefinitions.size + getIndexOffset() + 1)
3843
}
@@ -94,7 +99,21 @@ internal class MethodFieldResolver(
9499
if (this.additionalLastArgument) {
95100
when (this.method.parameterTypes.last()) {
96101
null -> throw ResolverError("Expected at least one argument but got none, this is most likely a bug with graphql-java-tools")
97-
options.contextClass -> args.add { environment -> environment.getContext() }
102+
options.contextClass -> args.add { environment ->
103+
val context: Any? = environment.graphQlContext[options.contextClass]
104+
if (context != null) {
105+
context
106+
} else {
107+
log.warn(
108+
"Generic context class has been deprecated by graphql-java. " +
109+
"To continue using a custom context class as the last parameter in resolver methods " +
110+
"please insert it into the GraphQLContext map when building the ExecutionInput. " +
111+
"This warning will become an error in the future."
112+
)
113+
environment.getContext() // TODO: remove deprecated use in next major release
114+
}
115+
}
116+
GraphQLContext::class.java -> args.add { environment -> environment.graphQlContext }
98117
else -> args.add { environment -> environment }
99118
}
100119
}

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -138,9 +138,9 @@ class DirectiveTest {
138138
val parentType = environment.fieldsContainer
139139

140140
val originalDataFetcher = environment.codeRegistry.getDataFetcher(parentType, field)
141-
val wrappedDataFetcher = DataFetcherFactories.wrapDataFetcher(originalDataFetcher, { _, value ->
142-
(value as? String)?.toUpperCase()
143-
})
141+
val wrappedDataFetcher = DataFetcherFactories.wrapDataFetcher(originalDataFetcher) { _, value ->
142+
(value as? String)?.uppercase()
143+
}
144144

145145
environment.codeRegistry.dataFetcher(parentType, field, wrappedDataFetcher)
146146

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

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -366,25 +366,23 @@ class Mutation : GraphQLMutationResolver {
366366
}
367367
}
368368

369-
class OnItemCreatedContext(val newItem: Item)
370-
371369
class Subscription : GraphQLSubscriptionResolver {
372370
fun onItemCreated(env: DataFetchingEnvironment) =
373371
Publisher<Item> { subscriber ->
374-
subscriber.onNext(env.getContext<OnItemCreatedContext>().newItem)
372+
subscriber.onNext(env.graphQlContext["newItem"])
375373
// subscriber.onComplete()
376374
}
377375

378376
fun onItemCreatedCoroutineChannel(env: DataFetchingEnvironment): ReceiveChannel<Item> {
379377
val channel = Channel<Item>(1)
380-
channel.offer(env.getContext<OnItemCreatedContext>().newItem)
378+
channel.trySend(env.graphQlContext["newItem"])
381379
return channel
382380
}
383381

384382
suspend fun onItemCreatedCoroutineChannelAndSuspendFunction(env: DataFetchingEnvironment): ReceiveChannel<Item> {
385383
return coroutineScope {
386384
val channel = Channel<Item>(1)
387-
channel.offer(env.getContext<OnItemCreatedContext>().newItem)
385+
channel.trySend(env.graphQlContext["newItem"])
388386
channel
389387
}
390388
}

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ class EndToEndTest {
7575

7676
val result = gql.execute(ExecutionInput.newExecutionInput()
7777
.query(closure.invoke())
78-
.context(OnItemCreatedContext(newItem))
78+
.graphQLContext(mapOf("newItem" to newItem))
7979
.variables(mapOf()))
8080

8181
val data = result.getData() as Publisher<ExecutionResult>
@@ -597,7 +597,7 @@ class EndToEndTest {
597597

598598
val result = gql.execute(ExecutionInput.newExecutionInput()
599599
.query(closure.invoke())
600-
.context(OnItemCreatedContext(newItem))
600+
.graphQLContext(mapOf("newItem" to newItem))
601601
.variables(mapOf()))
602602

603603
val data = result.getData() as Publisher<ExecutionResult>
@@ -625,7 +625,7 @@ class EndToEndTest {
625625

626626
val result = gql.execute(ExecutionInput.newExecutionInput()
627627
.query(closure.invoke())
628-
.context(OnItemCreatedContext(newItem))
628+
.graphQLContext(mapOf("newItem" to newItem))
629629
.variables(mapOf()))
630630

631631
val data = result.getData() as Publisher<ExecutionResult>

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

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package graphql.kickstart.tools
22

33
import graphql.ExecutionResult
4+
import graphql.GraphQLContext
45
import graphql.execution.*
56
import graphql.execution.instrumentation.SimpleInstrumentation
67
import graphql.kickstart.tools.resolver.FieldResolverError
@@ -79,8 +80,8 @@ class MethodFieldResolverDataFetcherTest {
7980
val channel = Channel<String>(10)
8081

8182
init {
82-
channel.offer("A")
83-
channel.offer("B")
83+
channel.trySend("A")
84+
channel.trySend("B")
8485
}
8586

8687
@Suppress("UNUSED_PARAMETER")
@@ -176,21 +177,34 @@ class MethodFieldResolverDataFetcherTest {
176177

177178
@Test
178179
fun `data fetcher passes environment if method has extra argument even if context is specified`() {
179-
val options = SchemaParserOptions.newOptions().contextClass(ContextClass::class).build()
180-
val resolver = createFetcher("active", options = options, resolver = object : GraphQLResolver<DataClass> {
180+
val context = GraphQLContext.newContext().build()
181+
val resolver = createFetcher("active", resolver = object : GraphQLResolver<DataClass> {
181182
fun isActive(dataClass: DataClass, env: DataFetchingEnvironment): Boolean = env is DataFetchingEnvironment
182183
})
183184

184-
assertEquals(resolver.get(createEnvironment(DataClass(), context = ContextClass())), true)
185+
assertEquals(resolver.get(createEnvironment(DataClass(), context = context)), true)
185186
}
186187

187188
@Test
188189
fun `data fetcher passes context if method has extra argument and context is specified`() {
189-
val context = ContextClass()
190+
val context = GraphQLContext.newContext().build()
191+
val resolver = createFetcher("active", resolver = object : GraphQLResolver<DataClass> {
192+
fun isActive(dataClass: DataClass, ctx: GraphQLContext): Boolean {
193+
return ctx == context
194+
}
195+
})
196+
197+
assertEquals(resolver.get(createEnvironment(DataClass(), context = context)), true)
198+
}
199+
200+
@Test
201+
fun `data fetcher passes custom context if method has extra argument and custom context is specified as part of GraphQLContext`() {
202+
val customContext = ContextClass()
203+
val context = GraphQLContext.of(mapOf(ContextClass::class.java to customContext))
190204
val options = SchemaParserOptions.newOptions().contextClass(ContextClass::class).build()
191205
val resolver = createFetcher("active", options = options, resolver = object : GraphQLResolver<DataClass> {
192206
fun isActive(dataClass: DataClass, ctx: ContextClass): Boolean {
193-
return ctx == context
207+
return ctx == customContext
194208
}
195209
})
196210

@@ -243,7 +257,7 @@ class MethodFieldResolverDataFetcherTest {
243257
private val channel = Channel<String>(10)
244258

245259
init {
246-
channel.offer("A")
260+
channel.trySend("A")
247261
channel.close(IllegalStateException("Channel error"))
248262
}
249263

@@ -281,11 +295,11 @@ class MethodFieldResolverDataFetcherTest {
281295
return FieldResolverScanner(options).findFieldResolver(field, resolverInfo).createDataFetcher()
282296
}
283297

284-
private fun createEnvironment(source: Any = Object(), arguments: Map<String, Any> = emptyMap(), context: Any? = null): DataFetchingEnvironment {
298+
private fun createEnvironment(source: Any = Object(), arguments: Map<String, Any> = emptyMap(), context: GraphQLContext? = null): DataFetchingEnvironment {
285299
return DataFetchingEnvironmentImpl.newDataFetchingEnvironment(buildExecutionContext())
286300
.source(source)
287301
.arguments(arguments)
288-
.context(context)
302+
.graphQLContext(context)
289303
.build()
290304
}
291305

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

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ class MethodFieldResolverTest {
4545
testNull(input: null)
4646
}
4747
""")
48-
.context(Object())
4948
.root(Object()))
5049

5150
assertEquals(result.getData(), mapOf(
@@ -88,7 +87,6 @@ class MethodFieldResolverTest {
8887
testNull(input: null)
8988
}
9089
""")
91-
.context(Object())
9290
.root(Object()))
9391

9492
assertEquals(result.getData(), mapOf(
@@ -124,7 +122,6 @@ class MethodFieldResolverTest {
124122
}
125123
""")
126124
.variables(mapOf("input" to "FooBar"))
127-
.context(Object())
128125
.root(Object()))
129126

130127
assertEquals(result.getData(), mapOf("test" to 6))
@@ -156,7 +153,6 @@ class MethodFieldResolverTest {
156153
}
157154
""")
158155
.variables(mapOf("input" to listOf("Foo", "Bar")))
159-
.context(Object())
160156
.root(Object()))
161157

162158
assertEquals(result.getData(), mapOf("test" to 6))
@@ -204,7 +200,6 @@ class MethodFieldResolverTest {
204200
}
205201
""")
206202
.variables(mapOf("input" to listOf("Foo", "Bar")))
207-
.context(Object())
208203
.root(Object()))
209204

210205
assertEquals(result.getData(), mapOf("test" to 6))

src/test/kotlin/graphql/kickstart/tools/ResolverMethodsTest.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ public void testOmittedBooleanArgument() {
3131
ExecutionResult result = gql
3232
.execute(ExecutionInput.newExecutionInput()
3333
.query("query { testOmittedBoolean }")
34-
.context(new Object())
3534
.root(new Object()));
3635

3736
assertTrue(result.getErrors().isEmpty());

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

Lines changed: 20 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,19 @@
11
package graphql.kickstart.tools
22

33
import graphql.kickstart.tools.resolver.FieldResolverError
4-
import graphql.schema.GraphQLInterfaceType
5-
import graphql.schema.GraphQLObjectType
6-
import graphql.schema.GraphQLArgument
7-
import graphql.schema.GraphQLInputObjectType
8-
import graphql.schema.GraphQLNonNull
4+
import graphql.schema.*
95
import graphql.schema.idl.SchemaDirectiveWiring
106
import graphql.schema.idl.SchemaDirectiveWiringEnvironment
7+
import org.junit.Assert.assertThrows
118
import org.junit.Before
12-
import org.junit.Rule
139
import org.junit.Test
14-
import org.junit.rules.ExpectedException
1510
import org.springframework.aop.framework.ProxyFactory
1611
import java.io.FileNotFoundException
1712
import java.util.concurrent.Future
1813

1914
class SchemaParserTest {
2015
private lateinit var builder: SchemaParserBuilder
2116

22-
@Rule
23-
@JvmField
24-
var expectedEx: ExpectedException = ExpectedException.none()
25-
2617
@Before
2718
fun setup() {
2819
builder = SchemaParser.newParser()
@@ -197,27 +188,24 @@ class SchemaParserTest {
197188

198189
@Test
199190
fun `parser should throw descriptive exception when object is used as input type incorrectly`() {
200-
expectedEx.expect(SchemaError::class.java)
201-
expectedEx.expectMessage("Was a type only permitted for object types incorrectly used as an input type, or vice-versa")
202-
203-
SchemaParser.newParser()
204-
.schemaString(
205-
"""
206-
type Query {
207-
name(filter: Filter): [String]
208-
}
209-
210-
type Filter {
211-
filter: String
212-
}
213-
""")
214-
.resolvers(object : GraphQLQueryResolver {
215-
fun name(filter: Filter): List<String>? = null
216-
})
217-
.build()
218-
.makeExecutableSchema()
219-
220-
throw AssertionError("should not be called")
191+
assertThrows("Was a type only permitted for object types incorrectly used as an input type, or vice-versa", SchemaError::class.java) {
192+
SchemaParser.newParser()
193+
.schemaString(
194+
"""
195+
type Query {
196+
name(filter: Filter): [String]
197+
}
198+
199+
type Filter {
200+
filter: String
201+
}
202+
""")
203+
.resolvers(object : GraphQLQueryResolver {
204+
fun name(filter: Filter): List<String>? = null
205+
})
206+
.build()
207+
.makeExecutableSchema()
208+
}
221209
}
222210

223211
@Test

0 commit comments

Comments
 (0)