1
1
package graphql.kickstart.tools
2
2
3
+ import graphql.Scalars
3
4
import graphql.introspection.Introspection
4
5
import graphql.kickstart.tools.directive.SchemaGeneratorDirectiveHelper
5
6
import graphql.kickstart.tools.util.getExtendedFieldDefinitions
@@ -11,7 +12,6 @@ import graphql.schema.idl.ScalarInfo
11
12
import graphql.schema.idl.SchemaGeneratorHelper
12
13
import graphql.schema.visibility.NoIntrospectionGraphqlFieldVisibility
13
14
import org.slf4j.LoggerFactory
14
- import java.util.*
15
15
import kotlin.reflect.KClass
16
16
17
17
/* *
@@ -129,7 +129,7 @@ class SchemaParser internal constructor(
129
129
.definition(objectDefinition)
130
130
.description(if (objectDefinition.description != null ) objectDefinition.description.content else getDocumentation(objectDefinition))
131
131
132
- builder.withDirectives(* buildDirectives(objectDefinition.directives, setOf (), Introspection .DirectiveLocation .OBJECT ))
132
+ builder.withDirectives(* buildDirectives(objectDefinition.directives, Introspection .DirectiveLocation .OBJECT ))
133
133
134
134
objectDefinition.implements.forEach { implementsDefinition ->
135
135
val interfaceName = (implementsDefinition as TypeName ).name
@@ -160,19 +160,6 @@ class SchemaParser internal constructor(
160
160
return schemaGeneratorDirectiveHelper.onObject(objectType, directiveHelperParameters)
161
161
}
162
162
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
-
176
163
private fun createInputObject (definition : InputObjectTypeDefinition , inputObjects : List <GraphQLInputObjectType >): GraphQLInputObjectType {
177
164
val extensionDefinitions = inputExtensionDefinitions.filter { it.name == definition.name }
178
165
@@ -182,17 +169,17 @@ class SchemaParser internal constructor(
182
169
.extensionDefinitions(extensionDefinitions)
183
170
.description(if (definition.description != null ) definition.description.content else getDocumentation(definition))
184
171
185
- builder.withDirectives(* buildDirectives(definition.directives, setOf (), Introspection .DirectiveLocation .INPUT_OBJECT ))
172
+ builder.withDirectives(* buildDirectives(definition.directives, Introspection .DirectiveLocation .INPUT_OBJECT ))
186
173
187
174
(extensionDefinitions + definition).forEach {
188
175
it.inputValueDefinitions.forEach { inputDefinition ->
189
176
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 ))
196
183
builder.field(fieldBuilder.build())
197
184
}
198
185
}
@@ -211,14 +198,14 @@ class SchemaParser internal constructor(
211
198
.definition(definition)
212
199
.description(if (definition.description != null ) definition.description.content else getDocumentation(definition))
213
200
214
- builder.withDirectives(* buildDirectives(definition.directives, setOf (), Introspection .DirectiveLocation .ENUM ))
201
+ builder.withDirectives(* buildDirectives(definition.directives, Introspection .DirectiveLocation .ENUM ))
215
202
216
203
definition.enumValueDefinitions.forEach { enumDefinition ->
217
204
val enumName = enumDefinition.name
218
205
val enumValue = type.unwrap().enumConstants.find { (it as Enum <* >).name == enumName }
219
206
? : throw SchemaError (" Expected value for name '$enumName ' in enum '${type.unwrap().simpleName} ' but found none!" )
220
207
221
- val enumValueDirectives = buildDirectives(enumDefinition.directives, setOf (), Introspection .DirectiveLocation .ENUM_VALUE )
208
+ val enumValueDirectives = buildDirectives(enumDefinition.directives, Introspection .DirectiveLocation .ENUM_VALUE )
222
209
getDeprecated(enumDefinition.directives).let {
223
210
val enumValueDefinition = GraphQLEnumValueDefinition .newEnumValueDefinition()
224
211
.name(enumName)
@@ -243,7 +230,7 @@ class SchemaParser internal constructor(
243
230
.definition(interfaceDefinition)
244
231
.description(if (interfaceDefinition.description != null ) interfaceDefinition.description.content else getDocumentation(interfaceDefinition))
245
232
246
- builder.withDirectives(* buildDirectives(interfaceDefinition.directives, setOf (), Introspection .DirectiveLocation .INTERFACE ))
233
+ builder.withDirectives(* buildDirectives(interfaceDefinition.directives, Introspection .DirectiveLocation .INTERFACE ))
247
234
248
235
interfaceDefinition.fieldDefinitions.forEach { fieldDefinition ->
249
236
builder.field { field -> createField(field, fieldDefinition, inputObjects) }
@@ -259,7 +246,7 @@ class SchemaParser internal constructor(
259
246
.definition(definition)
260
247
.description(if (definition.description != null ) definition.description.content else getDocumentation(definition))
261
248
262
- builder.withDirectives(* buildDirectives(definition.directives, setOf (), Introspection .DirectiveLocation .UNION ))
249
+ builder.withDirectives(* buildDirectives(definition.directives, Introspection .DirectiveLocation .UNION ))
263
250
264
251
getLeafUnionObjects(definition, types).forEach { builder.possibleType(it) }
265
252
return schemaGeneratorDirectiveHelper.onUnion(builder.build(), schemaDirectiveParameters)
@@ -286,25 +273,95 @@ class SchemaParser internal constructor(
286
273
}
287
274
288
275
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
+
294
283
fieldDefinition.inputValueDefinitions.forEach { argumentDefinition ->
295
284
val argumentBuilder = GraphQLArgument .newArgument()
296
285
.name(argumentDefinition.name)
297
286
.definition(argumentDefinition)
298
287
.description(if (argumentDefinition.description != null ) argumentDefinition.description.content else getDocumentation(argumentDefinition))
299
- .defaultValue(buildDefaultValue(argumentDefinition.defaultValue))
300
288
.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
+
302
292
field.argument(argumentBuilder.build())
303
293
}
304
- field.withDirectives(* buildDirectives(fieldDefinition.directives, setOf (), Introspection .DirectiveLocation .FIELD_DEFINITION ))
294
+ field.withDirectives(* buildDirectives(fieldDefinition.directives, Introspection .DirectiveLocation .FIELD_DEFINITION ))
295
+
305
296
return field
306
297
}
307
298
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
+
308
365
private fun buildDefaultValue (value : Value <* >? ): Any? {
309
366
return when (value) {
310
367
null -> null
@@ -406,4 +463,4 @@ class SchemaParser internal constructor(
406
463
407
464
class SchemaError (message : String , cause : Throwable ? = null ) : RuntimeException(message, cause)
408
465
409
- val graphQLScalars = ScalarInfo .STANDARD_SCALARS .associateBy { it.name }
466
+ val graphQLScalars = ScalarInfo .GRAPHQL_SPECIFICATION_SCALARS .associateBy { it.name }
0 commit comments