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

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
<dependency>
<groupId>com.graphql-java</groupId>
<artifactId>graphql-java</artifactId>
<version>6.0</version>
<version>8.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
Expand Down
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<Class<*>, TypeDefinition<*>>, private val types: Map<String, GraphQLObjectType>) : TypeResolver {

override fun getType(env: TypeResolutionEnvironment): GraphQLObjectType? {
val clazz = env.getObject<Any>().javaClass
Expand All @@ -23,11 +23,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<Class<*>, 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<Class<*>, 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 @@ -11,11 +11,11 @@ import graphql.schema.GraphQLScalarType
*/
internal data class ScannedSchemaObjects(
val dictionary: TypeClassDictionary,
val definitions: Set<TypeDefinition>,
val definitions: Set<TypeDefinition<*>>,
val customScalars: CustomScalarMap,
val rootInfo: RootTypeInfo,
val fieldResolversByType: Map<ObjectTypeDefinition, MutableMap<FieldDefinition, FieldResolver>>
)

internal typealias TypeClassDictionary = BiMap<TypeDefinition, Class<*>>
internal typealias TypeClassDictionary = BiMap<TypeDefinition<*>, Class<*>>
internal typealias CustomScalarMap = Map<String, GraphQLScalarType>
29 changes: 16 additions & 13 deletions src/main/kotlin/com/coxautodev/graphql/tools/SchemaClassScanner.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,29 @@ import com.google.common.collect.BiMap
import com.google.common.collect.HashBiMap
import com.google.common.collect.Maps
import graphql.language.Definition
import graphql.language.FieldDefinition import graphql.language.InputObjectTypeDefinition
import graphql.language.FieldDefinition
import graphql.language.InputObjectTypeDefinition
import graphql.language.InputValueDefinition
import graphql.language.InterfaceTypeDefinition
import graphql.language.ObjectTypeDefinition
import graphql.language.ObjectTypeExtensionDefinition
import graphql.language.ScalarTypeDefinition
import graphql.language.SchemaDefinition
import graphql.language.TypeDefinition
import graphql.language.TypeExtensionDefinition
import graphql.language.TypeName
import graphql.language.UnionTypeDefinition
import graphql.schema.GraphQLDirective
import graphql.schema.GraphQLScalarType
import graphql.schema.idl.ScalarInfo
import org.slf4j.LoggerFactory
import java.lang.reflect.Field
import java.lang.reflect.Method
import java.util.ArrayList

/**
* @author Andrew Potter
*/
internal class SchemaClassScanner(initialDictionary: BiMap<String, Class<*>>, allDefinitions: List<Definition>, resolvers: List<GraphQLResolver<*>>, private val scalars: CustomScalarMap, private val options: SchemaParserOptions) {
internal class SchemaClassScanner(initialDictionary: BiMap<String, Class<*>>, allDefinitions: List<Definition<*>>, resolvers: List<GraphQLResolver<*>>, private val scalars: CustomScalarMap, private val options: SchemaParserOptions) {

companion object {
val log = LoggerFactory.getLogger(SchemaClassScanner::class.java)!!
Expand All @@ -39,17 +42,17 @@ internal class SchemaClassScanner(initialDictionary: BiMap<String, Class<*>>, al
private val resolverInfosByDataClass = this.resolverInfos.associateBy { it.dataClassType }

private val initialDictionary = initialDictionary.mapValues { InitialDictionaryEntry(it.value) }
private val extensionDefinitions = allDefinitions.filterIsInstance<TypeExtensionDefinition>()
private val extensionDefinitions = allDefinitions.filterIsInstance<ObjectTypeExtensionDefinition>()

private val definitionsByName = (allDefinitions.filterIsInstance<TypeDefinition>() - extensionDefinitions).associateBy { it.name }
private val definitionsByName = (allDefinitions.filterIsInstance<TypeDefinition<*>>() - extensionDefinitions).associateBy { it.name }
private val objectDefinitions = (allDefinitions.filterIsInstance<ObjectTypeDefinition>() - extensionDefinitions)
private val objectDefinitionsByName = objectDefinitions.associateBy { it.name }
private val interfaceDefinitionsByName = allDefinitions.filterIsInstance<InterfaceTypeDefinition>().associateBy { it.name }

private val fieldResolverScanner = FieldResolverScanner(options)
private val typeClassMatcher = TypeClassMatcher(definitionsByName)
private val dictionary = mutableMapOf<TypeDefinition, DictionaryEntry>()
private val unvalidatedTypes = mutableSetOf<TypeDefinition>()
private val dictionary = mutableMapOf<TypeDefinition<*>, DictionaryEntry>()
private val unvalidatedTypes = mutableSetOf<TypeDefinition<*>>()
private val queue = linkedSetOf<QueueItem>()

private val fieldResolversByType = mutableMapOf<ObjectTypeDefinition, MutableMap<FieldDefinition, FieldResolver>>()
Expand Down Expand Up @@ -131,7 +134,7 @@ internal class SchemaClassScanner(initialDictionary: BiMap<String, Class<*>>, al
// Input types can also be excluded from the dictionary, since it's only used for interfaces, unions, and enums.
// Union types can also be excluded, as their possible types are resolved recursively later
val dictionary = try {
Maps.unmodifiableBiMap(HashBiMap.create<TypeDefinition, Class<*>>().also {
Maps.unmodifiableBiMap(HashBiMap.create<TypeDefinition<*>, Class<*>>().also {
dictionary.filter { it.value.typeClass != null && it.key !is InputObjectTypeDefinition && it.key !is UnionTypeDefinition}.mapValuesTo(it) { it.value.typeClass }
})
} catch (t: Throwable) {
Expand All @@ -142,7 +145,7 @@ internal class SchemaClassScanner(initialDictionary: BiMap<String, Class<*>>, al
// Ensure all scalar definitions have implementations and add the definition to those.
val scalars = scalarDefinitions.filter { !ScalarInfo.STANDARD_SCALAR_DEFINITIONS.containsKey(it.name) }.map { definition ->
val provided = scalars[definition.name] ?: throw SchemaClassScannerError("Expected a user-defined GraphQL scalar type with name '${definition.name}' but found none!")
GraphQLScalarType(provided.name, SchemaParser.getDocumentation(definition) ?: provided.description, provided.coercing, definition)
GraphQLScalarType(provided.name, SchemaParser.getDocumentation(definition) ?: provided.description, provided.coercing, listOf(), definition)
}.associateBy { it.name!! }

(definitionsByName.values - observedDefinitions).forEach { definition ->
Expand Down Expand Up @@ -236,7 +239,7 @@ internal class SchemaClassScanner(initialDictionary: BiMap<String, Class<*>>, al
/**
* Enter a found type into the dictionary if it doesn't exist yet, add a reference pointing back to where it was discovered.
*/
private fun handleFoundType(type: TypeDefinition, clazz: Class<*>?, reference: Reference) {
private fun handleFoundType(type: TypeDefinition<*>, clazz: Class<*>?, reference: Reference) {
val realEntry = dictionary.getOrPut(type, { DictionaryEntry() })
var typeWasSet = false

Expand All @@ -259,7 +262,7 @@ internal class SchemaClassScanner(initialDictionary: BiMap<String, Class<*>>, al
/**
* Handle a newly found type, adding it to the list of actually used types and putting it in the scanning queue if it's an object type.
*/
private fun handleNewType(graphQLType: TypeDefinition, javaType: Class<*>) {
private fun handleNewType(graphQLType: TypeDefinition<*>, javaType: Class<*>) {
when(graphQLType) {
is ObjectTypeDefinition -> {
enqueue(graphQLType, javaType)
Expand Down Expand Up @@ -371,7 +374,7 @@ internal class SchemaClassScanner(initialDictionary: BiMap<String, Class<*>>, al
override fun getDescription() = "type of field $field"
}

class RootTypesHolder(options: SchemaParserOptions, rootInfo: RootTypeInfo, definitionsByName: Map<String, TypeDefinition>, queryResolvers: List<GraphQLQueryResolver>, mutationResolvers: List<GraphQLMutationResolver>, subscriptionResolvers: List<GraphQLSubscriptionResolver>) {
class RootTypesHolder(options: SchemaParserOptions, rootInfo: RootTypeInfo, definitionsByName: Map<String, TypeDefinition<*>>, queryResolvers: List<GraphQLQueryResolver>, mutationResolvers: List<GraphQLMutationResolver>, subscriptionResolvers: List<GraphQLSubscriptionResolver>) {
private val queryName = rootInfo.getQueryName()
private val mutationName = rootInfo.getMutationName()
private val subscriptionName = rootInfo.getSubscriptionName()
Expand All @@ -388,7 +391,7 @@ internal class SchemaClassScanner(initialDictionary: BiMap<String, Class<*>>, al
val mutation = createRootType("mutation", mutationDefinition, mutationName, rootInfo.isMutationRequired(), mutationResolvers, GraphQLMutationResolver::class.java, mutationResolverInfo)
val subscription = createRootType("subscription", subscriptionDefinition, subscriptionName, rootInfo.isSubscriptionRequired(), subscriptionResolvers, GraphQLSubscriptionResolver::class.java, subscriptionResolverInfo)

private fun createRootType(name: String, type: TypeDefinition?, typeName: String, required: Boolean, resolvers: List<GraphQLRootResolver>, resolverInterface: Class<*>, resolverInfo: RootResolverInfo): RootType? {
private fun createRootType(name: String, type: TypeDefinition<*>?, typeName: String, required: Boolean, resolvers: List<GraphQLRootResolver>, resolverInterface: Class<*>, resolverInfo: RootResolverInfo): RootType? {
if(type == null) {
if(required) {
throw SchemaClassScannerError("Type definition for root $name type '$typeName' not found!")
Expand Down
14 changes: 7 additions & 7 deletions src/main/kotlin/com/coxautodev/graphql/tools/SchemaParser.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ import graphql.language.InterfaceTypeDefinition
import graphql.language.ListType
import graphql.language.NonNullType
import graphql.language.ObjectTypeDefinition
import graphql.language.ObjectTypeExtensionDefinition
import graphql.language.ObjectValue
import graphql.language.StringValue
import graphql.language.Type
import graphql.language.TypeExtensionDefinition
import graphql.language.TypeName
import graphql.language.UnionTypeDefinition
import graphql.language.Value
Expand Down Expand Up @@ -49,7 +49,7 @@ class SchemaParser internal constructor(scanResult: ScannedSchemaObjects) {
val DEFAULT_DEPRECATION_MESSAGE = "No longer supported"

@JvmStatic fun newParser() = SchemaParserBuilder()
internal fun getDocumentation(node: AbstractNode): String? = node.comments?.map { it.content.trim() }?.joinToString("\n")
internal fun getDocumentation(node: AbstractNode<*>): String? = node.comments?.map { it.content.trim() }?.joinToString("\n")
}

private val dictionary = scanResult.dictionary
Expand All @@ -58,7 +58,7 @@ class SchemaParser internal constructor(scanResult: ScannedSchemaObjects) {
private val rootInfo = scanResult.rootInfo
private val fieldResolversByType = scanResult.fieldResolversByType

private val extensionDefinitions = definitions.filterIsInstance<TypeExtensionDefinition>()
private val extensionDefinitions = definitions.filterIsInstance<ObjectTypeExtensionDefinition>()
private val objectDefinitions = (definitions.filterIsInstance<ObjectTypeDefinition>() - extensionDefinitions)

private val inputObjectDefinitions = definitions.filterIsInstance<InputObjectTypeDefinition>()
Expand Down Expand Up @@ -235,7 +235,7 @@ class SchemaParser internal constructor(scanResult: ScannedSchemaObjects) {
return field
}

private fun buildDefaultValue(value: Value?): Any? {
private fun buildDefaultValue(value: Value<*>?): Any? {
return when(value) {
null -> null
is IntValue -> value.value
Expand All @@ -249,12 +249,12 @@ class SchemaParser internal constructor(scanResult: ScannedSchemaObjects) {
}
}

private fun determineOutputType(typeDefinition: Type) =
private fun determineOutputType(typeDefinition: Type<*>) =
determineType(GraphQLOutputType::class, typeDefinition, permittedTypesForObject) as GraphQLOutputType
private fun determineInputType(typeDefinition: Type) =
private fun determineInputType(typeDefinition: Type<*>) =
determineType(GraphQLInputType::class, typeDefinition, permittedTypesForInputObject) as GraphQLInputType

private fun <T: Any> determineType(expectedType: KClass<T>, typeDefinition: Type, allowedTypeReferences: Set<String>): GraphQLType =
private fun <T: Any> determineType(expectedType: KClass<T>, typeDefinition: Type<*>, allowedTypeReferences: Set<String>): GraphQLType =
when (typeDefinition) {
is ListType -> GraphQLList(determineType(expectedType, typeDefinition.type, allowedTypeReferences))
is NonNullType -> GraphQLNonNull(determineType(expectedType, typeDefinition.type, allowedTypeReferences))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import java.util.Optional
/**
* @author Andrew Potter
*/
internal class TypeClassMatcher(private val definitionsByName: Map<String, TypeDefinition>) {
internal class TypeClassMatcher(private val definitionsByName: Map<String, TypeDefinition<*>>) {

companion object {
fun isListType(realType: ParameterizedType, generic: GenericType) = generic.isTypeAssignableFromRawClass(realType, Iterable::class.java)
Expand Down Expand Up @@ -75,7 +75,7 @@ internal class TypeClassMatcher(private val definitionsByName: Map<String, TypeD
}
}

is TypeDefinition -> ValidMatch(graphQLType, requireRawClass(realType), potentialMatch.reference)
is TypeDefinition<*> -> ValidMatch(graphQLType, requireRawClass(realType), potentialMatch.reference)
else -> throw error(potentialMatch, "Unknown type: ${realType.javaClass.name}")
}
}
Expand All @@ -102,7 +102,7 @@ internal class TypeClassMatcher(private val definitionsByName: Map<String, TypeD

internal interface Match
internal data class ScalarMatch(val type: ScalarTypeDefinition): Match
internal data class ValidMatch(val type: TypeDefinition, val clazz: Class<*>, val reference: SchemaClassScanner.Reference): Match
internal data class ValidMatch(val type: TypeDefinition<*>, val clazz: Class<*>, val reference: SchemaClassScanner.Reference): Match
internal enum class Location(val prettyName: String) {
RETURN_TYPE("return type"),
PARAMETER_TYPE("parameter"),
Expand Down
8 changes: 4 additions & 4 deletions src/main/kotlin/com/coxautodev/graphql/tools/Utils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,22 @@ import graphql.language.ListType
import graphql.language.NonNullType
import graphql.language.ObjectTypeDefinition
import graphql.language.Type
import graphql.language.TypeExtensionDefinition
import graphql.language.ObjectTypeExtensionDefinition

/**
* @author Andrew Potter
*/

internal typealias GraphQLRootResolver = GraphQLResolver<Void>
internal typealias JavaType = java.lang.reflect.Type
internal typealias GraphQLLangType = graphql.language.Type
internal typealias GraphQLLangType = graphql.language.Type<*>

internal fun Type.unwrap(): Type = when(this) {
internal fun Type<*>.unwrap(): Type<*> = when(this) {
is NonNullType -> this.type.unwrap()
is ListType -> this.type.unwrap()
else -> this
}

internal fun ObjectTypeDefinition.getExtendedFieldDefinitions(extensions: List<TypeExtensionDefinition>): List<FieldDefinition> {
internal fun ObjectTypeDefinition.getExtendedFieldDefinitions(extensions: List<ObjectTypeExtensionDefinition>): List<FieldDefinition> {
return this.fieldDefinitions + extensions.filter { it.name == this.name }.flatMap { it.fieldDefinitions }
}
Original file line number Diff line number Diff line change
Expand Up @@ -93,18 +93,16 @@ class EndToEndSpec extends Specification {

@Override
void onError(Throwable t) {

}

@Override
void onComplete() {

}
})
latch.await(3, TimeUnit.SECONDS)

then:
returnedItem.id == 1
returnedItem.get("onItemCreated").id == 1
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not 100% sure if this is correct.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What prompted the change?

Copy link
Contributor Author

@rwilliams-r7 rwilliams-r7 Apr 4, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

returnedItem.id was null and of course the junit failed as it returns a map. I am not sure if it is due to a change I have made, that is now returns a map.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@apottere - are you happy enough with this?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I dug into this - it looks like the reason the behavior change was due to this change in graphql-java:

graphql-java/graphql-java@adcd3f7#diff-83f68d2746cff37c74d371d07db0e53b

"This commit adds the root field to subscription results to conform to
the current version of the GraphQL specification."

So it's an expected change to be spec compliant.

}

def "generated schema should handle interface types"() {
Expand Down