Skip to content

Commit a1124c4

Browse files
authored
[plugin] generate separate file containing type aliases (#755)
Type aliases are unique per package so in order to ensure consistency we should generate separate file that will contain all String type aliases for custom scalars/IDs. Currently we generate typealiases per query file which means that if there are multiple queries referencing IDs (or custom scalars without custom mappers) we end up with invalid generated code due to the type aliases clash. Resolves: #746
1 parent 583c973 commit a1124c4

File tree

14 files changed

+95
-56
lines changed

14 files changed

+95
-56
lines changed

plugins/graphql-kotlin-gradle-plugin/src/test/kotlin/com/expediagroup/graphql/plugin/gradle/GraphQLGenerateClientTaskIT.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ class GraphQLGenerateClientTaskIT : GraphQLGradlePluginAbstractIT() {
7373

7474
assertEquals(TaskOutcome.SUCCESS, buildResult.task(":$GENERATE_CLIENT_TASK_NAME")?.outcome)
7575
assertTrue(File(testProjectDirectory, "build/generated/source/graphql/main/com/example/generated/JUnitQuery.kt").exists())
76+
assertTrue(File(testProjectDirectory, "build/generated/source/graphql/main/com/example/generated/GraphQLTypeAliases.kt").exists())
7677
assertEquals(TaskOutcome.SUCCESS, buildResult.task(":run")?.outcome)
7778
}
7879

plugins/graphql-kotlin-maven-plugin/src/integration/generate-client/src/test/kotlin/com/expediagroup/graphql/plugin/maven/GenerateClientMojoTest.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,9 @@ class GenerateClientMojoTest {
2525
@Test
2626
fun `verify client code was generated`() {
2727
val buildDirectory = System.getProperty("buildDirectory")
28-
val path = Paths.get(buildDirectory, "generated-sources", "graphql", "com", "expediagroup", "graphql", "plugin", "generated", "ExampleQuery.kt")
29-
assertTrue(path.toFile().exists(), "graphql client was generated")
28+
val queryFilePath = Paths.get(buildDirectory, "generated-sources", "graphql", "com", "expediagroup", "graphql", "plugin", "generated", "ExampleQuery.kt")
29+
assertTrue(queryFilePath.toFile().exists(), "graphql client query file was generated")
30+
val typeAliasesFilePath = Paths.get(buildDirectory, "generated-sources", "graphql", "com", "expediagroup", "graphql", "plugin", "generated", "GraphQLTypeAliases.kt")
31+
assertTrue(queryFilePath.toFile().exists(), "graphql client type aliases were generated")
3032
}
3133
}

plugins/graphql-kotlin-plugin-core/README.md

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,7 @@ using [square/kotlinpoet](https://github.com/square/kotlinpoet) library.
3232
* Only a single operation per GraphQL query file is supported.
3333
* Subscriptions are currently NOT supported.
3434
* You cannot make multiple selections to the same GraphQL object with different fields within a single GraphQL query.
35-
But you can have different selection sets across different GraphQL queries, e.g.
36-
* Anonymous operations are supported but will result in generic `AnonymousQuery` (for query operation) class. Plugins
37-
do not keep track of state across different query generations so if you have multiple anonymous operations in a single
38-
package your compilation will fail due to the generic class name collisions.
35+
But you can have different selection sets across different GraphQL queries
3936
* Nested queries have limited support as same object will be used for ALL nested results. This means that you have to
4037
explicitly ask for data from ALL nested levels + the NULL/empty child following it (that may skip recursive field selection
4138
as it will be NULL)

plugins/graphql-kotlin-plugin-core/src/main/kotlin/com/expediagroup/graphql/plugin/generateClient.kt

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,5 @@ fun generateClient(
3232
): List<FileSpec> {
3333
val graphQLSchema = SchemaParser().parse(schema)
3434
val generator = GraphQLClientGenerator(graphQLSchema, config)
35-
return queries.map { queryFile ->
36-
generator.generate(queryFile)
37-
}
35+
return generator.generate(queries)
3836
}

plugins/graphql-kotlin-plugin-core/src/main/kotlin/com/expediagroup/graphql/plugin/generator/GraphQLClientGenerator.kt

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
2626
import com.squareup.kotlinpoet.PropertySpec
2727
import com.squareup.kotlinpoet.STAR
2828
import com.squareup.kotlinpoet.STRING
29+
import com.squareup.kotlinpoet.TypeAliasSpec
2930
import com.squareup.kotlinpoet.TypeSpec
3031
import graphql.language.ObjectTypeDefinition
3132
import graphql.language.OperationDefinition
@@ -44,11 +45,30 @@ class GraphQLClientGenerator(
4445
private val config: GraphQLClientGeneratorConfig
4546
) {
4647
private val documentParser: Parser = Parser()
48+
private val typeAliases: MutableMap<String, TypeAliasSpec> = mutableMapOf()
49+
50+
/**
51+
* Generate GraphQL clients for the specified queries.
52+
*/
53+
fun generate(queries: List<File>): List<FileSpec> {
54+
val result = mutableListOf<FileSpec>()
55+
for (query in queries) {
56+
result.add(generate(query))
57+
}
58+
if (typeAliases.isNotEmpty()) {
59+
val typeAliasSpec = FileSpec.builder(packageName = config.packageName, fileName = "GraphQLTypeAliases")
60+
typeAliases.forEach { (_, alias) ->
61+
typeAliasSpec.addTypeAlias(alias)
62+
}
63+
result.add(typeAliasSpec.build())
64+
}
65+
return result
66+
}
4767

4868
/**
4969
* Generate GraphQL client wrapper class and data classes that match the specified query.
5070
*/
51-
fun generate(queryFile: File): FileSpec {
71+
internal fun generate(queryFile: File): FileSpec {
5272
val queryConst = queryFile.readText()
5373
val queryDocument = documentParser.parseDocument(queryConst)
5474

@@ -60,7 +80,7 @@ class GraphQLClientGenerator(
6080
val fileSpec = FileSpec.builder(packageName = config.packageName, fileName = queryFile.nameWithoutExtension.capitalize())
6181

6282
operationDefinitions.forEach { operationDefinition ->
63-
val operationName = operationDefinition.name ?: "anonymous${operationDefinition.operation.name.toLowerCase().capitalize()}"
83+
val operationName = operationDefinition.name ?: queryFile.nameWithoutExtension.capitalize()
6484
val operationTypeName = operationName.capitalize()
6585
val context = GraphQLClientGeneratorContext(
6686
packageName = config.packageName,
@@ -102,18 +122,16 @@ class GraphQLClientGenerator(
102122
.initializer("graphQLClient").build())
103123
operationTypeSpec.addFunction(funSpec.build())
104124

105-
context.typeAliases.forEach { (_, alias) ->
106-
fileSpec.addTypeAlias(alias)
107-
}
108125
context.typeSpecs.forEach {
109126
operationTypeSpec.addType(it.value)
110127
}
111128
fileSpec.addProperty(PropertySpec.builder(queryConstName, STRING)
112129
.addModifiers(KModifier.CONST)
113130
.initializer("%S", queryConst).build())
114131
fileSpec.addType(operationTypeSpec.build())
115-
}
116132

133+
typeAliases.putAll(context.typeAliases)
134+
}
117135
return fileSpec.build()
118136
}
119137

plugins/graphql-kotlin-plugin-core/src/test/kotlin/com/expediagroup/graphql/plugin/generator/GraphQLTestUtils.kt

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

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

19+
import com.squareup.kotlinpoet.FileSpec
1920
import graphql.schema.idl.SchemaParser
2021
import graphql.schema.idl.TypeDefinitionRegistry
2122
import kotlin.test.assertEquals
@@ -27,17 +28,23 @@ internal fun testSchema(): TypeDefinitionRegistry {
2728
}
2829
}
2930

30-
internal fun verifyGraphQLClientGeneration(
31+
internal fun generateTestFileSpec(
3132
query: String,
32-
expected: String,
3333
graphQLConfig: GraphQLClientGeneratorConfig = GraphQLClientGeneratorConfig(packageName = "com.expediagroup.graphql.plugin.generator.integration")
34-
) {
34+
): List<FileSpec> {
3535
val queryFile = createTempFile(suffix = ".graphql")
3636
queryFile.deleteOnExit()
3737
queryFile.writeText(query)
3838

3939
val generator = GraphQLClientGenerator(testSchema(), graphQLConfig)
40-
val fileSpec = generator.generate(queryFile)
40+
return generator.generate(listOf(queryFile))
41+
}
4142

42-
assertEquals(expected, fileSpec.toString().trim())
43+
internal fun verifyGeneratedFileSpecContents(
44+
query: String,
45+
expected: String,
46+
graphQLConfig: GraphQLClientGeneratorConfig = GraphQLClientGeneratorConfig(packageName = "com.expediagroup.graphql.plugin.generator.integration")
47+
) {
48+
val fileSpecs = generateTestFileSpec(query, graphQLConfig)
49+
assertEquals(expected, fileSpecs.first().toString().trim())
4350
}

plugins/graphql-kotlin-plugin-core/src/test/kotlin/com/expediagroup/graphql/plugin/generator/types/GenerateGraphQLCustomScalarTypeAliasIT.kt

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,27 +16,35 @@
1616

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

19-
import com.expediagroup.graphql.plugin.generator.verifyGraphQLClientGeneration
19+
import com.expediagroup.graphql.plugin.generator.generateTestFileSpec
2020
import org.junit.jupiter.api.Test
21+
import kotlin.test.assertEquals
2122

2223
class GenerateGraphQLCustomScalarTypeAliasIT {
2324

2425
@Test
2526
fun `verify can generate type aliases for GraphQL ID and custom scalars`() {
26-
val expected = """
27+
val expectedGraphQLAliasTypeSpec = """
2728
package com.expediagroup.graphql.plugin.generator.integration
2829
29-
import com.expediagroup.graphql.client.GraphQLClient
30-
import com.expediagroup.graphql.types.GraphQLResponse
3130
import kotlin.String
3231
32+
typealias ID = String
33+
3334
/**
3435
* Custom scalar representing UUID
3536
*/
3637
typealias UUID = String
38+
""".trimIndent()
39+
val expectedQueryFileSpec = """
40+
package com.expediagroup.graphql.plugin.generator.integration
41+
42+
import com.expediagroup.graphql.client.GraphQLClient
43+
import com.expediagroup.graphql.types.GraphQLResponse
44+
import kotlin.String
3745
3846
const val SCALAR_ALIAS_TEST_QUERY: String =
39-
"query ScalarAliasTestQuery {\n scalarQuery {\n custom\n }\n}"
47+
"query ScalarAliasTestQuery {\n scalarQuery {\n id\n custom\n }\n}"
4048
4149
class ScalarAliasTestQuery(
4250
private val graphQLClient: GraphQLClient<*>
@@ -48,6 +56,10 @@ class GenerateGraphQLCustomScalarTypeAliasIT {
4856
* Wrapper that holds all supported scalar types
4957
*/
5058
data class ScalarWrapper(
59+
/**
60+
* ID represents unique identifier that is not intended to be human readable
61+
*/
62+
val id: ID,
5163
/**
5264
* Custom scalar
5365
*/
@@ -66,10 +78,14 @@ class GenerateGraphQLCustomScalarTypeAliasIT {
6678
val query = """
6779
query ScalarAliasTestQuery {
6880
scalarQuery {
81+
id
6982
custom
7083
}
7184
}
7285
""".trimIndent()
73-
verifyGraphQLClientGeneration(query, expected)
86+
val fileSpecs = generateTestFileSpec(query)
87+
assertEquals(2, fileSpecs.size)
88+
assertEquals(expectedQueryFileSpec, fileSpecs[0].toString().trim())
89+
assertEquals(expectedGraphQLAliasTypeSpec, fileSpecs[1].toString().trim())
7490
}
7591
}

plugins/graphql-kotlin-plugin-core/src/test/kotlin/com/expediagroup/graphql/plugin/generator/types/GenerateGraphQLCustomScalarTypeSpecIT.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ package com.expediagroup.graphql.plugin.generator.types
1818

1919
import com.expediagroup.graphql.plugin.generator.GraphQLClientGeneratorConfig
2020
import com.expediagroup.graphql.plugin.generator.ScalarConverterMapping
21-
import com.expediagroup.graphql.plugin.generator.verifyGraphQLClientGeneration
21+
import com.expediagroup.graphql.plugin.generator.verifyGeneratedFileSpecContents
2222
import org.junit.jupiter.api.Test
2323

2424
class GenerateGraphQLCustomScalarTypeSpecIT {
@@ -90,7 +90,7 @@ class GenerateGraphQLCustomScalarTypeSpecIT {
9090
}
9191
""".trimIndent()
9292

93-
verifyGraphQLClientGeneration(
93+
verifyGeneratedFileSpecContents(
9494
query,
9595
expected,
9696
GraphQLClientGeneratorConfig(

plugins/graphql-kotlin-plugin-core/src/test/kotlin/com/expediagroup/graphql/plugin/generator/types/GenerateGraphQLEnumTypeSpecIT.kt

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

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

19-
import com.expediagroup.graphql.plugin.generator.verifyGraphQLClientGeneration
19+
import com.expediagroup.graphql.plugin.generator.verifyGeneratedFileSpecContents
2020
import org.junit.jupiter.api.Test
2121

2222
class GenerateGraphQLEnumTypeSpecIT {
@@ -82,6 +82,6 @@ class GenerateGraphQLEnumTypeSpecIT {
8282
}
8383
""".trimIndent()
8484

85-
verifyGraphQLClientGeneration(query, expected)
85+
verifyGeneratedFileSpecContents(query, expected)
8686
}
8787
}

plugins/graphql-kotlin-plugin-core/src/test/kotlin/com/expediagroup/graphql/plugin/generator/types/GenerateGraphQLInputObjectTypeSpecIT.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

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

19-
import com.expediagroup.graphql.plugin.generator.verifyGraphQLClientGeneration
19+
import com.expediagroup.graphql.plugin.generator.verifyGeneratedFileSpecContents
2020
import org.junit.jupiter.api.Test
2121

2222
class GenerateGraphQLInputObjectTypeSpecIT {
@@ -54,7 +54,7 @@ class GenerateGraphQLInputObjectTypeSpecIT {
5454
inputObjectQuery(criteria: { min: 1.0, max: 5.0 } )
5555
}
5656
""".trimIndent()
57-
verifyGraphQLClientGeneration(query, expected)
57+
verifyGeneratedFileSpecContents(query, expected)
5858
}
5959

6060
@Test
@@ -95,6 +95,6 @@ class GenerateGraphQLInputObjectTypeSpecIT {
9595
second: inputObjectQuery(criteria: { min: 5.0, max: 10.0 } )
9696
}
9797
""".trimIndent()
98-
verifyGraphQLClientGeneration(query, expected)
98+
verifyGeneratedFileSpecContents(query, expected)
9999
}
100100
}

0 commit comments

Comments
 (0)