Skip to content

Commit 1b1803e

Browse files
authored
Merge pull request #564 from graphql-java-kickstart/feature/comment-documentation-2
Add an option to fallback to comments if a description is not provided
2 parents 9f35a4b + 7b3b016 commit 1b1803e

File tree

5 files changed

+91
-17
lines changed

5 files changed

+91
-17
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ internal class SchemaClassScanner(
168168
?: throw SchemaClassScannerError("Expected a user-defined GraphQL scalar type with name '${definition.name}' but found none!")
169169
GraphQLScalarType.newScalar()
170170
.name(provided.name)
171-
.description(definition.description?.content ?: getDocumentation(definition) ?: provided.description)
171+
.description(getDocumentation(definition, options) ?: provided.description)
172172
.coercing(provided.coercing)
173173
.definition(definition)
174174
.build()

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

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ class SchemaParser internal constructor(
122122
val builder = GraphQLObjectType.newObject()
123123
.name(name)
124124
.definition(objectDefinition)
125-
.description(if (objectDefinition.description != null) objectDefinition.description.content else getDocumentation(objectDefinition))
125+
.description(getDocumentation(objectDefinition, options))
126126

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

@@ -133,7 +133,6 @@ class SchemaParser internal constructor(
133133
}
134134

135135
objectDefinition.getExtendedFieldDefinitions(extensionDefinitions).forEach { fieldDefinition ->
136-
fieldDefinition.description
137136
builder.field { field ->
138137
createField(field, fieldDefinition, inputObjects)
139138
codeRegistryBuilder.dataFetcher(
@@ -163,7 +162,7 @@ class SchemaParser internal constructor(
163162
.name(definition.name)
164163
.definition(definition)
165164
.extensionDefinitions(extensionDefinitions)
166-
.description(if (definition.description != null) definition.description.content else getDocumentation(definition))
165+
.description(getDocumentation(definition, options))
167166

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

@@ -174,7 +173,7 @@ class SchemaParser internal constructor(
174173
val fieldBuilder = GraphQLInputObjectField.newInputObjectField()
175174
.name(inputDefinition.name)
176175
.definition(inputDefinition)
177-
.description(if (inputDefinition.description != null) inputDefinition.description.content else getDocumentation(inputDefinition))
176+
.description(getDocumentation(inputDefinition, options))
178177
.defaultValue(buildDefaultValue(inputDefinition.defaultValue))
179178
.type(determineInputType(inputDefinition.type, inputObjects, referencingInputObjects))
180179
.withDirectives(*buildDirectives(inputDefinition.directives, Introspection.DirectiveLocation.INPUT_FIELD_DEFINITION))
@@ -194,7 +193,7 @@ class SchemaParser internal constructor(
194193
val builder = GraphQLEnumType.newEnum()
195194
.name(name)
196195
.definition(definition)
197-
.description(if (definition.description != null) definition.description.content else getDocumentation(definition))
196+
.description(getDocumentation(definition, options))
198197

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

@@ -207,7 +206,7 @@ class SchemaParser internal constructor(
207206
getDeprecated(enumDefinition.directives).let {
208207
val enumValueDefinition = GraphQLEnumValueDefinition.newEnumValueDefinition()
209208
.name(enumName)
210-
.description(if (enumDefinition.description != null) enumDefinition.description.content else getDocumentation(enumDefinition))
209+
.description(getDocumentation(enumDefinition, options))
211210
.value(enumValue)
212211
.deprecationReason(it)
213212
.withDirectives(*enumValueDirectives)
@@ -226,7 +225,7 @@ class SchemaParser internal constructor(
226225
val builder = GraphQLInterfaceType.newInterface()
227226
.name(name)
228227
.definition(interfaceDefinition)
229-
.description(if (interfaceDefinition.description != null) interfaceDefinition.description.content else getDocumentation(interfaceDefinition))
228+
.description(getDocumentation(interfaceDefinition, options))
230229

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

@@ -247,7 +246,7 @@ class SchemaParser internal constructor(
247246
val builder = GraphQLUnionType.newUnionType()
248247
.name(name)
249248
.definition(definition)
250-
.description(if (definition.description != null) definition.description.content else getDocumentation(definition))
249+
.description(getDocumentation(definition, options))
251250

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

@@ -278,7 +277,7 @@ class SchemaParser internal constructor(
278277
private fun createField(field: GraphQLFieldDefinition.Builder, fieldDefinition: FieldDefinition, inputObjects: List<GraphQLInputObjectType>): GraphQLFieldDefinition.Builder {
279278
field
280279
.name(fieldDefinition.name)
281-
.description(fieldDefinition.description?.content ?: getDocumentation(fieldDefinition))
280+
.description(getDocumentation(fieldDefinition, options))
282281
.definition(fieldDefinition)
283282
.apply { getDeprecated(fieldDefinition.directives)?.let { deprecate(it) } }
284283
.type(determineOutputType(fieldDefinition.type, inputObjects))
@@ -287,7 +286,7 @@ class SchemaParser internal constructor(
287286
val argumentBuilder = GraphQLArgument.newArgument()
288287
.name(argumentDefinition.name)
289288
.definition(argumentDefinition)
290-
.description(if (argumentDefinition.description != null) argumentDefinition.description.content else getDocumentation(argumentDefinition))
289+
.description(getDocumentation(argumentDefinition, options))
291290
.type(determineInputType(argumentDefinition.type, inputObjects, setOf()))
292291
.apply { buildDefaultValue(argumentDefinition.defaultValue)?.let { defaultValue(it) } }
293292
.withDirectives(*buildDirectives(argumentDefinition.directives, Introspection.DirectiveLocation.ARGUMENT_DEFINITION))

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

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ data class SchemaParserOptions internal constructor(
3232
val coroutineContextProvider: CoroutineContextProvider,
3333
val typeDefinitionFactories: List<TypeDefinitionFactory>,
3434
val fieldVisibility: GraphqlFieldVisibility?,
35-
val includeUnusedTypes: Boolean
35+
val includeUnusedTypes: Boolean,
36+
val useCommentsForDescriptions: Boolean
3637
) {
3738
companion object {
3839
@JvmStatic
@@ -61,6 +62,7 @@ data class SchemaParserOptions internal constructor(
6162
private var typeDefinitionFactories: MutableList<TypeDefinitionFactory> = mutableListOf(RelayConnectionFactory())
6263
private var fieldVisibility: GraphqlFieldVisibility? = null
6364
private var includeUnusedTypes = false
65+
private var useCommentsForDescriptions = true
6466

6567
fun contextClass(contextClass: Class<*>) = this.apply {
6668
this.contextClass = contextClass
@@ -138,6 +140,10 @@ data class SchemaParserOptions internal constructor(
138140
this.includeUnusedTypes = includeUnusedTypes
139141
}
140142

143+
fun useCommentsForDescriptions(useCommentsForDescriptions: Boolean) = this.apply {
144+
this.useCommentsForDescriptions = useCommentsForDescriptions
145+
}
146+
141147
@ExperimentalCoroutinesApi
142148
fun build(): SchemaParserOptions {
143149
val coroutineContextProvider = coroutineContextProvider
@@ -177,7 +183,8 @@ data class SchemaParserOptions internal constructor(
177183
coroutineContextProvider,
178184
typeDefinitionFactories,
179185
fieldVisibility,
180-
includeUnusedTypes
186+
includeUnusedTypes,
187+
useCommentsForDescriptions
181188
)
182189
}
183190
}

src/main/kotlin/graphql/kickstart/tools/util/Utils.kt

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

33
import graphql.kickstart.tools.GraphQLResolver
4+
import graphql.kickstart.tools.SchemaParserOptions
45
import graphql.language.*
56
import graphql.schema.DataFetchingEnvironment
67
import kotlinx.coroutines.CoroutineScope
@@ -49,10 +50,16 @@ internal val Class<*>.declaredNonProxyMethods: List<JavaMethod>
4950
}
5051
}
5152

52-
internal fun getDocumentation(node: AbstractNode<*>): String? = node.comments?.asSequence()
53-
?.filter { !it.content.startsWith("#") }
54-
?.joinToString("\n") { it.content.trimEnd() }
55-
?.trimIndent()
53+
internal fun getDocumentation(node: AbstractDescribedNode<*>, options: SchemaParserOptions): String? =
54+
when {
55+
node.description != null -> node.description.content
56+
!options.useCommentsForDescriptions -> null
57+
node.comments.isNullOrEmpty() -> null
58+
else -> node.comments.asSequence()
59+
.filter { !it.content.startsWith("#") }
60+
.joinToString("\n") { it.content.trimEnd() }
61+
.trimIndent()
62+
}
5663

5764
/**
5865
* Simple heuristic to check is a method is a trivial data fetcher.

src/test/kotlin/graphql/kickstart/tools/SchemaParserTest.kt

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -525,6 +525,67 @@ class SchemaParserTest {
525525
assert(testNullableArgument.type is GraphQLInputObjectType)
526526
}
527527

528+
@Test
529+
fun `parser should use comments for descriptions`() {
530+
val schema = SchemaParser.newParser()
531+
.schemaString(
532+
"""
533+
type Query {
534+
"description"
535+
description: String
536+
#comment
537+
comment: String
538+
omitted: String
539+
"description"
540+
#comment
541+
both: String
542+
""
543+
empty: String
544+
}
545+
""")
546+
.resolvers(object : GraphQLQueryResolver {})
547+
.options(SchemaParserOptions.newOptions().allowUnimplementedResolvers(true).build())
548+
.build()
549+
.makeExecutableSchema()
550+
551+
val queryType = schema.getObjectType("Query")
552+
assertEquals(queryType.getFieldDefinition("description").description, "description")
553+
assertEquals(queryType.getFieldDefinition("comment").description, "comment")
554+
assertNull(queryType.getFieldDefinition("omitted").description)
555+
assertEquals(queryType.getFieldDefinition("both").description, "description")
556+
assertEquals(queryType.getFieldDefinition("empty").description, "")
557+
}
558+
559+
@Test
560+
fun `parser should not use comments for descriptions`() {
561+
val schema = SchemaParser.newParser()
562+
.schemaString(
563+
"""
564+
type Query {
565+
"description"
566+
description: String
567+
#comment
568+
comment: String
569+
omitted: String
570+
"description"
571+
#comment
572+
both: String
573+
""
574+
empty: String
575+
}
576+
""")
577+
.resolvers(object : GraphQLQueryResolver {})
578+
.options(SchemaParserOptions.newOptions().useCommentsForDescriptions(false).allowUnimplementedResolvers(true).build())
579+
.build()
580+
.makeExecutableSchema()
581+
582+
assertEquals(schema.queryType.getFieldDefinition("description").description, "description")
583+
assertNull(schema.queryType.getFieldDefinition("comment").description)
584+
assertNull(schema.queryType.getFieldDefinition("omitted").description)
585+
assertEquals(schema.queryType.getFieldDefinition("both").description, "description")
586+
assertEquals(schema.queryType.getFieldDefinition("empty").description, "")
587+
}
588+
528589
enum class EnumType {
529590
TEST
530591
}

0 commit comments

Comments
 (0)