11package graphql.kickstart.tools
22
3+ import graphql.Scalars
34import graphql.introspection.Introspection
45import graphql.kickstart.tools.directive.SchemaGeneratorDirectiveHelper
56import graphql.kickstart.tools.util.getExtendedFieldDefinitions
@@ -11,7 +12,6 @@ import graphql.schema.idl.ScalarInfo
1112import graphql.schema.idl.SchemaGeneratorHelper
1213import graphql.schema.visibility.NoIntrospectionGraphqlFieldVisibility
1314import org.slf4j.LoggerFactory
14- import java.util.*
1515import kotlin.reflect.KClass
1616
1717/* *
@@ -129,7 +129,7 @@ class SchemaParser internal constructor(
129129 .definition(objectDefinition)
130130 .description(if (objectDefinition.description != null ) objectDefinition.description.content else getDocumentation(objectDefinition))
131131
132- builder.withDirectives(* buildDirectives(objectDefinition.directives, setOf (), Introspection .DirectiveLocation .OBJECT ))
132+ builder.withDirectives(* buildDirectives(objectDefinition.directives, Introspection .DirectiveLocation .OBJECT ))
133133
134134 objectDefinition.implements.forEach { implementsDefinition ->
135135 val interfaceName = (implementsDefinition as TypeName ).name
@@ -160,19 +160,6 @@ class SchemaParser internal constructor(
160160 return schemaGeneratorDirectiveHelper.onObject(objectType, directiveHelperParameters)
161161 }
162162
163- private fun buildDirectives (directives : List <Directive >, directiveDefinitions : Set <GraphQLDirective >, directiveLocation : Introspection .DirectiveLocation ): Array <GraphQLDirective > {
164- val names = HashSet <String >()
165-
166- val output = ArrayList <GraphQLDirective >()
167- for (directive in directives) {
168- if (! names.contains(directive.name)) {
169- names.add(directive.name)
170- output.add(schemaGeneratorHelper.buildDirective(directive, directiveDefinitions, directiveLocation, runtimeWiring.comparatorRegistry))
171- }
172- }
173- return output.toTypedArray()
174- }
175-
176163 private fun createInputObject (definition : InputObjectTypeDefinition , inputObjects : List <GraphQLInputObjectType >): GraphQLInputObjectType {
177164 val extensionDefinitions = inputExtensionDefinitions.filter { it.name == definition.name }
178165
@@ -182,17 +169,17 @@ class SchemaParser internal constructor(
182169 .extensionDefinitions(extensionDefinitions)
183170 .description(if (definition.description != null ) definition.description.content else getDocumentation(definition))
184171
185- builder.withDirectives(* buildDirectives(definition.directives, setOf (), Introspection .DirectiveLocation .INPUT_OBJECT ))
172+ builder.withDirectives(* buildDirectives(definition.directives, Introspection .DirectiveLocation .INPUT_OBJECT ))
186173
187174 (extensionDefinitions + definition).forEach {
188175 it.inputValueDefinitions.forEach { inputDefinition ->
189176 val fieldBuilder = GraphQLInputObjectField .newInputObjectField()
190- .name(inputDefinition.name)
191- .definition(inputDefinition)
192- .description(if (inputDefinition.description != null ) inputDefinition.description.content else getDocumentation(inputDefinition))
193- .defaultValue(buildDefaultValue(inputDefinition.defaultValue))
194- .type(determineInputType(inputDefinition.type, inputObjects))
195- .withDirectives(* buildDirectives(inputDefinition.directives, setOf () , Introspection .DirectiveLocation .INPUT_FIELD_DEFINITION ))
177+ .name(inputDefinition.name)
178+ .definition(inputDefinition)
179+ .description(if (inputDefinition.description != null ) inputDefinition.description.content else getDocumentation(inputDefinition))
180+ .defaultValue(buildDefaultValue(inputDefinition.defaultValue))
181+ .type(determineInputType(inputDefinition.type, inputObjects))
182+ .withDirectives(* buildDirectives(inputDefinition.directives, Introspection .DirectiveLocation .INPUT_FIELD_DEFINITION ))
196183 builder.field(fieldBuilder.build())
197184 }
198185 }
@@ -211,14 +198,14 @@ class SchemaParser internal constructor(
211198 .definition(definition)
212199 .description(if (definition.description != null ) definition.description.content else getDocumentation(definition))
213200
214- builder.withDirectives(* buildDirectives(definition.directives, setOf (), Introspection .DirectiveLocation .ENUM ))
201+ builder.withDirectives(* buildDirectives(definition.directives, Introspection .DirectiveLocation .ENUM ))
215202
216203 definition.enumValueDefinitions.forEach { enumDefinition ->
217204 val enumName = enumDefinition.name
218205 val enumValue = type.unwrap().enumConstants.find { (it as Enum <* >).name == enumName }
219206 ? : throw SchemaError (" Expected value for name '$enumName ' in enum '${type.unwrap().simpleName} ' but found none!" )
220207
221- val enumValueDirectives = buildDirectives(enumDefinition.directives, setOf (), Introspection .DirectiveLocation .ENUM_VALUE )
208+ val enumValueDirectives = buildDirectives(enumDefinition.directives, Introspection .DirectiveLocation .ENUM_VALUE )
222209 getDeprecated(enumDefinition.directives).let {
223210 val enumValueDefinition = GraphQLEnumValueDefinition .newEnumValueDefinition()
224211 .name(enumName)
@@ -243,7 +230,7 @@ class SchemaParser internal constructor(
243230 .definition(interfaceDefinition)
244231 .description(if (interfaceDefinition.description != null ) interfaceDefinition.description.content else getDocumentation(interfaceDefinition))
245232
246- builder.withDirectives(* buildDirectives(interfaceDefinition.directives, setOf (), Introspection .DirectiveLocation .INTERFACE ))
233+ builder.withDirectives(* buildDirectives(interfaceDefinition.directives, Introspection .DirectiveLocation .INTERFACE ))
247234
248235 interfaceDefinition.fieldDefinitions.forEach { fieldDefinition ->
249236 builder.field { field -> createField(field, fieldDefinition, inputObjects) }
@@ -259,7 +246,7 @@ class SchemaParser internal constructor(
259246 .definition(definition)
260247 .description(if (definition.description != null ) definition.description.content else getDocumentation(definition))
261248
262- builder.withDirectives(* buildDirectives(definition.directives, setOf (), Introspection .DirectiveLocation .UNION ))
249+ builder.withDirectives(* buildDirectives(definition.directives, Introspection .DirectiveLocation .UNION ))
263250
264251 getLeafUnionObjects(definition, types).forEach { builder.possibleType(it) }
265252 return schemaGeneratorDirectiveHelper.onUnion(builder.build(), schemaDirectiveParameters)
@@ -286,25 +273,95 @@ class SchemaParser internal constructor(
286273 }
287274
288275 private fun createField (field : GraphQLFieldDefinition .Builder , fieldDefinition : FieldDefinition , inputObjects : List <GraphQLInputObjectType >): GraphQLFieldDefinition .Builder {
289- field.name(fieldDefinition.name)
290- field.description(if (fieldDefinition.description != null ) fieldDefinition.description.content else getDocumentation(fieldDefinition))
291- field.definition(fieldDefinition)
292- getDeprecated(fieldDefinition.directives)?.let { field.deprecate(it) }
293- field.type(determineOutputType(fieldDefinition.type, inputObjects))
276+ field
277+ .name(fieldDefinition.name)
278+ .description(fieldDefinition.description?.content ? : getDocumentation(fieldDefinition))
279+ .definition(fieldDefinition)
280+ .apply { getDeprecated(fieldDefinition.directives)?.let { deprecate(it) } }
281+ .type(determineOutputType(fieldDefinition.type, inputObjects))
282+
294283 fieldDefinition.inputValueDefinitions.forEach { argumentDefinition ->
295284 val argumentBuilder = GraphQLArgument .newArgument()
296285 .name(argumentDefinition.name)
297286 .definition(argumentDefinition)
298287 .description(if (argumentDefinition.description != null ) argumentDefinition.description.content else getDocumentation(argumentDefinition))
299- .defaultValue(buildDefaultValue(argumentDefinition.defaultValue))
300288 .type(determineInputType(argumentDefinition.type, inputObjects))
301- .withDirectives(* buildDirectives(argumentDefinition.directives, setOf (), Introspection .DirectiveLocation .ARGUMENT_DEFINITION ))
289+ .apply { buildDefaultValue(argumentDefinition.defaultValue)?.let { defaultValue(it) } }
290+ .withDirectives(* buildDirectives(argumentDefinition.directives, Introspection .DirectiveLocation .ARGUMENT_DEFINITION ))
291+
302292 field.argument(argumentBuilder.build())
303293 }
304- field.withDirectives(* buildDirectives(fieldDefinition.directives, setOf (), Introspection .DirectiveLocation .FIELD_DEFINITION ))
294+ field.withDirectives(* buildDirectives(fieldDefinition.directives, Introspection .DirectiveLocation .FIELD_DEFINITION ))
295+
305296 return field
306297 }
307298
299+ private fun buildDirectives (directives : List <Directive >, directiveLocation : Introspection .DirectiveLocation ): Array <GraphQLDirective > {
300+ val names = mutableSetOf<String >()
301+
302+ val output = mutableListOf<GraphQLDirective >()
303+ for (directive in directives) {
304+ if (! names.contains(directive.name)) {
305+ names.add(directive.name)
306+ val graphQLDirective = GraphQLDirective .newDirective()
307+ .name(directive.name)
308+ .apply {
309+ directive.arguments.forEach { arg ->
310+ argument(GraphQLArgument .newArgument()
311+ .name(arg.name)
312+ .type(buildDirectiveInputType(arg.value))
313+ .build())
314+ }
315+ }
316+ .build()
317+
318+
319+ output.add(schemaGeneratorHelper.buildDirective(directive, setOf (graphQLDirective), directiveLocation, runtimeWiring.comparatorRegistry))
320+ }
321+ }
322+
323+ return output.toTypedArray()
324+ }
325+
326+ private fun buildDirectiveInputType (value : Value <* >): GraphQLInputType ? {
327+ when (value) {
328+ is NullValue -> return Scalars .GraphQLString
329+ is FloatValue -> return Scalars .GraphQLFloat
330+ is StringValue -> return Scalars .GraphQLString
331+ is IntValue -> return Scalars .GraphQLInt
332+ is BooleanValue -> return Scalars .GraphQLBoolean
333+ is ArrayValue -> return GraphQLList .list(buildDirectiveInputType(getArrayValueWrappedType(value)))
334+ else -> throw SchemaError (" Directive values of type '${value::class .simpleName} ' are not supported yet." )
335+ }
336+ }
337+
338+ private fun getArrayValueWrappedType (value : ArrayValue ): Value <* > {
339+ // empty array [] is equivalent to [null]
340+ if (value.values.isEmpty()) {
341+ return NullValue .newNullValue().build()
342+ }
343+
344+ // get rid of null values
345+ val nonNullValueList = value.values.filter { v -> v !is NullValue }
346+
347+ // [null, null, ...] unwrapped is null
348+ if (nonNullValueList.isEmpty()) {
349+ return NullValue .newNullValue().build()
350+ }
351+
352+ // make sure the array isn't polymorphic
353+ val distinctTypes = nonNullValueList
354+ .map { it::class .java }
355+ .distinct()
356+
357+ if (distinctTypes.size > 1 ) {
358+ throw SchemaError (" Arrays containing multiple types of values are not supported yet." )
359+ }
360+
361+ // peek at first value, value exists and is assured to be non-null
362+ return nonNullValueList[0 ]
363+ }
364+
308365 private fun buildDefaultValue (value : Value <* >? ): Any? {
309366 return when (value) {
310367 null -> null
@@ -406,4 +463,4 @@ class SchemaParser internal constructor(
406463
407464class SchemaError (message : String , cause : Throwable ? = null ) : RuntimeException(message, cause)
408465
409- val graphQLScalars = ScalarInfo .STANDARD_SCALARS .associateBy { it.name }
466+ val graphQLScalars = ScalarInfo .GRAPHQL_SPECIFICATION_SCALARS .associateBy { it.name }
0 commit comments