Skip to content

Commit b99db4b

Browse files
smyrickShane Myrick
andauthored
[generator] Update additional types to work with input types (ExpediaGroup#817)
* Update additional types to work with input types * Add more test coverage * Update build.gradle.kts * Move AdditionalType class to internal * Remove 4.0.0 comments Co-authored-by: Shane Myrick <[email protected]>
1 parent fc97f67 commit b99db4b

File tree

9 files changed

+117
-18
lines changed

9 files changed

+117
-18
lines changed

detekt.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ comments:
1010

1111
complexity:
1212
LongParameterList:
13-
threshold: 10
13+
functionThreshold: 10
1414
active: true
1515
TooManyFunctions:
1616
thresholdInInterfaces: 20

graphql-kotlin-federation/src/main/kotlin/com/expediagroup/graphql/federation/FederatedSchemaGenerator.kt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,10 @@ open class FederatedSchemaGenerator(generatorConfig: FederatedSchemaGeneratorCon
3535
queries: List<TopLevelObject>,
3636
mutations: List<TopLevelObject>,
3737
subscriptions: List<TopLevelObject>,
38-
additionalTypes: Set<KType>
38+
additionalTypes: Set<KType>,
39+
additionalInputTypes: Set<KType>
3940
): GraphQLSchema {
40-
addAdditionalTypesWithAnnotation(ExtendsDirective::class)
41-
return super.generateSchema(queries, mutations, subscriptions, additionalTypes)
41+
addAdditionalTypesWithAnnotation(ExtendsDirective::class, inputType = false)
42+
return super.generateSchema(queries, mutations, subscriptions, additionalTypes, additionalInputTypes)
4243
}
4344
}

