Skip to content
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

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

<name>GraphQL Java Tools</name>
Expand All @@ -17,7 +17,7 @@
<kotlin.version>1.3.70</kotlin.version>
<kotlin-coroutines.version>1.2.1</kotlin-coroutines.version>
<jackson.version>2.10.3</jackson.version>
<graphql-java.version>14.1</graphql-java.version>
<graphql-java.version>15.0</graphql-java.version>

<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
Expand Down
4 changes: 2 additions & 2 deletions src/main/kotlin/graphql/kickstart/tools/SchemaClassScanner.kt
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ internal class SchemaClassScanner(
val scalars = scalarDefinitions
.filter {
// Filter for any defined scalars OR scalars that aren't defined but also aren't standard
scalars.containsKey(it.name) || !ScalarInfo.STANDARD_SCALAR_DEFINITIONS.containsKey(it.name)
scalars.containsKey(it.name) || !ScalarInfo.GRAPHQL_SPECIFICATION_SCALARS_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!")
Expand Down Expand Up @@ -322,7 +322,7 @@ internal class SchemaClassScanner(
is InputObjectTypeDefinition -> {
graphQLType.inputValueDefinitions.forEach { inputValueDefinition ->
val inputGraphQLType = inputValueDefinition.type.unwrap()
if (inputGraphQLType is TypeName && !ScalarInfo.STANDARD_SCALAR_DEFINITIONS.containsKey(inputGraphQLType.name)) {
if (inputGraphQLType is TypeName && !ScalarInfo.GRAPHQL_SPECIFICATION_SCALARS_DEFINITIONS.containsKey(inputGraphQLType.name)) {
val inputValueJavaType = findInputValueType(inputValueDefinition.name, inputGraphQLType, javaType.unwrap())
if (inputValueJavaType != null) {
handleFoundType(typeClassMatcher.match(TypeClassMatcher.PotentialMatch.parameterType(
Expand Down
127 changes: 92 additions & 35 deletions src/main/kotlin/graphql/kickstart/tools/SchemaParser.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package graphql.kickstart.tools

import graphql.Scalars
import graphql.introspection.Introspection
import graphql.kickstart.tools.directive.SchemaGeneratorDirectiveHelper
import graphql.kickstart.tools.util.getExtendedFieldDefinitions
Expand All @@ -11,7 +12,6 @@ import graphql.schema.idl.ScalarInfo
import graphql.schema.idl.SchemaGeneratorHelper
import graphql.schema.visibility.NoIntrospectionGraphqlFieldVisibility
import org.slf4j.LoggerFactory
import java.util.*
import kotlin.reflect.KClass

/**
Expand Down Expand Up @@ -129,7 +129,7 @@ class SchemaParser internal constructor(
.definition(objectDefinition)
.description(if (objectDefinition.description != null) objectDefinition.description.content else getDocumentation(objectDefinition))

builder.withDirectives(*buildDirectives(objectDefinition.directives, setOf(), Introspection.DirectiveLocation.OBJECT))
builder.withDirectives(*buildDirectives(objectDefinition.directives, Introspection.DirectiveLocation.OBJECT))

objectDefinition.implements.forEach { implementsDefinition ->
val interfaceName = (implementsDefinition as TypeName).name
Expand Down Expand Up @@ -160,19 +160,6 @@ class SchemaParser internal constructor(
return schemaGeneratorDirectiveHelper.onObject(objectType, directiveHelperParameters)
}

private fun buildDirectives(directives: List<Directive>, directiveDefinitions: Set<GraphQLDirective>, directiveLocation: Introspection.DirectiveLocation): Array<GraphQLDirective> {
val names = HashSet<String>()

val output = ArrayList<GraphQLDirective>()
for (directive in directives) {
if (!names.contains(directive.name)) {
names.add(directive.name)
output.add(schemaGeneratorHelper.buildDirective(directive, directiveDefinitions, directiveLocation, runtimeWiring.comparatorRegistry))
}
}
return output.toTypedArray()
}

private fun createInputObject(definition: InputObjectTypeDefinition, inputObjects: List<GraphQLInputObjectType>): GraphQLInputObjectType {
val extensionDefinitions = inputExtensionDefinitions.filter { it.name == definition.name }

Expand All @@ -182,17 +169,17 @@ class SchemaParser internal constructor(
.extensionDefinitions(extensionDefinitions)
.description(if (definition.description != null) definition.description.content else getDocumentation(definition))

builder.withDirectives(*buildDirectives(definition.directives, setOf(), Introspection.DirectiveLocation.INPUT_OBJECT))
builder.withDirectives(*buildDirectives(definition.directives, Introspection.DirectiveLocation.INPUT_OBJECT))

(extensionDefinitions + definition).forEach {
it.inputValueDefinitions.forEach { inputDefinition ->
val fieldBuilder = GraphQLInputObjectField.newInputObjectField()
.name(inputDefinition.name)
.definition(inputDefinition)
.description(if (inputDefinition.description != null) inputDefinition.description.content else getDocumentation(inputDefinition))
.defaultValue(buildDefaultValue(inputDefinition.defaultValue))
.type(determineInputType(inputDefinition.type, inputObjects))
.withDirectives(*buildDirectives(inputDefinition.directives, setOf(), Introspection.DirectiveLocation.INPUT_FIELD_DEFINITION))
.name(inputDefinition.name)
.definition(inputDefinition)
.description(if (inputDefinition.description != null) inputDefinition.description.content else getDocumentation(inputDefinition))
.defaultValue(buildDefaultValue(inputDefinition.defaultValue))
.type(determineInputType(inputDefinition.type, inputObjects))
.withDirectives(*buildDirectives(inputDefinition.directives, Introspection.DirectiveLocation.INPUT_FIELD_DEFINITION))
builder.field(fieldBuilder.build())
}
}
Expand All @@ -211,14 +198,14 @@ class SchemaParser internal constructor(
.definition(definition)
.description(if (definition.description != null) definition.description.content else getDocumentation(definition))

builder.withDirectives(*buildDirectives(definition.directives, setOf(), Introspection.DirectiveLocation.ENUM))
builder.withDirectives(*buildDirectives(definition.directives, Introspection.DirectiveLocation.ENUM))

definition.enumValueDefinitions.forEach { enumDefinition ->
val enumName = enumDefinition.name
val enumValue = type.unwrap().enumConstants.find { (it as Enum<*>).name == enumName }
?: throw SchemaError("Expected value for name '$enumName' in enum '${type.unwrap().simpleName}' but found none!")

val enumValueDirectives = buildDirectives(enumDefinition.directives, setOf(), Introspection.DirectiveLocation.ENUM_VALUE)
val enumValueDirectives = buildDirectives(enumDefinition.directives, Introspection.DirectiveLocation.ENUM_VALUE)
getDeprecated(enumDefinition.directives).let {
val enumValueDefinition = GraphQLEnumValueDefinition.newEnumValueDefinition()
.name(enumName)
Expand All @@ -243,7 +230,7 @@ class SchemaParser internal constructor(
.definition(interfaceDefinition)
.description(if (interfaceDefinition.description != null) interfaceDefinition.description.content else getDocumentation(interfaceDefinition))

builder.withDirectives(*buildDirectives(interfaceDefinition.directives, setOf(), Introspection.DirectiveLocation.INTERFACE))
builder.withDirectives(*buildDirectives(interfaceDefinition.directives, Introspection.DirectiveLocation.INTERFACE))

interfaceDefinition.fieldDefinitions.forEach { fieldDefinition ->
builder.field { field -> createField(field, fieldDefinition, inputObjects) }
Expand All @@ -259,7 +246,7 @@ class SchemaParser internal constructor(
.definition(definition)
.description(if (definition.description != null) definition.description.content else getDocumentation(definition))

builder.withDirectives(*buildDirectives(definition.directives, setOf(), Introspection.DirectiveLocation.UNION))
builder.withDirectives(*buildDirectives(definition.directives, Introspection.DirectiveLocation.UNION))

getLeafUnionObjects(definition, types).forEach { builder.possibleType(it) }
return schemaGeneratorDirectiveHelper.onUnion(builder.build(), schemaDirectiveParameters)
Expand All @@ -286,25 +273,95 @@ class SchemaParser internal constructor(
}

private fun createField(field: GraphQLFieldDefinition.Builder, fieldDefinition: FieldDefinition, inputObjects: List<GraphQLInputObjectType>): GraphQLFieldDefinition.Builder {
field.name(fieldDefinition.name)
field.description(if (fieldDefinition.description != null) fieldDefinition.description.content else getDocumentation(fieldDefinition))
field.definition(fieldDefinition)
getDeprecated(fieldDefinition.directives)?.let { field.deprecate(it) }
field.type(determineOutputType(fieldDefinition.type, inputObjects))
field
.name(fieldDefinition.name)
.description(fieldDefinition.description?.content ?: getDocumentation(fieldDefinition))
.definition(fieldDefinition)
.apply { getDeprecated(fieldDefinition.directives)?.let { deprecate(it) } }
.type(determineOutputType(fieldDefinition.type, inputObjects))

fieldDefinition.inputValueDefinitions.forEach { argumentDefinition ->
val argumentBuilder = GraphQLArgument.newArgument()
.name(argumentDefinition.name)
.definition(argumentDefinition)
.description(if (argumentDefinition.description != null) argumentDefinition.description.content else getDocumentation(argumentDefinition))
.defaultValue(buildDefaultValue(argumentDefinition.defaultValue))
.type(determineInputType(argumentDefinition.type, inputObjects))
.withDirectives(*buildDirectives(argumentDefinition.directives, setOf(), Introspection.DirectiveLocation.ARGUMENT_DEFINITION))
.apply { buildDefaultValue(argumentDefinition.defaultValue)?.let { defaultValue(it) } }
.withDirectives(*buildDirectives(argumentDefinition.directives, Introspection.DirectiveLocation.ARGUMENT_DEFINITION))

field.argument(argumentBuilder.build())
}
field.withDirectives(*buildDirectives(fieldDefinition.directives, setOf(), Introspection.DirectiveLocation.FIELD_DEFINITION))
field.withDirectives(*buildDirectives(fieldDefinition.directives, Introspection.DirectiveLocation.FIELD_DEFINITION))

return field
}

private fun buildDirectives(directives: List<Directive>, directiveLocation: Introspection.DirectiveLocation): Array<GraphQLDirective> {
val names = mutableSetOf<String>()

val output = mutableListOf<GraphQLDirective>()
for (directive in directives) {
if (!names.contains(directive.name)) {
names.add(directive.name)
val graphQLDirective = GraphQLDirective.newDirective()
.name(directive.name)
.apply {
directive.arguments.forEach { arg ->
argument(GraphQLArgument.newArgument()
.name(arg.name)
.type(buildDirectiveInputType(arg.value))
.build())
}
}
.build()


output.add(schemaGeneratorHelper.buildDirective(directive, setOf(graphQLDirective), directiveLocation, runtimeWiring.comparatorRegistry))
}
}

return output.toTypedArray()
}

private fun buildDirectiveInputType(value: Value<*>): GraphQLInputType? {
when (value) {
is NullValue -> return Scalars.GraphQLString
is FloatValue -> return Scalars.GraphQLFloat
is StringValue -> return Scalars.GraphQLString
is IntValue -> return Scalars.GraphQLInt
is BooleanValue -> return Scalars.GraphQLBoolean
is ArrayValue -> return GraphQLList.list(buildDirectiveInputType(getArrayValueWrappedType(value)))
else -> throw SchemaError("Directive values of type '${value::class.simpleName}' are not supported yet.")
}
}

private fun getArrayValueWrappedType(value: ArrayValue): Value<*> {
// empty array [] is equivalent to [null]
if (value.values.isEmpty()) {
return NullValue.newNullValue().build()
}

// get rid of null values
val nonNullValueList = value.values.filter { v -> v !is NullValue }

// [null, null, ...] unwrapped is null
if (nonNullValueList.isEmpty()) {
return NullValue.newNullValue().build()
}

// make sure the array isn't polymorphic
val distinctTypes = nonNullValueList
.map { it::class.java }
.distinct()

if (distinctTypes.size > 1) {
throw SchemaError("Arrays containing multiple types of values are not supported yet.")
}

// peek at first value, value exists and is assured to be non-null
return nonNullValueList[0]
}

private fun buildDefaultValue(value: Value<*>?): Any? {
return when (value) {
null -> null
Expand Down Expand Up @@ -406,4 +463,4 @@ class SchemaParser internal constructor(

class SchemaError(message: String, cause: Throwable? = null) : RuntimeException(message, cause)

val graphQLScalars = ScalarInfo.STANDARD_SCALARS.associateBy { it.name }
val graphQLScalars = ScalarInfo.GRAPHQL_SPECIFICATION_SCALARS.associateBy { it.name }
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ class SchemaParserBuilder {
files.forEach { documents.add(parser.parseDocument(readFile(it), it)) }

if (schemaString.isNotEmpty()) {
documents.add(parser.parseDocument(this.schemaString.toString()))
documents.add(parser.parseDocument(schemaString.toString()))
}
} catch (pce: ParseCancellationException) {
val cause = pce.cause
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ internal class TypeClassMatcher(private val definitionsByName: Map<String, TypeD
}

is TypeName -> {
val typeDefinition = ScalarInfo.STANDARD_SCALAR_DEFINITIONS[graphQLType.name]
val typeDefinition = ScalarInfo.GRAPHQL_SPECIFICATION_SCALARS_DEFINITIONS[graphQLType.name]
?: definitionsByName[graphQLType.name]
?: throw error(potentialMatch, "No ${TypeDefinition::class.java.simpleName} for type name ${graphQLType.name}")
if (typeDefinition is ScalarTypeDefinition) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
/*
* DO NOT EDIT THIS FILE!
*
* File copied from com.graphql-java.graphql-java:14.0 without changes.
* File copied from com.graphql-java.graphql-java:15.0 without any changes.
*/
@Internal
public class SchemaDirectiveWiringEnvironmentImpl<T extends GraphQLDirectiveContainer> implements SchemaDirectiveWiringEnvironment<T> {
Expand Down Expand Up @@ -113,15 +113,15 @@ public GraphQLFieldDefinition getFieldDefinition() {

@Override
public DataFetcher getFieldDataFetcher() {
assertNotNull(fieldDefinition, "An output field must be in context to call this method");
assertNotNull(fieldsContainer, "An output field container must be in context to call this method");
assertNotNull(fieldDefinition, () -> "An output field must be in context to call this method");
assertNotNull(fieldsContainer, () -> "An output field container must be in context to call this method");
return codeRegistry.getDataFetcher(fieldsContainer, fieldDefinition);
}

@Override
public GraphQLFieldDefinition setFieldDataFetcher(DataFetcher newDataFetcher) {
assertNotNull(fieldDefinition, "An output field must be in context to call this method");
assertNotNull(fieldsContainer, "An output field container must be in context to call this method");
assertNotNull(fieldDefinition, () -> "An output field must be in context to call this method");
assertNotNull(fieldsContainer, () -> "An output field container must be in context to call this method");

FieldCoordinates coordinates = FieldCoordinates.coordinates(fieldsContainer, fieldDefinition);
codeRegistry.dataFetcher(coordinates, newDataFetcher);
Expand Down
Loading