Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ language: java
jdk:
- oraclejdk8
sudo: false
script: mvn clean verify
script:
- sh travis-build.sh
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# GraphQL Java Tools

[![TravisCI Build](https://travis-ci.org/graphql-java/graphql-java-tools.svg?branch=master)](https://travis-ci.org/graphql-java/graphql-java-tools)
[![TravisCI Build](https://travis-ci.org/graphql-java-kickstart/graphql-java-tools.svg?branch=master)](https://travis-ci.org/graphql-java-kickstart/graphql-java-tools)
[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.graphql-java/graphql-java-tools/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.graphql-java/graphql-java-tools)
[![Chat on Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/graphql-java/graphql-java)

Expand Down
33 changes: 11 additions & 22 deletions pom.xml
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.graphql-java</groupId>
<groupId>com.graphql-java-kickstart</groupId>
<artifactId>graphql-java-tools</artifactId>
<version>5.2.5-SNAPSHOT</version>
<version>5.3.0-SNAPSHOT</version>
<packaging>jar</packaging>

<name>GraphQL Java Tools</name>
Expand All @@ -15,7 +15,7 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
<kotlin.version>1.2.60</kotlin.version>
<kotlin.version>1.2.71</kotlin.version>
<jackson.version>2.9.6</jackson.version>

<!--This contends with dokka and creates two javadoc jars...-->
Expand Down Expand Up @@ -212,19 +212,6 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.jetbrains.dokka</groupId>
<artifactId>dokka-maven-plugin</artifactId>
<version>0.9.14</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>javadocJar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
Expand All @@ -247,19 +234,21 @@
<distributionManagement>
<snapshotRepository>
<id>bintray</id>
<url>https://api.bintray.com/maven/apottere/maven/graphql-java-tools</url>
<name>graphql-java-kickstart-graphql-java-tools</name>
<url>https://api.bintray.com/maven/graphql-java-kickstart/graphql-java-tools/graphql-java-tools/</url>
</snapshotRepository>

<!-- Released with: mvn release:clean release:prepare release:perform -B -e -Pbintray -->
<repository>
<id>bintray</id>
<url>https://api.bintray.com/maven/apottere/maven/graphql-java-tools/;publish=1</url>
<name>graphql-java-kickstart-graphql-java-tools</name>
<url>https://api.bintray.com/maven/graphql-java-kickstart/graphql-java-tools/graphql-java-tools/;publish=1</url>
</repository>
</distributionManagement>
<scm>
<connection>scm:git:git://github.com/graphql-java/graphql-java-tools.git</connection>
<developerConnection>scm:git:git@github.com:graphql-java/graphql-java-tools.git</developerConnection>
<url>http://github.com/graphql-java/graphql-java-tools</url>
<connection>scm:git:git://github.com/graphql-java-kickstart/graphql-java-tools.git</connection>
<developerConnection>scm:git:git@github.com:graphql-java-kickstart/graphql-java-tools.git</developerConnection>
<url>http://github.com/graphql-javakickstart/graphql-java-tools</url>
<tag>HEAD</tag>
</scm>
</project>
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import graphql.schema.TypeResolver
/**
* @author Andrew Potter
*/
abstract class DictionaryTypeResolver(private val dictionary: BiMap<Class<*>, TypeDefinition<*>>, private val types: Map<String, GraphQLObjectType>) : TypeResolver {
abstract class DictionaryTypeResolver(private val dictionary: BiMap<JavaType, TypeDefinition<*>>, private val types: Map<String, GraphQLObjectType>) : TypeResolver {
private fun <T> getTypeName(clazz: Class<T>): String? {
val name = dictionary[clazz]?.name

Expand All @@ -34,11 +34,11 @@ abstract class DictionaryTypeResolver(private val dictionary: BiMap<Class<*>, Ty
abstract fun getError(name: String): String
}

class InterfaceTypeResolver(dictionary: BiMap<Class<*>, TypeDefinition<*>>, private val thisInterface: GraphQLInterfaceType, types: List<GraphQLObjectType>) : DictionaryTypeResolver(dictionary, types.filter { it.interfaces.any { it.name == thisInterface.name } }.associateBy { it.name }) {
class InterfaceTypeResolver(dictionary: BiMap<JavaType, TypeDefinition<*>>, private val thisInterface: GraphQLInterfaceType, types: List<GraphQLObjectType>) : DictionaryTypeResolver(dictionary, types.filter { it.interfaces.any { it.name == thisInterface.name } }.associateBy { it.name }) {
override fun getError(name: String) = "Expected object type with name '$name' to implement interface '${thisInterface.name}', but it doesn't!"
}

class UnionTypeResolver(dictionary: BiMap<Class<*>, TypeDefinition<*>>, private val thisUnion: GraphQLUnionType, types: List<GraphQLObjectType>) : DictionaryTypeResolver(dictionary, types.filter { type -> thisUnion.types.any { it.name == type.name } }.associateBy { it.name }) {
class UnionTypeResolver(dictionary: BiMap<JavaType, TypeDefinition<*>>, private val thisUnion: GraphQLUnionType, types: List<GraphQLObjectType>) : DictionaryTypeResolver(dictionary, types.filter { type -> thisUnion.types.any { it.name == type.name } }.associateBy { it.name }) {
override fun getError(name: String) = "Expected object type with name '$name' to exist for union '${thisUnion.name}', but it doesn't!"
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ internal class FieldResolverScanner(val options: SchemaParserOptions) {
companion object {
private val log = LoggerFactory.getLogger(FieldResolverScanner::class.java)

fun getAllMethods(type: Class<*>) =
(type.declaredMethods.toList() + ClassUtils.getAllSuperclasses(type).flatMap { it.methods.toList() })
fun getAllMethods(type: JavaType) =
(type.unwrap().declaredMethods.toList() + ClassUtils.getAllSuperclasses(type.unwrap()).flatMap { it.methods.toList() })
.asSequence()
.filter { !it.isSynthetic }
.filter { !Modifier.isPrivate(it.modifiers) }
Expand Down Expand Up @@ -131,7 +131,7 @@ internal class FieldResolverScanner(val options: SchemaParserOptions) {
}

private fun findResolverProperty(field: FieldDefinition, search: Search) =
FieldUtils.getAllFields(search.type).find { it.name == field.name }
FieldUtils.getAllFields(search.type.unwrap()).find { it.name == field.name }

private fun getMissingFieldMessage(field: FieldDefinition, searches: List<Search>, scannedProperties: Boolean): String {
val signatures = mutableListOf("")
Expand All @@ -143,11 +143,11 @@ internal class FieldResolverScanner(val options: SchemaParserOptions) {

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

private fun getMissingMethodSignatures(field: FieldDefinition, search: Search, isBoolean: Boolean, scannedProperties: Boolean): List<String> {
val baseType = search.type
val baseType = search.type.unwrap()
val signatures = mutableListOf<String>()
val args = mutableListOf<String>()
val sep = ", "
Expand All @@ -172,7 +172,7 @@ internal class FieldResolverScanner(val options: SchemaParserOptions) {
return signatures
}

data class Search(val type: Class<*>, val resolverInfo: ResolverInfo, val source: Any?, val requiredFirstParameterType: Class<*>? = null, val allowBatched: Boolean = false)
data class Search(val type: JavaType, val resolverInfo: ResolverInfo, val source: Any?, val requiredFirstParameterType: Class<*>? = null, val allowBatched: Boolean = false)
}

class FieldResolverError(msg: String) : RuntimeException(msg)
82 changes: 59 additions & 23 deletions src/main/kotlin/com/coxautodev/graphql/tools/GenericType.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,18 @@ package com.coxautodev.graphql.tools

import com.google.common.primitives.Primitives
import org.apache.commons.lang3.reflect.TypeUtils
import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl
import sun.reflect.generics.reflectiveObjects.WildcardTypeImpl
import java.lang.reflect.ParameterizedType
import java.lang.reflect.TypeVariable

/**
* @author Andrew Potter
*/
open internal class GenericType(protected val mostSpecificType: JavaType, protected val options: SchemaParserOptions) {
internal open class GenericType(protected val mostSpecificType: JavaType, protected val options: SchemaParserOptions) {

fun isTypeAssignableFromRawClass(type: ParameterizedType, clazz: Class<*>) =
clazz.isAssignableFrom(getRawClass(type.rawType))
clazz.isAssignableFrom(getRawClass(type.rawType))

fun getRawClass() = getRawClass(mostSpecificType)

Expand All @@ -21,29 +22,30 @@ open internal class GenericType(protected val mostSpecificType: JavaType, protec
fun isAssignableFrom(type: JavaType) = TypeUtils.isAssignable(type, mostSpecificType)

fun relativeToPotentialParent(declaringType: JavaType): RelativeTo {
if(declaringType !is Class<*>) {
if (declaringType !is Class<*> || declaringType.isInterface) {
return relativeToType(declaringType)
}

val type = getGenericSuperType(mostSpecificType, declaringType)
if(type == null) {
if (type == null) {
error("Unable to find generic type of class ${TypeUtils.toString(declaringType)} relative to ${TypeUtils.toString(mostSpecificType)}")
} else {
return relativeToType(type)
}
}

fun relativeToType(declaringType: JavaType) = RelativeTo(declaringType, mostSpecificType, options)

fun getGenericInterface(targetInterface: Class<*>) = getGenericInterface(mostSpecificType, targetInterface)

private fun getGenericInterface(type: JavaType?, targetInterface: Class<*>): JavaType? {
if(type == null) {
if (type == null) {
return null
}

val raw = type as? Class<*> ?: getRawClass(type)

if(raw == targetInterface) {
if (raw == targetInterface) {
return type
}

Expand All @@ -59,54 +61,88 @@ open internal class GenericType(protected val mostSpecificType: JavaType, protec
fun getGenericSuperType(targetSuperClass: Class<*>) = getGenericSuperType(mostSpecificType, targetSuperClass)

private fun getGenericSuperType(type: JavaType?, targetSuperClass: Class<*>): JavaType? {
if(type == null) {
if (type == null) {
return null
}

val raw = type as? Class<*> ?: TypeUtils.getRawType(type, type)

if(raw == targetSuperClass) {
if (raw == targetSuperClass) {
return type
}

return getGenericSuperType(raw.genericSuperclass, targetSuperClass)
}

class RelativeTo(private val declaringType: JavaType, mostSpecificType: JavaType, options: SchemaParserOptions): GenericType(mostSpecificType, options) {
class RelativeTo(private val declaringType: JavaType, mostSpecificType: JavaType, options: SchemaParserOptions) : GenericType(mostSpecificType, options) {

/**
* Unwrap certain Java types to find the "real" class.
*/
fun unwrapGenericType(type: JavaType): JavaType {
return when(type) {
fun unwrapGenericType(javaType: JavaType): JavaType {
val type = replaceTypeVariable(javaType)
return when (type) {
is ParameterizedType -> {
val rawType = type.rawType
val genericType = options.genericWrappers.find { it.type == rawType } ?: return type

val typeArguments = type.actualTypeArguments
if(typeArguments.size <= genericType.index) {
if (typeArguments.size <= genericType.index) {
throw IndexOutOfBoundsException("Generic type '${TypeUtils.toString(type)}' does not have a type argument at index ${genericType.index}!")
}

val unwrapsTo = if (genericType.schemaWrapper != null) {
genericType.schemaWrapper.invoke(typeArguments[genericType.index])
} else {
typeArguments[genericType.index]
}

val unwrapsTo = genericType.schemaWrapper.invoke(typeArguments[genericType.index])
return unwrapGenericType(unwrapsTo)
}
is Class<*> -> if(type.isPrimitive) Primitives.wrap(type) else type
is TypeVariable<*> -> {
if(declaringType !is ParameterizedType) {
val parameterizedDeclaringType = parameterizedDeclaringTypeOrSuperType(declaringType)
if (parameterizedDeclaringType != null) {
unwrapGenericType(parameterizedDeclaringType, type)
} else {
error("Could not resolve type variable '${TypeUtils.toLongString(type)}' because declaring type is not parameterized: ${TypeUtils.toString(declaringType)}")
}

unwrapGenericType(TypeUtils.determineTypeArguments(getRawClass(mostSpecificType), declaringType)[type] ?: error("No type variable found for: ${TypeUtils.toLongString(type)}"))
}
is WildcardTypeImpl -> type.upperBounds.firstOrNull() ?: throw error("Unable to unwrap type, wildcard has no upper bound: $type")
is WildcardTypeImpl -> type.upperBounds.firstOrNull()
?: throw error("Unable to unwrap type, wildcard has no upper bound: $type")
is Class<*> -> if (type.isPrimitive) Primitives.wrap(type) else type
else -> error("Unable to unwrap type: $type")
}
}

private fun parameterizedDeclaringTypeOrSuperType(declaringType: JavaType): ParameterizedType? =
if (declaringType is ParameterizedType) {
declaringType
} else {
val superclass = declaringType.unwrap().genericSuperclass
if (superclass != null) {
parameterizedDeclaringTypeOrSuperType(superclass)
} else {
null
}
}

private fun unwrapGenericType(declaringType: ParameterizedType, type: TypeVariable<*>) =
unwrapGenericType(TypeUtils.determineTypeArguments(getRawClass(mostSpecificType), declaringType)[type]
?: error("No type variable found for: ${TypeUtils.toLongString(type)}"))

private fun replaceTypeVariable(type: JavaType): JavaType {
return when (type) {
is ParameterizedType -> {
val actualTypeArguments = type.actualTypeArguments.map { replaceTypeVariable(it) }.toTypedArray()
ParameterizedTypeImpl.make(type.rawType as Class<*>?, actualTypeArguments, type.ownerType)
}
is TypeVariable<*> -> {
if (declaringType is ParameterizedType) {
TypeUtils.getRawType(type, declaringType)
} else {
type
}
}
else -> {
type
}
}
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,24 @@ import graphql.language.NonNullType
import graphql.schema.DataFetcher
import graphql.schema.DataFetchingEnvironment
import java.lang.reflect.Method
import java.lang.reflect.ParameterizedType
import java.lang.reflect.TypeVariable
import java.util.*

/**
* @author Andrew Potter
*/
internal class MethodFieldResolver(field: FieldDefinition, search: FieldResolverScanner.Search, options: SchemaParserOptions, val method: Method) : FieldResolver(field, search, options, method.declaringClass) {
internal class MethodFieldResolver(field: FieldDefinition, search: FieldResolverScanner.Search, options: SchemaParserOptions, val method: Method) : FieldResolver(field, search, options, search.type) {

companion object {
fun isBatched(method: Method, search: FieldResolverScanner.Search): Boolean {
if (method.getAnnotation(Batched::class.java) != null) {
if (!search.allowBatched) {
throw ResolverError("The @Batched annotation is only allowed on non-root resolver methods, but it was found on ${search.type.name}#${method.name}!")
throw ResolverError("The @Batched annotation is only allowed on non-root resolver methods, but it was found on ${search.type.unwrap().name}#${method.name}!")
}

return true
}

return false
}
}
Expand Down Expand Up @@ -98,7 +99,8 @@ internal class MethodFieldResolver(field: FieldDefinition, search: FieldResolver

override fun scanForMatches(): List<TypeClassMatcher.PotentialMatch> {
val batched = isBatched(method, search)
val returnValueMatch = TypeClassMatcher.PotentialMatch.returnValue(field.type, method.genericReturnType, genericType, SchemaClassScanner.ReturnValueReference(method), batched)
val unwrappedGenericType = genericType.unwrapGenericType(method.genericReturnType)
val returnValueMatch = TypeClassMatcher.PotentialMatch.returnValue(field.type, unwrappedGenericType, genericType, SchemaClassScanner.ReturnValueReference(method), batched)

return field.inputValueDefinitions.mapIndexed { i, inputDefinition ->
TypeClassMatcher.PotentialMatch.parameterType(inputDefinition.type, getJavaMethodParameterType(i)!!, genericType, SchemaClassScanner.MethodParameterReference(method, i), batched)
Expand Down
Loading