graphql-kotlin-schema-generator/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ tasks {
2323
limit {
2424
counter = "INSTRUCTION"
2525
value = "COVEREDRATIO"
26-
minimum = "0.98".toBigDecimal()
26+
minimum = "0.95".toBigDecimal()
2727
}
2828
limit {
2929
counter = "BRANCH"

graphql-kotlin-schema-generator/src/main/kotlin/com/expediagroup/graphql/generator/SchemaGenerator.kt

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package com.expediagroup.graphql.generator
1919
import com.expediagroup.graphql.SchemaGeneratorConfig
2020
import com.expediagroup.graphql.TopLevelObject
2121
import com.expediagroup.graphql.exceptions.InvalidPackagesException
22+
import com.expediagroup.graphql.generator.state.AdditionalType
2223
import com.expediagroup.graphql.generator.state.ClassScanner
2324
import com.expediagroup.graphql.generator.state.TypesCache
2425
import com.expediagroup.graphql.generator.types.generateGraphQLType
@@ -47,7 +48,7 @@ import kotlin.reflect.full.createType
4748
*/
4849
open class SchemaGenerator(internal val config: SchemaGeneratorConfig) : Closeable {
4950

50-
internal val additionalTypes: MutableSet<KType> = mutableSetOf()
51+
internal val additionalTypes: MutableSet<AdditionalType> = mutableSetOf()
5152
internal val classScanner = ClassScanner(config.supportedPackages)
5253
internal val cache = TypesCache(config.supportedPackages)
5354
internal val codeRegistry = GraphQLCodeRegistry.newCodeRegistry()
@@ -69,10 +70,13 @@ open class SchemaGenerator(internal val config: SchemaGeneratorConfig) : Closeab
6970
queries: List<TopLevelObject>,
7071
mutations: List<TopLevelObject> = emptyList(),
7172
subscriptions: List<TopLevelObject> = emptyList(),
72-
additionalTypes: Set<KType> = emptySet()
73+
additionalTypes: Set<KType> = emptySet(),
74+
additionalInputTypes: Set<KType> = emptySet()
7375
): GraphQLSchema {
7476

75-
this.additionalTypes.addAll(additionalTypes)
77+
this.additionalTypes.addAll(additionalTypes.map { AdditionalType(it, inputType = false) })
78+
this.additionalTypes.addAll(additionalInputTypes.map { AdditionalType(it, inputType = true) })
79+
7680
val builder = GraphQLSchema.newSchema()
7781
builder.query(generateQueries(this, queries))
7882
builder.mutation(generateMutations(this, mutations))
@@ -94,14 +98,15 @@ open class SchemaGenerator(internal val config: SchemaGeneratorConfig) : Closeab
9498
*
9599
* This is helpful for things like federation or combining external schemas
96100
*/
97-
protected fun addAdditionalTypesWithAnnotation(annotation: KClass<*>) {
101+
protected fun addAdditionalTypesWithAnnotation(annotation: KClass<*>, inputType: Boolean = false) {
98102
classScanner.getClassesWithAnnotation(annotation).forEach {
99-
additionalTypes.add(it.createType())
103+
additionalTypes.add(AdditionalType(it.createType(), inputType))
100104
}
101105
}
102106

103107
/**
104-
* Generate the GraphQL type for all the `additionalTypes`. They are generated as non-inputs and not as IDs.
108+
* Generate the GraphQL type for all the `additionalTypes`.
109+
*
105110
* If you need to provide more custom additional types that were not picked up from reflection of the schema objects,
106111
* you can provide more types to be added through [generateSchema].
107112
*
@@ -112,7 +117,7 @@ open class SchemaGenerator(internal val config: SchemaGeneratorConfig) : Closeab
112117
while (this.additionalTypes.isNotEmpty()) {
113118
val currentlyProcessedTypes = LinkedHashSet(this.additionalTypes)
114119
this.additionalTypes.clear()
115-
graphqlTypes.addAll(currentlyProcessedTypes.map { generateGraphQLType(this, it) })
120+
graphqlTypes.addAll(currentlyProcessedTypes.map { generateGraphQLType(this, it.kType, it.inputType) })
116121
}
117122

118123
return graphqlTypes.toSet()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* Copyright 2020 Expedia, Inc
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.expediagroup.graphql.generator.state
18+
19+
import kotlin.reflect.KType
20+
21+
/**
22+
* Used to represent the additional types to be included in the schema
23+
* and that can also be picked up at generation time by including all the
24+
* interface implementations that may not be used in the code.
25+
*/
26+
internal data class AdditionalType(
27+
val kType: KType,
28+
val inputType: Boolean = false
29+
)

graphql-kotlin-schema-generator/src/main/kotlin/com/expediagroup/graphql/generator/types/generateInterface.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package com.expediagroup.graphql.generator.types
1818

19+
import com.expediagroup.graphql.generator.state.AdditionalType
1920
import com.expediagroup.graphql.extensions.unwrapType
2021
import com.expediagroup.graphql.generator.SchemaGenerator
2122
import com.expediagroup.graphql.generator.extensions.getGraphQLDescription
@@ -58,7 +59,7 @@ internal fun generateInterface(generator: SchemaGenerator, kClass: KClass<*>): G
5859

5960
generator.classScanner.getSubTypesOf(kClass)
6061
.filter { it.isGraphQLIgnored().not() }
61-
.forEach { generator.additionalTypes.add(it.createType()) }
62+
.forEach { generator.additionalTypes.add(AdditionalType(it.createType(), inputType = false)) }
6263

6364
val interfaceType = builder.build()
6465
generator.codeRegistry.typeResolver(interfaceType) { env: TypeResolutionEnvironment -> env.schema.getObjectType(env.getObject<Any>().javaClass.kotlin.getSimpleName()) }

graphql-kotlin-schema-generator/src/test/kotlin/com/expediagroup/graphql/generator/SchemaGeneratorTest.kt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ class SchemaGeneratorTest {
3939
// Add a valid annotation
4040
generator.addTypes(MyCustomAnnotation::class)
4141
assertEquals(1, generator.additionalTypes.size)
42+
43+
generator.addInputTypes(MyCustomAnnotation::class)
44+
assertEquals(2, generator.additionalTypes.size)
4245
}
4346

4447
@Test
@@ -53,6 +56,18 @@ class SchemaGeneratorTest {
5356
assertEquals("SomeObjectWithAnnotation!", result.first().deepName)
5457
}
5558

59+
@Test
60+
fun generateAdditionalInputTypes() {
61+
val config = SchemaGeneratorConfig(listOf("com.expediagroup.graphql.generator"))
62+
val generator = CustomSchemaGenerator(config)
63+
generator.addInputTypes(MyCustomAnnotation::class)
64+
65+
val result = generator.generateCustomAdditionalTypes()
66+
67+
assertEquals(1, result.size)
68+
assertEquals("SomeObjectWithAnnotationInput!", result.first().deepName)
69+
}
70+
5671
@Test
5772
fun invalidPackagesThrowsException() {
5873
assertFailsWith(InvalidPackagesException::class) {
@@ -64,6 +79,8 @@ class SchemaGeneratorTest {
6479
class CustomSchemaGenerator(config: SchemaGeneratorConfig) : SchemaGenerator(config) {
6580
internal fun addTypes(annotation: KClass<*>) = addAdditionalTypesWithAnnotation(annotation)
6681

82+
internal fun addInputTypes(annotation: KClass<*>) = addAdditionalTypesWithAnnotation(annotation, true)
83+
6784
internal fun generateCustomAdditionalTypes() = generateAdditionalTypes()
6885
}
6986

graphql-kotlin-schema-generator/src/test/kotlin/com/expediagroup/graphql/generator/types/GenerateInterfaceTest.kt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import kotlin.math.PI
2626
import kotlin.test.assertEquals
2727
import kotlin.test.assertNotNull
2828

29-
internal class GenerateInterfaceTest : TypeTestHelper() {
29+
class GenerateInterfaceTest : TypeTestHelper() {
3030

3131
@Test
3232
fun `Test naming`() {
@@ -59,8 +59,8 @@ internal class GenerateInterfaceTest : TypeTestHelper() {
5959
val result = generateInterface(generator, Shape::class) as? GraphQLInterfaceType
6060
assertEquals("Shape", result?.name)
6161
assertEquals(2, generator.additionalTypes.size)
62-
assertNotNull(generator.additionalTypes.find { it.getSimpleName() == "Circle" })
63-
assertNotNull(generator.additionalTypes.find { it.getSimpleName() == "Square" })
62+
assertNotNull(generator.additionalTypes.find { it.kType.getSimpleName() == "Circle" })
63+
assertNotNull(generator.additionalTypes.find { it.kType.getSimpleName() == "Square" })
6464
}
6565

6666
@Test
@@ -69,8 +69,8 @@ internal class GenerateInterfaceTest : TypeTestHelper() {
6969
val result = generateInterface(generator, Pet::class) as? GraphQLInterfaceType
7070
assertEquals("Pet", result?.name)
7171
assertEquals(2, generator.additionalTypes.size)
72-
assertNotNull(generator.additionalTypes.find { it.getSimpleName() == "Cat" })
73-
assertNotNull(generator.additionalTypes.find { it.getSimpleName() == "Dog" })
72+
assertNotNull(generator.additionalTypes.find { it.kType.getSimpleName() == "Cat" })
73+
assertNotNull(generator.additionalTypes.find { it.kType.getSimpleName() == "Dog" })
7474
}
7575

7676
@Suppress("Detekt.UnusedPrivateClass")
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* Copyright 2020 Expedia, Inc
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.expediagroup.graphql.scalars
18+
19+
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
20+
import com.fasterxml.jackson.module.kotlin.readValue
21+
import org.junit.jupiter.api.Test
22+
import kotlin.test.assertEquals
23+
24+
class IDTest {
25+
26+
private val mapper = jacksonObjectMapper()
27+
28+
@Test
29+
fun testToString() {
30+
val id = ID("1")
31+
assertEquals(expected = "1", actual = id.toString())
32+
}
33+
34+
@Test
35+
fun serialization() {
36+
val id = ID("2")
37+
38+
val json = mapper.writeValueAsString(id)
39+
40+
assertEquals(expected = "\"2\"", actual = json)
41+
42+
val parsed: ID = mapper.readValue(json)
43+
44+
assertEquals(expected = "2", actual = parsed.value)
45+
}
46+
}

0 commit comments

Comments
 (0)