Skip to content

Commit 522516b

Browse files
smyrickrickfast
authored andcommitted
refactor: add schema state (#59)
* feat: pull generator state into a container * refactor: add schema state Add the SchemaState class so we can pass that through the generator and start breaking up the functions in the SchemaGenerator class * fix: remove unnessacary argument
1 parent a012240 commit 522516b

File tree

5 files changed

+74
-54
lines changed

5 files changed

+74
-54
lines changed

detekt_baseline.xml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,9 @@
22
<SmellBaseline>
33
<Blacklist timestamp="1538767296980"></Blacklist>
44
<Whitelist timestamp="1538768092037">
5-
<ID>FunctionOnlyReturningConstant:DirectiveTests.kt$Geography$@DirectiveOnFunction fun somethingCool(): String</ID>
65
<ID>LargeClass:SchemaGenerator.kt$SchemaGenerator</ID>
76
<ID>MethodOverloading:SchemaGeneratorTest.kt$SchemaGeneratorTest</ID>
8-
<ID>SpreadOperator:annotationExtensions.kt$(*directiveInfo.locations)</ID>
97
<ID>TooManyFunctions:SchemaGenerator.kt$SchemaGenerator</ID>
10-
<ID>UnsafeCallOnNullableType:SchemaGenerator.kt$SchemaGenerator$name!!</ID>
118
<ID>UnsafeCast:SchemaGeneratorAsyncTests.kt$SchemaGeneratorAsyncTests$schema.getObjectType("TopLevelQuery").getFieldDefinition("asynchronouslyDo").type as GraphQLNonNull</ID>
129
<ID>UnsafeCast:SchemaGeneratorAsyncTests.kt$SchemaGeneratorAsyncTests$schema.getObjectType("TopLevelQuery").getFieldDefinition("asynchronouslyDoSingle").type as GraphQLNonNull</ID>
1310
<ID>UnsafeCast:SchemaGeneratorAsyncTests.kt$SchemaGeneratorAsyncTests$schema.getObjectType("TopLevelQuery").getFieldDefinition("maybe").type as GraphQLNonNull</ID>

src/main/kotlin/com/expedia/graphql/schema/extensions/annotationExtensions.kt

Lines changed: 44 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -34,18 +34,23 @@ internal fun KAnnotatedElement.graphQLDescription(): String? {
3434
}
3535

3636
private fun KAnnotatedElement.listOfDirectives(): List<String> {
37-
val directiveNames = mutableListOf(this.getDeprecationReason()?.let { "deprecated" })
38-
39-
directiveNames.addAll(this.annotations
40-
.filter { it.getDirectiveInfo() != null }
41-
.map {
42-
when {
43-
it.getDirectiveInfo()?.name != "" -> "@${it.getDirectiveInfo()?.name}"
44-
else -> "@${it.annotationClass.simpleName}"
45-
}
37+
val deprecationReason: String? = this.getDeprecationReason()?.let { "deprecated" }
38+
39+
return this.annotations.asSequence()
40+
.mapNotNull { it.getDirectiveInfo() }
41+
.map {
42+
val directiveName = it.getDirectiveInfo()?.name
43+
val simpleName = it.annotationClass.simpleName
44+
45+
when {
46+
directiveName.isNullOrEmpty().not() -> "@$directiveName"
47+
simpleName.isNullOrEmpty().not() -> "@$simpleName"
48+
else -> null
4649
}
47-
)
48-
return directiveNames.filterNotNull()
50+
}
51+
.plus(deprecationReason)
52+
.filterNotNull()
53+
.toList()
4954
}
5055

5156
internal fun KAnnotatedElement.getDeprecationReason(): String? {
@@ -63,34 +68,41 @@ internal fun KAnnotatedElement.isGraphQLIgnored() = this.findAnnotation<GraphQLI
6368

6469
internal fun KAnnotatedElement.isGraphQLID() = this.findAnnotation<GraphQLID>() != null
6570

66-
internal fun Annotation.getDirectiveInfo() =
71+
internal fun Annotation.getDirectiveInfo(): DirectiveAnnotation? =
6772
this.annotationClass.annotations.find { it is DirectiveAnnotation } as? DirectiveAnnotation
6873

6974
internal fun KClass<out Annotation>.properties() = this.declaredMemberFunctions.filter(isNotBlackListed)
7075

7176
internal fun KAnnotatedElement.directives() =
72-
this.annotations.mapNotNull { annotation ->
73-
annotation.getDirectiveInfo()?.let { directiveInfo ->
74-
val builder = GraphQLDirective.newDirective()
75-
val name: String = if (directiveInfo.name.isNotEmpty()) {
76-
directiveInfo.name
77-
} else {
78-
annotation.annotationClass.simpleName ?: throw CouldNotGetNameOfAnnotationException(annotation.annotationClass)
79-
}
80-
builder.name(name.normalizeDirectiveName())
81-
.validLocations(*directiveInfo.locations)
82-
.description(directiveInfo.description)
83-
84-
annotation::class.properties().forEach { prop ->
85-
val propertyName = prop.name
86-
val value = prop.call(annotation)
87-
@Suppress("Detekt.UnsafeCast")
88-
val type = defaultGraphQLScalars(prop.returnType) as GraphQLInputType
89-
builder.argument(GraphQLArgument.newArgument().name(propertyName).value(value).type(type).build())
90-
}
77+
this.annotations.asSequence()
78+
.mapNotNull { it.getDirectiveInfo() }
79+
.map { it.getGraphQLDirective() }
80+
.toList()
9181

92-
builder.build()
82+
@Throws(CouldNotGetNameOfAnnotationException::class)
83+
private fun DirectiveAnnotation.getGraphQLDirective(): GraphQLDirective {
84+
val kClass: KClass<out DirectiveAnnotation> = this.annotationClass
85+
val builder = GraphQLDirective.newDirective()
86+
val name: String = if (this.name.isNotEmpty()) {
87+
this.name
88+
} else {
89+
kClass.simpleName ?: throw CouldNotGetNameOfAnnotationException(kClass)
9390
}
91+
92+
@Suppress("Detekt.SpreadOperator")
93+
builder.name(name.normalizeDirectiveName())
94+
.validLocations(*this.locations)
95+
.description(this.description)
96+
97+
kClass.properties().forEach { prop ->
98+
val propertyName = prop.name
99+
val value = prop.call(kClass)
100+
@Suppress("Detekt.UnsafeCast")
101+
val type = defaultGraphQLScalars(prop.returnType) as GraphQLInputType
102+
builder.argument(GraphQLArgument.newArgument().name(propertyName).value(value).type(type).build())
103+
}
104+
105+
return builder.build()
94106
}
95107

96108
private fun String.normalizeDirectiveName() = CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_CAMEL, this)

src/main/kotlin/com/expedia/graphql/schema/generator/SchemaGenerator.kt

Lines changed: 16 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,14 @@ import com.expedia.graphql.schema.extensions.isGraphQLContext
1616
import com.expedia.graphql.schema.extensions.isGraphQLID
1717
import com.expedia.graphql.schema.extensions.throwIfUnathorizedInterface
1818
import com.expedia.graphql.schema.extensions.wrapInNonNull
19+
import com.expedia.graphql.schema.generator.state.SchemaGeneratorState
1920
import com.expedia.graphql.schema.generator.types.defaultGraphQLScalars
2021
import com.expedia.graphql.schema.generator.types.enumType
2122
import com.expedia.graphql.schema.generator.types.getInputClassName
2223
import com.expedia.graphql.schema.models.KGraphQLType
2324
import graphql.TypeResolutionEnvironment
2425
import graphql.schema.DataFetcher
2526
import graphql.schema.GraphQLArgument
26-
import graphql.schema.GraphQLDirective
2727
import graphql.schema.GraphQLFieldDefinition
2828
import graphql.schema.GraphQLInputObjectField
2929
import graphql.schema.GraphQLInputObjectType
@@ -54,10 +54,8 @@ internal class SchemaGenerator(
5454
private val config: SchemaGeneratorConfig
5555
) {
5656

57-
private val cache = TypesCache(config.supportedPackages)
57+
private val state = SchemaGeneratorState(config.supportedPackages)
5858
private val subTypeMapper = SubTypeMapper(config.supportedPackages)
59-
private val additionTypes = mutableSetOf<GraphQLType>()
60-
private val directives = mutableSetOf<GraphQLDirective>()
6159

6260
internal fun generate(): GraphQLSchema {
6361
val builder = generateWithReflection()
@@ -74,12 +72,10 @@ internal class SchemaGenerator(
7472
}
7573

7674
private fun addAdditionalTypes(builder: GraphQLSchema.Builder) {
77-
additionTypes
78-
.filter { cache.doesNotContainGraphQLType(it) }
79-
.forEach { builder.additionalType(it) }
75+
state.getValidAdditionalTypes().forEach { builder.additionalType(it) }
8076
}
8177

82-
private fun addDirectives(builder: GraphQLSchema.Builder) = builder.additionalDirectives(directives)
78+
private fun addDirectives(builder: GraphQLSchema.Builder) = builder.additionalDirectives(state.directives)
8379

8480
private fun addQueries(builder: GraphQLSchema.Builder) {
8581
val queryBuilder = GraphQLObjectType.Builder()
@@ -126,7 +122,7 @@ internal class SchemaGenerator(
126122

127123
fn.directives().forEach {
128124
builder.withDirective(it)
129-
directives.add(it)
125+
state.directives.add(it)
130126
}
131127

132128
val args = mutableMapOf<String, Parameter>()
@@ -141,6 +137,7 @@ internal class SchemaGenerator(
141137
throw IllegalArgumentException("argument name is null or blank, $it")
142138
} else {
143139
// Kotlin 1.3 will support contracts, until then we need to force non-null
140+
@Suppress("Detekt.UnsafeCallOnNullableType")
144141
args[name!!] = Parameter(it.type.javaType as Class<*>, it.annotations)
145142
}
146143
}
@@ -191,7 +188,7 @@ internal class SchemaGenerator(
191188

192189
private fun objectFromReflection(type: KType, inputType: Boolean): GraphQLType {
193190
val cacheKey = TypesCacheKey(type, inputType)
194-
val cachedType = cache.get(cacheKey)
191+
val cachedType = state.cache.get(cacheKey)
195192

196193
if (cachedType != null) {
197194
return cachedType
@@ -201,7 +198,7 @@ internal class SchemaGenerator(
201198
val graphQLType = getGraphQLType(kClass, inputType, type)
202199
val kGraphQLType = KGraphQLType(kClass, graphQLType)
203200

204-
cache.put(cacheKey, kGraphQLType)
201+
state.cache.put(cacheKey, kGraphQLType)
205202

206203
return graphQLType
207204
}
@@ -218,15 +215,15 @@ internal class SchemaGenerator(
218215
GraphQLList.list(graphQLTypeOf(type.getTypeOfFirstArgument(), inputType))
219216

220217
private fun objectType(kClass: KClass<*>, interfaceType: GraphQLInterfaceType? = null): GraphQLType {
221-
return cache.buildIfNotUnderConstruction(kClass) { _ ->
218+
return state.cache.buildIfNotUnderConstruction(kClass) { _ ->
222219
val builder = GraphQLObjectType.newObject()
223220

224221
builder.name(kClass.simpleName)
225222
builder.description(kClass.graphQLDescription())
226223

227-
kClass.directives().map {
224+
kClass.directives().forEach {
228225
builder.withDirective(it)
229-
directives.add(it)
226+
state.directives.add(it)
230227
}
231228

232229
if (interfaceType != null) {
@@ -274,7 +271,7 @@ internal class SchemaGenerator(
274271
}
275272

276273
private fun interfaceType(kClass: KClass<*>): GraphQLType {
277-
return cache.buildIfNotUnderConstruction(kClass) { _ ->
274+
return state.cache.buildIfNotUnderConstruction(kClass) { _ ->
278275
val builder = GraphQLInterfaceType.newInterface()
279276

280277
builder.name(kClass.simpleName)
@@ -296,16 +293,16 @@ internal class SchemaGenerator(
296293
val objectType = objectType(it.kotlin, interfaceType)
297294
val key = TypesCacheKey(it.kotlin.createType(), false)
298295

299-
additionTypes.add(objectType)
300-
cache.put(key, KGraphQLType(it.kotlin, objectType))
296+
state.additionalTypes.add(objectType)
297+
state.cache.put(key, KGraphQLType(it.kotlin, objectType))
301298
}
302299

303300
interfaceType
304301
}
305302
}
306303

307304
private fun unionType(kClass: KClass<*>): GraphQLType {
308-
return cache.buildIfNotUnderConstruction(kClass) { _ ->
305+
return state.cache.buildIfNotUnderConstruction(kClass) { _ ->
309306
val builder = GraphQLUnionType.newUnionType()
310307

311308
builder.name(kClass.simpleName)
@@ -325,7 +322,7 @@ internal class SchemaGenerator(
325322
builder.possibleType(objectType as GraphQLObjectType)
326323
}
327324

328-
cache.put(key, KGraphQLType(it.kotlin, objectType))
325+
state.cache.put(key, KGraphQLType(it.kotlin, objectType))
329326
}
330327

331328
builder.build()
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.expedia.graphql.schema.generator.state
2+
3+
import com.expedia.graphql.schema.generator.TypesCache
4+
import graphql.schema.GraphQLDirective
5+
import graphql.schema.GraphQLType
6+
7+
internal class SchemaGeneratorState(supportedPackages: List<String>) {
8+
val cache = TypesCache(supportedPackages)
9+
val additionalTypes = mutableSetOf<GraphQLType>()
10+
val directives = mutableSetOf<GraphQLDirective>()
11+
12+
fun getValidAdditionalTypes(): List<GraphQLType> = additionalTypes.filter { cache.doesNotContainGraphQLType(it) }
13+
}

src/test/kotlin/com/expedia/graphql/schema/generator/DirectiveTests.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ class Geography(
8989
val type: GeoType,
9090
val locations: List<Location>
9191
) {
92+
@Suppress("Detekt.FunctionOnlyReturningConstant")
9293
@DirectiveOnFunction
9394
fun somethingCool(): String = "Something cool"
9495
}

0 commit comments

Comments
 (0)