Skip to content

Commit 2665238

Browse files
committed
Read CoroutineScope from GraphQLContext, deprecate context object
- Look up CoroutineScope by its class key in GraphQLContext before falling back - Log a deprecation warning when the legacy context object supplies the scope - Deprecate the legacy directive getters on SchemaDirectiveWiringEnvironmentImpl - Suppress DEPRECATION warnings at the remaining legacy call sites - Add tests for scopes from GraphQLContext and from the legacy context - Add @ConsistentCopyVisibility to SchemaParserOptions - Remove UtilsTest, which had no test methods
1 parent 404b3e7 commit 2665238

11 files changed

Lines changed: 75 additions & 28 deletions

File tree

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import java.util.concurrent.Future
2020
import kotlin.coroutines.CoroutineContext
2121
import kotlin.reflect.KClass
2222

23+
@ConsistentCopyVisibility
2324
data class SchemaParserOptions internal constructor(
2425
val contextClass: Class<*>?,
2526
val genericWrappers: List<GenericWrapper>,

‎src/main/kotlin/graphql/kickstart/tools/directive/DirectiveWiringHelper.kt‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,8 @@ internal class DirectiveWiringHelper(
9696
return checkNotNull(wrapper.invoker(wiring, env)) { "The SchemaDirectiveWiring MUST return a non null return value for element '${wrapper.graphQlType.name}'" }
9797
}
9898

99+
// the legacy directives still back the deprecated getters of SchemaDirectiveWiringEnvironment
100+
@Suppress("DEPRECATION")
99101
private fun <T : GraphQLDirectiveContainer> buildEnvironment(wrapper: WiringWrapper<T>, element: T, appliedDirective: GraphQLAppliedDirective? = null): SchemaDirectiveWiringEnvironmentImpl<T> {
100102
val type = wrapper.graphQlType
101103
val directive = appliedDirective?.let { d -> type.directives.find { it.name == d.name } }

‎src/main/kotlin/graphql/kickstart/tools/directive/SchemaDirectiveWiringEnvironmentImpl.kt‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,12 @@ class SchemaDirectiveWiringEnvironmentImpl<T : GraphQLDirectiveContainer?>(
3838
}
3939

4040
override fun getElement(): T = element
41+
@Deprecated("Use getAppliedDirective() instead", ReplaceWith("appliedDirective"))
4142
override fun getDirective(): GraphQLDirective? = registeredDirective
4243
override fun getAppliedDirective(): GraphQLAppliedDirective? = registeredAppliedDirective
44+
@Deprecated("Use getAppliedDirectives() instead", ReplaceWith("appliedDirectives"))
4345
override fun getDirectives(): Map<String, GraphQLDirective> = LinkedHashMap(directives)
46+
@Deprecated("Use getAppliedDirective(directiveName) instead", ReplaceWith("getAppliedDirective(directiveName)"))
4447
override fun getDirective(directiveName: String): GraphQLDirective = directives[directiveName]!!
4548
override fun getAppliedDirectives(): Map<String, GraphQLAppliedDirective> = appliedDirectives
4649
override fun getAppliedDirective(directiveName: String): GraphQLAppliedDirective = appliedDirectives[directiveName]!!

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ internal class FieldResolverScanner(val options: SchemaParserOptions) {
7373
}
7474

7575
private fun missingFieldResolver(field: FieldDefinition, searches: List<Search>, scanProperties: Boolean): FieldResolver {
76+
@Suppress("DEPRECATION")
7677
return if (options.allowUnimplementedResolvers
7778
|| options.missingResolverDataFetcher != null
7879
|| options.missingResolverDataFetcherProvider != null) {

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ internal class MethodFieldResolver(
119119
"please insert it into the GraphQLContext map when building the ExecutionInput. " +
120120
"This warning will become an error in the future."
121121
)
122+
@Suppress("DEPRECATION")
122123
environment.getContext() // TODO: remove deprecated use in next major release
123124
}
124125
}

‎src/main/kotlin/graphql/kickstart/tools/resolver/MissingFieldResolver.kt‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ internal class MissingFieldResolver(
1111
options: SchemaParserOptions
1212
) : FieldResolver(field, FieldResolverScanner.Search(Any::class.java, MissingResolverInfo(), null), options) {
1313

14+
@Suppress("DEPRECATION")
1415
private val missingResolverDataFetcherProvider: MissingResolverDataFetcherProvider = options.missingResolverDataFetcherProvider
1516
?: MissingResolverDataFetcherProvider {
1617
_, options -> options.missingResolverDataFetcher ?: DataFetcher<Any> { TODO("Schema resolver not implemented") }

‎src/main/kotlin/graphql/kickstart/tools/util/Utils.kt‎

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import graphql.schema.DataFetchingEnvironment
77
import kotlinx.coroutines.CoroutineScope
88
import kotlinx.coroutines.Dispatchers
99
import org.apache.commons.lang3.reflect.TypeUtils
10+
import org.slf4j.LoggerFactory
11+
import java.lang.invoke.MethodHandles
1012
import java.lang.reflect.Method
1113
import java.lang.reflect.ParameterizedType
1214
import java.lang.reflect.Proxy
@@ -18,6 +20,8 @@ import kotlin.reflect.jvm.kotlinFunction
1820
* @author Andrew Potter
1921
*/
2022

23+
private val log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass())
24+
2125
internal typealias GraphQLRootResolver = GraphQLResolver<Void>
2226

2327
internal typealias JavaType = java.lang.reflect.Type
@@ -60,8 +64,23 @@ internal fun JavaType.futureValueType(): JavaType? =
6064
typeArgument(CompletionStage::class.java) ?: typeArgument(Future::class.java)
6165

6266
internal fun DataFetchingEnvironment.coroutineScope(): CoroutineScope {
67+
val scope: Any? = graphQlContext[CoroutineScope::class.java]
68+
if (scope is CoroutineScope) {
69+
return scope
70+
}
71+
72+
@Suppress("DEPRECATION")
6373
val context: Any? = this.getContext()
64-
return if (context is CoroutineScope) context else CoroutineScope(Dispatchers.Default)
74+
if (context is CoroutineScope) {
75+
log.warn(
76+
"Passing a CoroutineScope as the context object has been deprecated by graphql-java. " +
77+
"Please insert it into the GraphQLContext map under the CoroutineScope class key when building the ExecutionInput. " +
78+
"Support for the context object will be removed in the next major release."
79+
)
80+
return context
81+
}
82+
83+
return CoroutineScope(Dispatchers.Default)
6584
}
6685

6786
internal val Class<*>.declaredNonProxyMethods: List<JavaMethod>

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -629,6 +629,7 @@ class DirectiveTest {
629629
private fun recordMessage(environment: SchemaDirectiveWiringEnvironment<*>) {
630630
val name = environment.element.name
631631
appliedMessages[name] = environment.appliedDirective.getArgument("message")?.getValue<String>()
632+
@Suppress("DEPRECATION")
632633
legacyMessages[name] = environment.directive.getArgument("message")?.let { GraphQLArgument.getArgumentValue<String>(it) }
633634
}
634635
}

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

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import graphql.language.TypeName
1313
import graphql.schema.DataFetcher
1414
import graphql.schema.DataFetchingEnvironment
1515
import graphql.schema.DataFetchingEnvironmentImpl
16+
import kotlinx.coroutines.CoroutineScope
1617
import kotlinx.coroutines.Dispatchers
1718
import kotlinx.coroutines.ExperimentalCoroutinesApi
1819
import kotlinx.coroutines.Job
@@ -85,6 +86,43 @@ class MethodFieldResolverDataFetcherTest {
8586
}
8687
}
8788

89+
@Test
90+
fun `data fetcher executes suspend function in CoroutineScope from GraphQLContext`() {
91+
// setup
92+
val scopeClass = ScopeClass()
93+
val context = GraphQLContext.of(mapOf(CoroutineScope::class.java to scopeClass.scope))
94+
95+
val resolver = createFetcher("active", scopeClass)
96+
97+
// expect
98+
@Suppress("UNCHECKED_CAST")
99+
val future = resolver.get(createEnvironment(DataClass(), context = context)) as CompletableFuture<Boolean>
100+
assert(future.get())
101+
}
102+
103+
@Test
104+
fun `data fetcher executes suspend function in CoroutineScope from legacy context`() {
105+
// setup
106+
val scopeClass = ScopeClass()
107+
108+
val resolver = createFetcher("active", scopeClass)
109+
110+
// expect
111+
@Suppress("UNCHECKED_CAST")
112+
val future = resolver.get(createEnvironment(DataClass(), legacyContext = scopeClass.scope)) as CompletableFuture<Boolean>
113+
assert(future.get())
114+
}
115+
116+
class ScopeClass : GraphQLResolver<DataClass> {
117+
private val job = Job()
118+
val scope = CoroutineScope(job)
119+
120+
@Suppress("UNUSED_PARAMETER")
121+
suspend fun isActive(data: DataClass): Boolean {
122+
return coroutineContext[Job] == job.children.first()
123+
}
124+
}
125+
88126
@ExperimentalCoroutinesApi
89127
@Test
90128
fun `canceling subscription Publisher also cancels underlying Kotlin coroutine channel`() {
@@ -375,11 +413,16 @@ class MethodFieldResolverDataFetcherTest {
375413
private fun createEnvironment(
376414
source: Any = Object(),
377415
arguments: Map<String, Any> = emptyMap(),
378-
context: GraphQLContext = GraphQLContext.newContext().build()
416+
context: GraphQLContext = GraphQLContext.newContext().build(),
417+
legacyContext: Any? = null
379418
) = DataFetchingEnvironmentImpl.newDataFetchingEnvironment(buildExecutionContext(context))
380419
.source(source)
381420
.arguments(arguments)
382421
.graphQLContext(context)
422+
.apply {
423+
@Suppress("DEPRECATION")
424+
if (legacyContext != null) context(legacyContext)
425+
}
383426
.build()
384427

385428
private fun buildExecutionContext(context: GraphQLContext): ExecutionContext {

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ class MissingFieldResolverTest {
3232
}
3333

3434
@Test
35+
@Suppress("DEPRECATION")
3536
fun `should call missing resolver data fetcher if provided`() {
3637
val schema = SchemaParser.newParser()
3738
.schemaString(

0 commit comments

Comments
 (0)