Skip to content

Commit 67aefa7

Browse files
committed
Refactored parsing schemas to include correct source names and locations
fix #162
1 parent 1edce27 commit 67aefa7

File tree

5 files changed

+215
-131
lines changed

5 files changed

+215
-131
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
@@ -138,7 +138,8 @@ internal class FieldResolverScanner(val options: SchemaParserOptions) {
138138
signatures.addAll(getMissingMethodSignatures(field, search, isBoolean, scannedProperties))
139139
}
140140

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 ")}"
141+
val sourceLocation = if (field.sourceLocation != null) "${field.sourceLocation.sourceName}:${field.sourceLocation.line}" else "<unknown>"
142+
return "No method${if (scannedProperties) " or field" else ""} found as defined in $sourceLocation with any of the following signatures (with or without one of $allowedLastArgumentTypes as the last argument), in priority order:\n${signatures.joinToString("\n ")}"
142143
}
143144

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

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ import kotlin.reflect.KClass
4646
class SchemaParser internal constructor(scanResult: ScannedSchemaObjects) {
4747

4848
companion object {
49-
val DEFAULT_DEPRECATION_MESSAGE = "No longer supported"
49+
const val DEFAULT_DEPRECATION_MESSAGE = "No longer supported"
5050

5151
@JvmStatic fun newParser() = SchemaParserBuilder()
5252
internal fun getDocumentation(node: AbstractNode<*>): String? = node.comments?.filter {

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

Lines changed: 92 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import com.fasterxml.jackson.databind.ObjectMapper
44
import com.google.common.collect.BiMap
55
import com.google.common.collect.HashBiMap
66
import com.google.common.collect.Maps
7+
import graphql.language.Document
78
import graphql.parser.Parser
89
import graphql.schema.DataFetchingEnvironment
910
import graphql.schema.GraphQLScalarType
@@ -22,6 +23,7 @@ import kotlin.reflect.KClass
2223
class SchemaParserBuilder constructor(private val dictionary: SchemaParserDictionary = SchemaParserDictionary()) {
2324

2425
private val schemaString = StringBuilder()
26+
private val files = mutableListOf<String>()
2527
private val resolvers = mutableListOf<GraphQLResolver<*>>()
2628
private val scalars = mutableListOf<GraphQLScalarType>()
2729
private var options = SchemaParserOptions.defaultOptions()
@@ -37,16 +39,17 @@ class SchemaParserBuilder constructor(private val dictionary: SchemaParserDictio
3739
* Add a GraphQL Schema file from the classpath.
3840
*/
3941
fun file(filename: String) = this.apply {
40-
this.schemaString(java.io.BufferedReader(java.io.InputStreamReader(
41-
object : Any() {}.javaClass.classLoader.getResourceAsStream(filename) ?: throw java.io.FileNotFoundException("classpath:$filename")
42-
)).readText())
42+
files.add(filename)
4343
}
4444

4545
/**
4646
* Add a GraphQL schema string directly.
4747
*/
4848
fun schemaString(string: String) = this.apply {
49-
schemaString.append("\n").append(string)
49+
if (!schemaString.isEmpty()) {
50+
schemaString.append("\n")
51+
}
52+
schemaString.append(string)
5053
}
5154

5255
/**
@@ -134,21 +137,40 @@ class SchemaParserBuilder constructor(private val dictionary: SchemaParserDictio
134137
* Scan for classes with the supplied schema and dictionary. Used for testing.
135138
*/
136139
private fun scan(): ScannedSchemaObjects {
137-
val document = try {
138-
Parser().parseDocument(this.schemaString.toString())
140+
val definitions = parseDefinitions()
141+
val customScalars = scalars.associateBy { it.name }
142+
143+
return SchemaClassScanner(dictionary.getDictionary(), definitions, resolvers, customScalars, options)
144+
.scanForClasses()
145+
}
146+
147+
private fun parseDefinitions() = parseDocuments().flatMap { it.definitions }
148+
149+
private fun parseDocuments(): List<Document> {
150+
val parser = Parser()
151+
val documents = mutableListOf<Document>()
152+
try {
153+
files.forEach { documents.add(parser.parseDocument(readFile(it), it)) }
154+
155+
if (!schemaString.isEmpty()) {
156+
documents.add(parser.parseDocument(this.schemaString.toString()))
157+
}
139158
} catch (pce: ParseCancellationException) {
140159
val cause = pce.cause
141-
if(cause != null && cause is RecognitionException) {
160+
if (cause != null && cause is RecognitionException) {
142161
throw InvalidSchemaError(pce, cause)
143162
} else {
144163
throw pce
145164
}
146165
}
166+
return documents
167+
}
147168

148-
val definitions = document.definitions
149-
val customScalars = scalars.associateBy { it.name }
150-
151-
return SchemaClassScanner(dictionary.getDictionary(), definitions, resolvers, customScalars, options).scanForClasses()
169+
private fun readFile(filename: String): String {
170+
return java.io.BufferedReader(java.io.InputStreamReader(
171+
object : Any() {}.javaClass.classLoader.getResourceAsStream(filename)
172+
?: throw java.io.FileNotFoundException("classpath:$filename")
173+
)).readText()
152174
}
153175

154176
/**
@@ -157,7 +179,7 @@ class SchemaParserBuilder constructor(private val dictionary: SchemaParserDictio
157179
fun build() = SchemaParser(scan())
158180
}
159181

160-
class InvalidSchemaError(pce: ParseCancellationException, private val recognitionException: RecognitionException): RuntimeException(pce) {
182+
class InvalidSchemaError(pce: ParseCancellationException, private val recognitionException: RecognitionException) : RuntimeException(pce) {
161183
override val message: String?
162184
get() = "Invalid schema provided (${recognitionException.javaClass.name}) at: ${recognitionException.offendingToken}"
163185
}
@@ -227,8 +249,11 @@ class SchemaParserDictionary {
227249

228250
data class SchemaParserOptions internal constructor(val contextClass: Class<*>?, val genericWrappers: List<GenericWrapper>, val allowUnimplementedResolvers: Boolean, val objectMapperProvider: PerFieldObjectMapperProvider, val proxyHandlers: List<ProxyHandler>, val preferGraphQLResolver: Boolean) {
229251
companion object {
230-
@JvmStatic fun newOptions() = Builder()
231-
@JvmStatic fun defaultOptions() = Builder().build()
252+
@JvmStatic
253+
fun newOptions() = Builder()
254+
255+
@JvmStatic
256+
fun defaultOptions() = Builder().build()
232257
}
233258

234259
class Builder {
@@ -247,7 +272,7 @@ data class SchemaParserOptions internal constructor(val contextClass: Class<*>?,
247272
fun contextClass(contextClass: KClass<*>) = this.apply {
248273
this.contextClass = contextClass.java
249274
}
250-
275+
251276
fun genericWrappers(genericWrappers: List<GenericWrapper>) = this.apply {
252277
this.genericWrappers.addAll(genericWrappers)
253278
}
@@ -271,6 +296,7 @@ data class SchemaParserOptions internal constructor(val contextClass: Class<*>?,
271296
fun objectMapperConfigurer(objectMapperConfigurer: ObjectMapperConfigurer) = this.apply {
272297
this.objectMapperProvider = PerFieldConfiguringObjectMapperProvider(objectMapperConfigurer)
273298
}
299+
274300
fun objectMapperProvider(objectMapperProvider: PerFieldObjectMapperProvider) = this.apply {
275301
this.objectMapperProvider = objectMapperProvider
276302
}
@@ -284,12 +310,12 @@ data class SchemaParserOptions internal constructor(val contextClass: Class<*>?,
284310
}
285311

286312
fun build(): SchemaParserOptions {
287-
val wrappers = if(useDefaultGenericWrappers) {
313+
val wrappers = if (useDefaultGenericWrappers) {
288314
genericWrappers + listOf(
289-
GenericWrapper(Future::class, 0),
290-
GenericWrapper(CompletableFuture::class, 0),
291-
GenericWrapper(CompletionStage::class, 0),
292-
GenericWrapper(Publisher::class, 0)
315+
GenericWrapper(Future::class, 0),
316+
GenericWrapper(CompletableFuture::class, 0),
317+
GenericWrapper(CompletionStage::class, 0),
318+
GenericWrapper(Publisher::class, 0)
293319
)
294320
} else {
295321
genericWrappers
@@ -300,75 +326,78 @@ data class SchemaParserOptions internal constructor(val contextClass: Class<*>?,
300326
}
301327

302328
data class GenericWrapper(
303-
val type: Class<*>,
304-
val index: Int,
305-
val transformer: (Any, DataFetchingEnvironment) -> Any? = { x, _ -> x },
306-
val schemaWrapper: (JavaType) -> JavaType = { x -> x }
329+
val type: Class<*>,
330+
val index: Int,
331+
val transformer: (Any, DataFetchingEnvironment) -> Any? = { x, _ -> x },
332+
val schemaWrapper: (JavaType) -> JavaType = { x -> x }
307333
) {
308334

309-
constructor(type: Class<*>, index: Int): this(type, index, { x, _ -> x })
310-
constructor(type: KClass<*>, index: Int): this(type.java, index, { x, _ -> x })
335+
constructor(type: Class<*>, index: Int) : this(type, index, { x, _ -> x })
336+
constructor(type: KClass<*>, index: Int) : this(type.java, index, { x, _ -> x })
311337

312338
companion object {
313339

314340
@Suppress("UNCHECKED_CAST")
315-
@JvmStatic fun <T> withTransformer(
316-
type: Class<T>,
317-
index: Int,
318-
transformer: (T, DataFetchingEnvironment) -> Any?,
319-
schemaWrapper: (JavaType) -> JavaType = { x -> x }
320-
): GenericWrapper where T: Any {
341+
@JvmStatic
342+
fun <T> withTransformer(
343+
type: Class<T>,
344+
index: Int,
345+
transformer: (T, DataFetchingEnvironment) -> Any?,
346+
schemaWrapper: (JavaType) -> JavaType = { x -> x }
347+
): GenericWrapper where T : Any {
321348
return GenericWrapper(type, index, transformer as (Any, DataFetchingEnvironment) -> Any?, schemaWrapper)
322349
}
323-
350+
324351
fun <T> withTransformer(
325-
type: KClass<T>,
326-
index: Int,
327-
transformer: (T, DataFetchingEnvironment) -> Any?,
328-
schemaWrapper: (JavaType) -> JavaType = { x -> x }
329-
): GenericWrapper where T: Any {
352+
type: KClass<T>,
353+
index: Int,
354+
transformer: (T, DataFetchingEnvironment) -> Any?,
355+
schemaWrapper: (JavaType) -> JavaType = { x -> x }
356+
): GenericWrapper where T : Any {
330357
return withTransformer(type.java, index, transformer, schemaWrapper)
331358
}
332359

333-
@JvmStatic fun <T> withTransformer(
334-
type: Class<T>,
335-
index: Int,
336-
transformer: (T) -> Any?,
337-
schemaWrapper: (JavaType) -> JavaType = { x -> x }
338-
): GenericWrapper where T: Any {
360+
@JvmStatic
361+
fun <T> withTransformer(
362+
type: Class<T>,
363+
index: Int,
364+
transformer: (T) -> Any?,
365+
schemaWrapper: (JavaType) -> JavaType = { x -> x }
366+
): GenericWrapper where T : Any {
339367
return withTransformer(type, index, { x, _ -> transformer.invoke(x) }, schemaWrapper)
340368
}
341369

342370
fun <T> withTransformer(
343-
type: KClass<T>,
344-
index: Int,
345-
transformer: (T) -> Any?,
346-
schemaWrapper: (JavaType) -> JavaType = { x -> x }
347-
): GenericWrapper where T: Any {
371+
type: KClass<T>,
372+
index: Int,
373+
transformer: (T) -> Any?,
374+
schemaWrapper: (JavaType) -> JavaType = { x -> x }
375+
): GenericWrapper where T : Any {
348376
return withTransformer(type.java, index, transformer, schemaWrapper)
349377
}
350378

351-
@JvmStatic fun <T> listCollectionWithTransformer(
352-
type: Class<T>,
353-
index: Int,
354-
transformer: (T) -> Any?
355-
): GenericWrapper where T: Any {
379+
@JvmStatic
380+
fun <T> listCollectionWithTransformer(
381+
type: Class<T>,
382+
index: Int,
383+
transformer: (T) -> Any?
384+
): GenericWrapper where T : Any {
356385
return withTransformer(
357-
type,
358-
index,
359-
transformer,
360-
{ innerType -> ParameterizedTypeImpl.make(List::class.java, arrayOf(innerType), null) }
386+
type,
387+
index,
388+
transformer,
389+
{ innerType -> ParameterizedTypeImpl.make(List::class.java, arrayOf(innerType), null) }
361390
)
362391
}
363392

364393
fun <T> listCollectionWithTransformer(
365-
type: KClass<T>,
366-
index: Int,
367-
transformer: (T) -> Any?
368-
): GenericWrapper where T: Any {
394+
type: KClass<T>,
395+
index: Int,
396+
transformer: (T) -> Any?
397+
): GenericWrapper where T : Any {
369398
return listCollectionWithTransformer(type.java, index, transformer)
370399
}
371400
}
372-
401+
373402
}
374403
}

0 commit comments

Comments
 (0)