Skip to content

Commit b114b0d

Browse files
committed
Ignore proxies when scanning for field resolvers.
1 parent 0ee4f39 commit b114b0d

File tree

3 files changed

+71
-3
lines changed

3 files changed

+71
-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: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import graphql.language.ObjectTypeDefinition
77
import graphql.language.ObjectTypeExtensionDefinition
88
import graphql.language.Type
99
import java.lang.reflect.ParameterizedType
10+
import java.lang.reflect.Proxy
1011

1112
/**
1213
* @author Andrew Potter
@@ -15,6 +16,7 @@ import java.lang.reflect.ParameterizedType
1516
internal typealias GraphQLRootResolver = GraphQLResolver<Void>
1617

1718
internal typealias JavaType = java.lang.reflect.Type
19+
internal typealias JavaMethod = java.lang.reflect.Method
1820
internal typealias GraphQLLangType = graphql.language.Type<*>
1921

2022
internal fun Type<*>.unwrap(): Type<*> = when (this) {
@@ -33,3 +35,11 @@ internal fun JavaType.unwrap(): Class<out Any> =
3335
} else {
3436
this as Class<*>
3537
}
38+
39+
internal val Class<*>.declaredNonProxyMethods: List<JavaMethod>
40+
get() {
41+
return when {
42+
Proxy.isProxyClass(this) -> emptyList()
43+
else -> this.declaredMethods.toList()
44+
}
45+
}

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)