Skip to content

Commit 08d2561

Browse files
authored
Merge branch 'master' into environment-coroutine-scope
2 parents be99494 + f973984 commit 08d2561

File tree

3 files changed

+74
-3
lines changed

3 files changed

+74
-3
lines changed

src/main/kotlin/com/coxautodev/graphql/tools/FieldResolverScanner.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import org.apache.commons.lang3.reflect.FieldUtils
99
import org.slf4j.LoggerFactory
1010
import java.lang.reflect.Modifier
1111
import java.lang.reflect.ParameterizedType
12+
import java.lang.reflect.Proxy
1213
import java.lang.reflect.Type
1314
import kotlin.reflect.full.valueParameters
1415
import kotlin.reflect.jvm.javaType
@@ -25,7 +26,7 @@ internal class FieldResolverScanner(val options: SchemaParserOptions) {
2526
private val log = LoggerFactory.getLogger(FieldResolverScanner::class.java)
2627

2728
fun getAllMethods(type: JavaType) =
28-
(type.unwrap().declaredMethods.toList()
29+
(type.unwrap().declaredNonProxyMethods.toList()
2930
+ ClassUtils.getAllInterfaces(type.unwrap()).flatMap { it.methods.toList() }
3031
+ ClassUtils.getAllSuperclasses(type.unwrap()).flatMap { it.methods.toList() })
3132
.asSequence()

src/main/kotlin/com/coxautodev/graphql/tools/Utils.kt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import kotlinx.coroutines.CoroutineScope
1111
import kotlinx.coroutines.GlobalScope
1212
import java.lang.reflect.Method
1313
import java.lang.reflect.ParameterizedType
14+
import java.lang.reflect.Proxy
1415

1516
/**
1617
* @author Andrew Potter
@@ -19,6 +20,7 @@ import java.lang.reflect.ParameterizedType
1920
internal typealias GraphQLRootResolver = GraphQLResolver<Void>
2021

2122
internal typealias JavaType = java.lang.reflect.Type
23+
internal typealias JavaMethod = java.lang.reflect.Method
2224
internal typealias GraphQLLangType = graphql.language.Type<*>
2325

2426
internal fun Type<*>.unwrap(): Type<*> = when (this) {
@@ -39,11 +41,21 @@ internal fun JavaType.unwrap(): Class<out Any> =
3941
}
4042

4143

44+
4245
internal fun DataFetchingEnvironment.coroutineScope(): CoroutineScope {
4346
val context: Any? = this.getContext()
4447
return if (context is CoroutineScope) context else GlobalScope
4548
}
4649

50+
internal val Class<*>.declaredNonProxyMethods: List<JavaMethod>
51+
get() {
52+
return when {
53+
Proxy.isProxyClass(this) -> emptyList()
54+
else -> this.declaredMethods.toList()
55+
}
56+
}
57+
58+
4759
/**
4860
* Simple heuristic to check is a method is a trivial data fetcher.
4961
*
@@ -61,3 +73,4 @@ internal fun isTrivialDataFetcher(method: Method): Boolean {
6173
private fun isBooleanGetter(method: Method) = (method.name.startsWith("is")
6274
&& (method.returnType == java.lang.Boolean::class.java)
6375
|| method.returnType == Boolean::class.java)
76+

src/test/kotlin/com/coxautodev/graphql/tools/MethodFieldResolverTest.kt

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,15 @@ import graphql.schema.Coercing
77
import graphql.schema.GraphQLScalarType
88
import org.junit.Assert
99
import org.junit.Test
10+
import java.lang.reflect.InvocationHandler
11+
import java.lang.reflect.Method
12+
import java.lang.reflect.Proxy
13+
import java.util.*
1014

1115
class MethodFieldResolverTest {
1216

1317
@Test
14-
fun `should handle scalar types as method input argument`() {
18+
fun shouldHandleScalarTypesAsMethodInputArgument() {
1519
val schema = SchemaParser.newParser()
1620
.schemaString("""
1721
scalar CustomScalar
@@ -44,7 +48,7 @@ class MethodFieldResolverTest {
4448
}
4549

4650
@Test
47-
fun `should handle lists of scalar types`() {
51+
fun shouldHandleListsOfScalarTypes() {
4852
val schema = SchemaParser.newParser()
4953
.schemaString("""
5054
scalar CustomScalar
@@ -76,6 +80,55 @@ class MethodFieldResolverTest {
7680
Assert.assertEquals(6, result.getData<Map<String, Any>>()["test"])
7781
}
7882

83+
@Test
84+
fun shouldHandleProxies() {
85+
val invocationHandler = object : InvocationHandler {
86+
override fun invoke(proxy: Any, method: Method, args: Array<out Any>): Any {
87+
return when (method.name) {
88+
"toString" -> "Proxy$" + System.identityHashCode(this)
89+
"hashCode" -> System.identityHashCode(this)
90+
"equals" -> Proxy.isProxyClass(args[0].javaClass)
91+
"test" -> (args[0] as List<*>).map { (it as CustomScalar).value.length }.sum()
92+
else -> UnsupportedOperationException()
93+
}
94+
}
95+
}
96+
97+
val resolver = Proxy.newProxyInstance(
98+
MethodFieldResolverTest::class.java.classLoader,
99+
arrayOf(Resolver::class.java, GraphQLQueryResolver::class.java),
100+
invocationHandler
101+
) as GraphQLQueryResolver
102+
103+
val schema = SchemaParser.newParser()
104+
.schemaString("""
105+
scalar CustomScalar
106+
type Query {
107+
test(input: [CustomScalar]): Int
108+
}
109+
""".trimIndent()
110+
)
111+
.scalars(customScalarType)
112+
.resolvers(resolver)
113+
.build()
114+
.makeExecutableSchema()
115+
116+
val gql = GraphQL.newGraphQL(schema).build()
117+
118+
val result = gql
119+
.execute(ExecutionInput.newExecutionInput()
120+
.query("""
121+
query Test(${"$"}input: [CustomScalar]) {
122+
test(input: ${"$"}input)
123+
}
124+
""".trimIndent())
125+
.variables(mapOf("input" to listOf("Foo", "Bar")))
126+
.context(Object())
127+
.root(Object()))
128+
129+
Assert.assertEquals(6, result.getData<Map<String, Any>>()["test"])
130+
}
131+
79132
/**
80133
* Custom Scalar Class type that doesn't work with Jackson serialization/deserialization
81134
*/
@@ -90,6 +143,10 @@ class MethodFieldResolverTest {
90143
}
91144
}
92145

146+
interface Resolver {
147+
fun test(scalars: List<CustomScalar>): Int
148+
}
149+
93150
private val customScalarType: GraphQLScalarType = GraphQLScalarType.newScalar()
94151
.name("CustomScalar")
95152
.description("customScalar")

0 commit comments

Comments
 (0)