Skip to content

Commit cae530a

Browse files
committed
2 parents a6393e0 + d780322 commit cae530a

17 files changed

+410
-283
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
[![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)
44
[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.graphql-java-kickstart/graphql-java-tools/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.graphql-java-kickstart/graphql-java-tools)
5-
[![Chat on Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://graphqljavakickstart.slack.com)
5+
[![Chat on Slack](https://img.shields.io/badge/slack-join%20chat-informational)](https://graphqljavakickstart.slack.com)
66

77
This library allows you to use the GraphQL schema language to build your [graphql-java](https://github.com/graphql-java/graphql-java) schema.
88
Inspired by [graphql-tools](https://github.com/apollographql/graphql-tools), it parses the given GraphQL schema and allows you to BYOO (bring your own object) to fill in the implementations.
@@ -55,7 +55,7 @@ A few libraries exist to ease the boilerplate pain, including [GraphQL-Java's bu
5555
<dependency>
5656
<groupId>com.graphql-java-kickstart</groupId>
5757
<artifactId>graphql-java-tools</artifactId>
58-
<version>6.0.2</version>
58+
<version>6.2.0</version>
5959
</dependency>
6060
```
6161
```groovy

pom.xml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
<groupId>com.graphql-java-kickstart</groupId>
66
<artifactId>graphql-java-tools</artifactId>
7-
<version>6.1.1-SNAPSHOT</version>
7+
<version>6.2.1-SNAPSHOT</version>
88
<packaging>jar</packaging>
99

1010
<name>GraphQL Java Tools</name>
@@ -14,10 +14,10 @@
1414
<properties>
1515
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
1616
<java.version>1.8</java.version>
17-
<kotlin.version>1.3.70</kotlin.version>
18-
<kotlin-coroutines.version>1.2.1</kotlin-coroutines.version>
17+
<kotlin.version>1.3.72</kotlin.version>
18+
<kotlin-coroutines.version>1.3.9</kotlin-coroutines.version>
1919
<jackson.version>2.10.3</jackson.version>
20-
<graphql-java.version>14.1</graphql-java.version>
20+
<graphql-java.version>15.0</graphql-java.version>
2121

2222
<maven.compiler.source>${java.version}</maven.compiler.source>
2323
<maven.compiler.target>${java.version}</maven.compiler.target>

src/main/kotlin/graphql/kickstart/tools/SchemaClassScanner.kt

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,7 @@ internal class SchemaClassScanner(
1919
private val scalars: CustomScalarMap,
2020
private val options: SchemaParserOptions
2121
) {
22-
companion object {
23-
val log = LoggerFactory.getLogger(SchemaClassScanner::class.java)!!
24-
}
22+
private val log = LoggerFactory.getLogger(javaClass)
2523

2624
private val rootInfo = RootTypeInfo.fromSchemaDefinitions(allDefinitions.filterIsInstance<SchemaDefinition>())
2725

@@ -147,15 +145,13 @@ internal class SchemaClassScanner(
147145
val scalars = scalarDefinitions
148146
.filter {
149147
// Filter for any defined scalars OR scalars that aren't defined but also aren't standard
150-
scalars.containsKey(it.name) || !ScalarInfo.STANDARD_SCALAR_DEFINITIONS.containsKey(it.name)
148+
scalars.containsKey(it.name) || !ScalarInfo.GRAPHQL_SPECIFICATION_SCALARS_DEFINITIONS.containsKey(it.name)
151149
}.map { definition ->
152150
val provided = scalars[definition.name]
153151
?: throw SchemaClassScannerError("Expected a user-defined GraphQL scalar type with name '${definition.name}' but found none!")
154152
GraphQLScalarType.newScalar()
155153
.name(provided.name)
156-
.description(
157-
if (definition.description != null) definition.description.content
158-
else SchemaParser.getDocumentation(definition) ?: provided.description)
154+
.description(definition.description?.content ?: getDocumentation(definition) ?: provided.description)
159155
.coercing(provided.coercing)
160156
.definition(definition)
161157
.build()
@@ -322,7 +318,7 @@ internal class SchemaClassScanner(
322318
is InputObjectTypeDefinition -> {
323319
graphQLType.inputValueDefinitions.forEach { inputValueDefinition ->
324320
val inputGraphQLType = inputValueDefinition.type.unwrap()
325-
if (inputGraphQLType is TypeName && !ScalarInfo.STANDARD_SCALAR_DEFINITIONS.containsKey(inputGraphQLType.name)) {
321+
if (inputGraphQLType is TypeName && !ScalarInfo.GRAPHQL_SPECIFICATION_SCALARS_DEFINITIONS.containsKey(inputGraphQLType.name)) {
326322
val inputValueJavaType = findInputValueType(inputValueDefinition.name, inputGraphQLType, javaType.unwrap())
327323
if (inputValueJavaType != null) {
328324
handleFoundType(typeClassMatcher.match(TypeClassMatcher.PotentialMatch.parameterType(

src/main/kotlin/graphql/kickstart/tools/SchemaParser.kt

Lines changed: 99 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package graphql.kickstart.tools
22

3+
import graphql.Scalars
34
import graphql.introspection.Introspection
45
import graphql.kickstart.tools.directive.SchemaGeneratorDirectiveHelper
6+
import graphql.kickstart.tools.util.getDocumentation
57
import graphql.kickstart.tools.util.getExtendedFieldDefinitions
68
import graphql.kickstart.tools.util.unwrap
79
import graphql.language.*
@@ -11,7 +13,6 @@ import graphql.schema.idl.ScalarInfo
1113
import graphql.schema.idl.SchemaGeneratorHelper
1214
import graphql.schema.visibility.NoIntrospectionGraphqlFieldVisibility
1315
import org.slf4j.LoggerFactory
14-
import java.util.*
1516
import kotlin.reflect.KClass
1617

1718
/**
@@ -24,17 +25,11 @@ class SchemaParser internal constructor(
2425
private val options: SchemaParserOptions,
2526
private val runtimeWiring: RuntimeWiring
2627
) {
27-
companion object {
28-
val log = LoggerFactory.getLogger(SchemaClassScanner::class.java)!!
29-
const val DEFAULT_DEPRECATION_MESSAGE = "No longer supported"
28+
private val log = LoggerFactory.getLogger(javaClass)
3029

30+
companion object {
3131
@JvmStatic
3232
fun newParser() = SchemaParserBuilder()
33-
34-
internal fun getDocumentation(node: AbstractNode<*>): String? = node.comments?.asSequence()
35-
?.filter { !it.content.startsWith("#") }
36-
?.joinToString("\n") { it.content.trimEnd() }
37-
?.trimIndent()
3833
}
3934

4035
private val dictionary = scanResult.dictionary
@@ -129,7 +124,7 @@ class SchemaParser internal constructor(
129124
.definition(objectDefinition)
130125
.description(if (objectDefinition.description != null) objectDefinition.description.content else getDocumentation(objectDefinition))
131126

132-
builder.withDirectives(*buildDirectives(objectDefinition.directives, setOf(), Introspection.DirectiveLocation.OBJECT))
127+
builder.withDirectives(*buildDirectives(objectDefinition.directives, Introspection.DirectiveLocation.OBJECT))
133128

134129
objectDefinition.implements.forEach { implementsDefinition ->
135130
val interfaceName = (implementsDefinition as TypeName).name
@@ -160,19 +155,6 @@ class SchemaParser internal constructor(
160155
return schemaGeneratorDirectiveHelper.onObject(objectType, directiveHelperParameters)
161156
}
162157

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-
176158
private fun createInputObject(definition: InputObjectTypeDefinition, inputObjects: List<GraphQLInputObjectType>,
177159
referencingInputObjects: MutableSet<String>): GraphQLInputObjectType {
178160
val extensionDefinitions = inputExtensionDefinitions.filter { it.name == definition.name }
@@ -183,19 +165,19 @@ class SchemaParser internal constructor(
183165
.extensionDefinitions(extensionDefinitions)
184166
.description(if (definition.description != null) definition.description.content else getDocumentation(definition))
185167

186-
builder.withDirectives(*buildDirectives(definition.directives, setOf(), Introspection.DirectiveLocation.INPUT_OBJECT))
168+
builder.withDirectives(*buildDirectives(definition.directives, Introspection.DirectiveLocation.INPUT_OBJECT))
187169

188170
referencingInputObjects.add(definition.name)
189171

190172
(extensionDefinitions + definition).forEach {
191173
it.inputValueDefinitions.forEach { inputDefinition ->
192174
val fieldBuilder = GraphQLInputObjectField.newInputObjectField()
193-
.name(inputDefinition.name)
194-
.definition(inputDefinition)
195-
.description(if (inputDefinition.description != null) inputDefinition.description.content else getDocumentation(inputDefinition))
196-
.defaultValue(buildDefaultValue(inputDefinition.defaultValue))
197-
.type(determineInputType(inputDefinition.type, inputObjects, referencingInputObjects))
198-
.withDirectives(*buildDirectives(inputDefinition.directives, setOf(), Introspection.DirectiveLocation.INPUT_FIELD_DEFINITION))
175+
.name(inputDefinition.name)
176+
.definition(inputDefinition)
177+
.description(if (inputDefinition.description != null) inputDefinition.description.content else getDocumentation(inputDefinition))
178+
.defaultValue(buildDefaultValue(inputDefinition.defaultValue))
179+
.type(determineInputType(inputDefinition.type, inputObjects, referencingInputObjects))
180+
.withDirectives(*buildDirectives(inputDefinition.directives, setOf(), Introspection.DirectiveLocation.INPUT_FIELD_DEFINITION))
199181
builder.field(fieldBuilder.build())
200182
}
201183
}
@@ -214,14 +196,14 @@ class SchemaParser internal constructor(
214196
.definition(definition)
215197
.description(if (definition.description != null) definition.description.content else getDocumentation(definition))
216198

217-
builder.withDirectives(*buildDirectives(definition.directives, setOf(), Introspection.DirectiveLocation.ENUM))
199+
builder.withDirectives(*buildDirectives(definition.directives, Introspection.DirectiveLocation.ENUM))
218200

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

224-
val enumValueDirectives = buildDirectives(enumDefinition.directives, setOf(), Introspection.DirectiveLocation.ENUM_VALUE)
206+
val enumValueDirectives = buildDirectives(enumDefinition.directives, Introspection.DirectiveLocation.ENUM_VALUE)
225207
getDeprecated(enumDefinition.directives).let {
226208
val enumValueDefinition = GraphQLEnumValueDefinition.newEnumValueDefinition()
227209
.name(enumName)
@@ -246,7 +228,7 @@ class SchemaParser internal constructor(
246228
.definition(interfaceDefinition)
247229
.description(if (interfaceDefinition.description != null) interfaceDefinition.description.content else getDocumentation(interfaceDefinition))
248230

249-
builder.withDirectives(*buildDirectives(interfaceDefinition.directives, setOf(), Introspection.DirectiveLocation.INTERFACE))
231+
builder.withDirectives(*buildDirectives(interfaceDefinition.directives, Introspection.DirectiveLocation.INTERFACE))
250232

251233
interfaceDefinition.fieldDefinitions.forEach { fieldDefinition ->
252234
builder.field { field -> createField(field, fieldDefinition, inputObjects) }
@@ -262,7 +244,7 @@ class SchemaParser internal constructor(
262244
.definition(definition)
263245
.description(if (definition.description != null) definition.description.content else getDocumentation(definition))
264246

265-
builder.withDirectives(*buildDirectives(definition.directives, setOf(), Introspection.DirectiveLocation.UNION))
247+
builder.withDirectives(*buildDirectives(definition.directives, Introspection.DirectiveLocation.UNION))
266248

267249
getLeafUnionObjects(definition, types).forEach { builder.possibleType(it) }
268250
return schemaGeneratorDirectiveHelper.onUnion(builder.build(), schemaDirectiveParameters)
@@ -289,25 +271,95 @@ class SchemaParser internal constructor(
289271
}
290272

291273
private fun createField(field: GraphQLFieldDefinition.Builder, fieldDefinition: FieldDefinition, inputObjects: List<GraphQLInputObjectType>): GraphQLFieldDefinition.Builder {
292-
field.name(fieldDefinition.name)
293-
field.description(if (fieldDefinition.description != null) fieldDefinition.description.content else getDocumentation(fieldDefinition))
294-
field.definition(fieldDefinition)
295-
getDeprecated(fieldDefinition.directives)?.let { field.deprecate(it) }
296-
field.type(determineOutputType(fieldDefinition.type, inputObjects))
274+
field
275+
.name(fieldDefinition.name)
276+
.description(fieldDefinition.description?.content ?: getDocumentation(fieldDefinition))
277+
.definition(fieldDefinition)
278+
.apply { getDeprecated(fieldDefinition.directives)?.let { deprecate(it) } }
279+
.type(determineOutputType(fieldDefinition.type, inputObjects))
280+
297281
fieldDefinition.inputValueDefinitions.forEach { argumentDefinition ->
298282
val argumentBuilder = GraphQLArgument.newArgument()
299283
.name(argumentDefinition.name)
300284
.definition(argumentDefinition)
301285
.description(if (argumentDefinition.description != null) argumentDefinition.description.content else getDocumentation(argumentDefinition))
302-
.defaultValue(buildDefaultValue(argumentDefinition.defaultValue))
303286
.type(determineInputType(argumentDefinition.type, inputObjects, setOf()))
304-
.withDirectives(*buildDirectives(argumentDefinition.directives, setOf(), Introspection.DirectiveLocation.ARGUMENT_DEFINITION))
287+
.apply { buildDefaultValue(argumentDefinition.defaultValue)?.let { defaultValue(it) } }
288+
.withDirectives(*buildDirectives(argumentDefinition.directives, Introspection.DirectiveLocation.ARGUMENT_DEFINITION))
289+
305290
field.argument(argumentBuilder.build())
306291
}
307-
field.withDirectives(*buildDirectives(fieldDefinition.directives, setOf(), Introspection.DirectiveLocation.FIELD_DEFINITION))
292+
field.withDirectives(*buildDirectives(fieldDefinition.directives, Introspection.DirectiveLocation.FIELD_DEFINITION))
293+
308294
return field
309295
}
310296

297+
private fun buildDirectives(directives: List<Directive>, directiveLocation: Introspection.DirectiveLocation): Array<GraphQLDirective> {
298+
val names = mutableSetOf<String>()
299+
300+
val output = mutableListOf<GraphQLDirective>()
301+
for (directive in directives) {
302+
if (!names.contains(directive.name)) {
303+
names.add(directive.name)
304+
val graphQLDirective = GraphQLDirective.newDirective()
305+
.name(directive.name)
306+
.apply {
307+
directive.arguments.forEach { arg ->
308+
argument(GraphQLArgument.newArgument()
309+
.name(arg.name)
310+
.type(buildDirectiveInputType(arg.value))
311+
.build())
312+
}
313+
}
314+
.build()
315+
316+
317+
output.add(schemaGeneratorHelper.buildDirective(directive, setOf(graphQLDirective), directiveLocation, runtimeWiring.comparatorRegistry))
318+
}
319+
}
320+
321+
return output.toTypedArray()
322+
}
323+
324+
private fun buildDirectiveInputType(value: Value<*>): GraphQLInputType? {
325+
when (value) {
326+
is NullValue -> return Scalars.GraphQLString
327+
is FloatValue -> return Scalars.GraphQLFloat
328+
is StringValue -> return Scalars.GraphQLString
329+
is IntValue -> return Scalars.GraphQLInt
330+
is BooleanValue -> return Scalars.GraphQLBoolean
331+
is ArrayValue -> return GraphQLList.list(buildDirectiveInputType(getArrayValueWrappedType(value)))
332+
else -> throw SchemaError("Directive values of type '${value::class.simpleName}' are not supported yet.")
333+
}
334+
}
335+
336+
private fun getArrayValueWrappedType(value: ArrayValue): Value<*> {
337+
// empty array [] is equivalent to [null]
338+
if (value.values.isEmpty()) {
339+
return NullValue.newNullValue().build()
340+
}
341+
342+
// get rid of null values
343+
val nonNullValueList = value.values.filter { v -> v !is NullValue }
344+
345+
// [null, null, ...] unwrapped is null
346+
if (nonNullValueList.isEmpty()) {
347+
return NullValue.newNullValue().build()
348+
}
349+
350+
// make sure the array isn't polymorphic
351+
val distinctTypes = nonNullValueList
352+
.map { it::class.java }
353+
.distinct()
354+
355+
if (distinctTypes.size > 1) {
356+
throw SchemaError("Arrays containing multiple types of values are not supported yet.")
357+
}
358+
359+
// peek at first value, value exists and is assured to be non-null
360+
return nonNullValueList[0]
361+
}
362+
311363
private fun buildDefaultValue(value: Value<*>?): Any? {
312364
return when (value) {
313365
null -> null
@@ -335,7 +387,7 @@ class SchemaParser internal constructor(
335387
}
336388
is TypeName -> {
337389
val scalarType = customScalars[typeDefinition.name]
338-
?: graphQLScalars[typeDefinition.name]
390+
?: GRAPHQL_SCALARS[typeDefinition.name]
339391
if (scalarType != null) {
340392
scalarType
341393
} else {
@@ -365,7 +417,7 @@ class SchemaParser internal constructor(
365417
}
366418
is TypeName -> {
367419
val scalarType = customScalars[typeDefinition.name]
368-
?: graphQLScalars[typeDefinition.name]
420+
?: GRAPHQL_SCALARS[typeDefinition.name]
369421
if (scalarType != null) {
370422
scalarType
371423
} else {
@@ -417,4 +469,6 @@ class SchemaParser internal constructor(
417469

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

420-
val graphQLScalars = ScalarInfo.STANDARD_SCALARS.associateBy { it.name }
472+
val GRAPHQL_SCALARS = ScalarInfo.GRAPHQL_SPECIFICATION_SCALARS.associateBy { it.name }
473+
474+
const val DEFAULT_DEPRECATION_MESSAGE = "No longer supported"

src/main/kotlin/graphql/kickstart/tools/SchemaParserBuilder.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ class SchemaParserBuilder {
169169
files.forEach { documents.add(parser.parseDocument(readFile(it), it)) }
170170

171171
if (schemaString.isNotEmpty()) {
172-
documents.add(parser.parseDocument(this.schemaString.toString()))
172+
documents.add(parser.parseDocument(schemaString.toString()))
173173
}
174174
} catch (pce: ParseCancellationException) {
175175
val cause = pce.cause

0 commit comments

Comments
 (0)