Skip to content

Commit 1f73808

Browse files
sp00mapottere
authored andcommitted
Context injection (graphql-java-kickstart#121)
* Allow passing the context type when creating a SchemaParser * Allow injecting the context as the last argument (fix graphql-java-kickstart#119) * Test the context injection * Improve test accuracy * Improve formatting * Improve formatting
1 parent 31fb371 commit 1f73808

File tree

4 files changed

+65
-14
lines changed

4 files changed

+65
-14
lines changed

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import java.lang.reflect.ParameterizedType
1515
*/
1616
internal class FieldResolverScanner(val options: SchemaParserOptions) {
1717

18+
private val allowedLastArgumentTypes = listOfNotNull(DataFetchingEnvironment::class.java, options.contextClass)
19+
1820
companion object {
1921
private val log = LoggerFactory.getLogger(FieldResolverScanner::class.java)
2022

@@ -103,7 +105,7 @@ internal class FieldResolverScanner(val options: SchemaParserOptions) {
103105
true
104106
}
105107

106-
val correctParameterCount = method.parameterCount == requiredCount || (method.parameterCount == (requiredCount + 1) && method.parameterTypes.last() == DataFetchingEnvironment::class.java)
108+
val correctParameterCount = method.parameterCount == requiredCount || (method.parameterCount == (requiredCount + 1) && allowedLastArgumentTypes.contains(method.parameterTypes.last()))
107109
return correctParameterCount && appropriateFirstParameter
108110
}
109111

@@ -136,7 +138,7 @@ internal class FieldResolverScanner(val options: SchemaParserOptions) {
136138
signatures.addAll(getMissingMethodSignatures(field, search, isBoolean, scannedProperties))
137139
}
138140

139-
return "No method${if (scannedProperties) " or field" else ""} found with any of the following signatures (with or without ${DataFetchingEnvironment::class.java.name} as the last argument), in priority order:\n${signatures.joinToString("\n ")}"
141+
return "No method${if (scannedProperties) " or field" else ""} found with any of the following signatures (with or without one of $allowedLastArgumentTypes as the last argument), in priority order:\n${signatures.joinToString("\n ")}"
140142
}
141143

142144
private fun getMissingMethodSignatures(field: FieldDefinition, search: Search, isBoolean: Boolean, scannedProperties: Boolean): List<String> {

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

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import graphql.language.NonNullType
1111
import graphql.schema.DataFetcher
1212
import graphql.schema.DataFetchingEnvironment
1313
import java.lang.reflect.Method
14-
import java.util.Optional
14+
import java.util.*
1515

1616
/**
1717
* @author Andrew Potter
@@ -32,7 +32,7 @@ internal class MethodFieldResolver(field: FieldDefinition, search: FieldResolver
3232
}
3333
}
3434

35-
private val dataFetchingEnvironment = method.parameterCount == (field.inputValueDefinitions.size + getIndexOffset() + 1)
35+
private val additionalLastArgument = method.parameterCount == (field.inputValueDefinitions.size + getIndexOffset() + 1)
3636

3737
override fun createDataFetcher(): DataFetcher<*> {
3838
val batched = isBatched(method, search)
@@ -82,9 +82,14 @@ internal class MethodFieldResolver(field: FieldDefinition, search: FieldResolver
8282
})
8383
}
8484

85-
// Add DataFetchingEnvironment argument
86-
if(this.dataFetchingEnvironment) {
87-
args.add({ environment -> environment })
85+
// Add DataFetchingEnvironment/Context argument
86+
if(this.additionalLastArgument) {
87+
val lastArgumentType = this.method.parameterTypes.last()
88+
when(lastArgumentType) {
89+
null -> throw ResolverError("Expected at least one argument but got none, this is most likely a bug with graphql-java-tools")
90+
options.contextClass -> args.add({ environment -> environment.getContext() })
91+
else -> args.add({ environment -> environment })
92+
}
8893
}
8994

9095
return if(batched) {

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

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -223,19 +223,28 @@ class SchemaParserDictionary {
223223
}
224224
}
225225

226-
data class SchemaParserOptions internal constructor(val genericWrappers: List<GenericWrapper>, val allowUnimplementedResolvers: Boolean, val objectMapperConfigurer: ObjectMapperConfigurer, val proxyHandlers: List<ProxyHandler>) {
226+
data class SchemaParserOptions internal constructor(val contextClass: Class<*>?, val genericWrappers: List<GenericWrapper>, val allowUnimplementedResolvers: Boolean, val objectMapperConfigurer: ObjectMapperConfigurer, val proxyHandlers: List<ProxyHandler>) {
227227
companion object {
228228
@JvmStatic fun newOptions() = Builder()
229229
@JvmStatic fun defaultOptions() = Builder().build()
230230
}
231231

232232
class Builder {
233+
private var contextClass: Class<*>? = null
233234
private val genericWrappers: MutableList<GenericWrapper> = mutableListOf()
234235
private var useDefaultGenericWrappers = true
235236
private var allowUnimplementedResolvers = false
236237
private var objectMapperConfigurer: ObjectMapperConfigurer = ObjectMapperConfigurer { _, _ -> }
237238
private val proxyHandlers: MutableList<ProxyHandler> = mutableListOf(Spring4AopProxyHandler(), GuiceAopProxyHandler(), JavassistProxyHandler())
238239

240+
fun contextClass(contextClass: Class<*>) = this.apply {
241+
this.contextClass = contextClass
242+
}
243+
244+
fun contextClass(contextClass: KClass<*>) = this.apply {
245+
this.contextClass = contextClass.java
246+
}
247+
239248
fun genericWrappers(genericWrappers: List<GenericWrapper>) = this.apply {
240249
this.genericWrappers.addAll(genericWrappers)
241250
}
@@ -276,7 +285,7 @@ data class SchemaParserOptions internal constructor(val genericWrappers: List<Ge
276285
genericWrappers
277286
}
278287

279-
return SchemaParserOptions(wrappers, allowUnimplementedResolvers, objectMapperConfigurer, proxyHandlers)
288+
return SchemaParserOptions(contextClass, wrappers, allowUnimplementedResolvers, objectMapperConfigurer, proxyHandlers)
280289
}
281290
}
282291

src/test/groovy/com/coxautodev/graphql/tools/MethodFieldResolverDataFetcherSpec.groovy

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,6 @@ import spock.lang.Specification
1414
*/
1515
class MethodFieldResolverDataFetcherSpec extends Specification {
1616

17-
static final FieldResolverScanner fieldResolverScanner = new FieldResolverScanner(SchemaParserOptions.defaultOptions())
18-
1917
def "data fetcher throws exception if resolver has too many arguments"() {
2018
when:
2119
createFetcher("active", new GraphQLQueryResolver() {
@@ -104,6 +102,33 @@ class MethodFieldResolverDataFetcherSpec extends Specification {
104102
resolver.get(createEnvironment(new DataClass()))
105103
}
106104
105+
def "data fetcher passes environment if method has extra argument even if context is specified"() {
106+
setup:
107+
def options = SchemaParserOptions.newOptions().contextClass(ContextClass).build()
108+
def resolver = createFetcher(options, "active", new GraphQLResolver<DataClass>() {
109+
boolean isActive(DataClass dataClass, DataFetchingEnvironment env) {
110+
env instanceof DataFetchingEnvironment
111+
}
112+
})
113+
114+
expect:
115+
resolver.get(createEnvironment(new ContextClass(), new DataClass()))
116+
}
117+
118+
def "data fetcher passes context if method has extra argument and context is specified"() {
119+
setup:
120+
def context = new ContextClass()
121+
def options = SchemaParserOptions.newOptions().contextClass(ContextClass).build()
122+
def resolver = createFetcher(options, "active", new GraphQLResolver<DataClass>() {
123+
boolean isActive(DataClass dataClass, ContextClass ctx) {
124+
ctx == context
125+
}
126+
})
127+
128+
expect:
129+
resolver.get(createEnvironment(context, new DataClass()))
130+
}
131+
107132
def "data fetcher marshalls input object if required"() {
108133
setup:
109134
def name = "correct name"
@@ -158,18 +183,25 @@ class MethodFieldResolverDataFetcherSpec extends Specification {
158183
}
159184
160185
private static DataFetcher createFetcher(String methodName, List<InputValueDefinition> arguments = [], GraphQLResolver<?> resolver) {
186+
return createFetcher(SchemaParserOptions.defaultOptions(), methodName, arguments, resolver)
187+
}
188+
189+
private static DataFetcher createFetcher(SchemaParserOptions options, String methodName, List<InputValueDefinition> arguments = [], GraphQLResolver<?> resolver) {
161190
def field = new FieldDefinition(methodName, new TypeName('Boolean')).with { getInputValueDefinitions().addAll(arguments); it }
162-
def options = SchemaParserOptions.defaultOptions()
163191
164-
fieldResolverScanner.findFieldResolver(field, resolver instanceof GraphQLQueryResolver ? new RootResolverInfo([resolver], options) : new NormalResolverInfo(resolver, options)).createDataFetcher()
192+
new FieldResolverScanner(options).findFieldResolver(field, resolver instanceof GraphQLQueryResolver ? new RootResolverInfo([resolver], options) : new NormalResolverInfo(resolver, options)).createDataFetcher()
165193
}
166194
167195
private static DataFetchingEnvironment createEnvironment(Map<String, Object> arguments = [:]) {
168196
createEnvironment(new Object(), arguments)
169197
}
170198
171199
private static DataFetchingEnvironment createEnvironment(Object source, Map<String, Object> arguments = [:]) {
172-
new DataFetchingEnvironmentImpl(source, arguments, null, null, null, null, null, null, null, null, null, null, null)
200+
createEnvironment(null, source, arguments)
201+
}
202+
203+
private static DataFetchingEnvironment createEnvironment(Object context, Object source, Map<String, Object> arguments = [:]) {
204+
new DataFetchingEnvironmentImpl(source, arguments, context, null, null, null, null, null, null, null, null, null, null)
173205
}
174206
}
175207
@@ -184,3 +216,6 @@ class DataClass {
184216
class InputClass {
185217
String name
186218
}
219+
220+
class ContextClass {
221+
}

0 commit comments

Comments
 (0)