Skip to content

Commit edad0b4

Browse files
authored
Client Generation - Allow schema to be on the classpath (ExpediaGroup#1136)
* Client Generation - Allow schema SDL to be on the classpath * Suggestions and Refactors * Minor touchup * More improvements to the code, remove redundant edits
1 parent 6a262aa commit edad0b4

File tree

10 files changed

+60
-44
lines changed

10 files changed

+60
-44
lines changed

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

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import com.expediagroup.graphql.plugin.client.generator.GraphQLClientGeneratorCo
2121
import com.expediagroup.graphql.plugin.client.generator.GraphQLSerializer
2222
import com.expediagroup.graphql.plugin.client.generator.GraphQLScalar
2323
import com.squareup.kotlinpoet.FileSpec
24-
import graphql.schema.idl.SchemaParser
2524
import java.io.File
2625

2726
/**
@@ -32,7 +31,7 @@ fun generateClient(
3231
allowDeprecated: Boolean = false,
3332
customScalarsMap: List<GraphQLScalar> = emptyList(),
3433
serializer: GraphQLSerializer = GraphQLSerializer.JACKSON,
35-
schema: File,
34+
schemaPath: String,
3635
queries: List<File>
3736
): List<FileSpec> {
3837
val customScalars = customScalarsMap.associateBy { it.scalar }
@@ -42,7 +41,6 @@ fun generateClient(
4241
customScalarMap = customScalars,
4342
serializer = serializer
4443
)
45-
val graphQLSchema = SchemaParser().parse(schema)
46-
val generator = GraphQLClientGenerator(graphQLSchema, config)
44+
val generator = GraphQLClientGenerator(schemaPath, config)
4745
return generator.generate(queries)
4846
}

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

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package com.expediagroup.graphql.plugin.client.generator
1818

1919
import com.expediagroup.graphql.plugin.client.generator.exceptions.MultipleOperationsInFileException
20+
import com.expediagroup.graphql.plugin.client.generator.exceptions.SchemaUnavailableException
2021
import com.expediagroup.graphql.plugin.client.generator.types.generateGraphQLObjectTypeSpec
2122
import com.expediagroup.graphql.plugin.client.generator.types.generateVariableTypeSpec
2223
import com.squareup.kotlinpoet.ClassName
@@ -31,6 +32,7 @@ import com.squareup.kotlinpoet.TypeSpec
3132
import graphql.language.ObjectTypeDefinition
3233
import graphql.language.OperationDefinition
3334
import graphql.parser.Parser
35+
import graphql.schema.idl.SchemaParser
3436
import graphql.schema.idl.TypeDefinitionRegistry
3537
import kotlinx.serialization.Serializable
3638
import java.io.File
@@ -41,12 +43,17 @@ private const val CORE_TYPES_PACKAGE = "com.expediagroup.graphql.client.types"
4143
* GraphQL client code generator that uses [KotlinPoet](https://github.com/square/kotlinpoet) to generate Kotlin classes based on the specified GraphQL queries.
4244
*/
4345
class GraphQLClientGenerator(
44-
private val graphQLSchema: TypeDefinitionRegistry,
46+
schemaPath: String,
4547
private val config: GraphQLClientGeneratorConfig
4648
) {
4749
private val documentParser: Parser = Parser()
4850
private val typeAliases: MutableMap<String, TypeAliasSpec> = mutableMapOf()
4951
private val sharedTypes: MutableMap<ClassName, List<TypeSpec>> = mutableMapOf()
52+
private val graphQLSchema: TypeDefinitionRegistry
53+
54+
init {
55+
graphQLSchema = parseSchema(schemaPath)
56+
}
5057

5158
/**
5259
* Generate GraphQL clients for the specified queries.
@@ -196,6 +203,16 @@ class GraphQLClientGenerator(
196203
val rootType = operationNames[operationDefinition.operation.name]
197204
return graphQLSchema.getType(rootType).get() as ObjectTypeDefinition
198205
}
206+
207+
private fun parseSchema(path: String): TypeDefinitionRegistry {
208+
val schemaFile = File(path)
209+
return if (schemaFile.isFile) {
210+
SchemaParser().parse(schemaFile)
211+
} else {
212+
val schemaInputStream = this.javaClass.classLoader.getResourceAsStream(path) ?: throw SchemaUnavailableException(path)
213+
SchemaParser().parse(schemaInputStream)
214+
}
215+
}
199216
}
200217

201218
internal fun String.toUpperUnderscore(): String {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package com.expediagroup.graphql.plugin.client.generator.exceptions
2+
3+
/**
4+
* Exception thrown when specified schema file path is not found or unavailable
5+
*/
6+
internal class SchemaUnavailableException(schemaPath: String) : RuntimeException("Specified schema file=$schemaPath does not exist")

plugins/client/graphql-kotlin-client-generator/src/test/kotlin/com/expediagroup/graphql/plugin/client/generator/GenerateInvalidClientIT.kt

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

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

19+
import com.expediagroup.graphql.plugin.client.generator.exceptions.SchemaUnavailableException
20+
import org.junit.jupiter.api.Test
1921
import org.junit.jupiter.params.ParameterizedTest
2022
import org.junit.jupiter.params.provider.Arguments
2123
import org.junit.jupiter.params.provider.MethodSource
@@ -31,13 +33,21 @@ class GenerateInvalidClientIT {
3133
val (queries, _) = locateTestFiles(testDirectory)
3234
val expectedException = File(testDirectory, "exception.txt").readText().trim()
3335

34-
val generator = GraphQLClientGenerator(testSchema(), defaultConfig)
36+
val generator = GraphQLClientGenerator(TEST_SCHEMA_PATH, defaultConfig)
3537
val exception = assertFails {
3638
generator.generate(queries)
3739
}
3840
assertEquals(expectedException, exception::class.simpleName)
3941
}
4042

43+
@Test
44+
fun `verify an invalid schema path will raise an exception`() {
45+
val exception = assertFails {
46+
GraphQLClientGenerator("missingSchema.graphql", defaultConfig)
47+
}
48+
assertEquals(SchemaUnavailableException::class, exception::class)
49+
}
50+
4151
companion object {
4252
@JvmStatic
4353
fun invalidTests(): List<Arguments> = locateTestCaseArguments("src/test/data/invalid")

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

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@ package com.expediagroup.graphql.plugin.client.generator
1919
import com.expediagroup.graphql.client.converter.ScalarConverter
2020
import com.tschuchort.compiletesting.KotlinCompilation
2121
import com.tschuchort.compiletesting.SourceFile
22-
import graphql.schema.idl.SchemaParser
23-
import graphql.schema.idl.TypeDefinitionRegistry
2422
import org.junit.jupiter.params.provider.Arguments
2523
import java.io.File
2624
import java.util.UUID
@@ -52,17 +50,12 @@ internal fun locateTestFiles(directory: File): Pair<List<File>, Map<String, File
5250
return queries to expectedFiles
5351
}
5452

55-
internal fun testSchema(): TypeDefinitionRegistry {
56-
val schemaFileStream = ClassLoader.getSystemClassLoader().getResourceAsStream("testSchema.graphql") ?: throw RuntimeException("unable to locate test schema")
57-
return schemaFileStream.use {
58-
SchemaParser().parse(schemaFileStream)
59-
}
60-
}
53+
internal const val TEST_SCHEMA_PATH = "testSchema.graphql"
6154

6255
internal fun verifyClientGeneration(config: GraphQLClientGeneratorConfig, testDirectory: File) {
6356
val (queries, expectedFiles) = locateTestFiles(testDirectory)
6457

65-
val generator = GraphQLClientGenerator(testSchema(), config)
58+
val generator = GraphQLClientGenerator(TEST_SCHEMA_PATH, config)
6659
val fileSpecs = generator.generate(queries)
6760
assertTrue(fileSpecs.isNotEmpty())
6861
assertEquals(expectedFiles.size, fileSpecs.size)

plugins/graphql-kotlin-gradle-plugin/src/main/kotlin/com/expediagroup/graphql/plugin/gradle/actions/GenerateClientAction.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,11 @@ abstract class GenerateClientAction : WorkAction<GenerateClientParameters> {
3535
val allowDeprecated = parameters.allowDeprecated.get()
3636
val customScalarMap = parameters.customScalars.get().map { GraphQLScalar(it.scalar, it.type, it.converter) }
3737
val serializer = GraphQLSerializer.valueOf(parameters.serializer.get().name)
38-
val schemaFile = parameters.schemaFile.get()
38+
val schemaPath = parameters.schemaPath.get()
3939
val queryFiles = parameters.queryFiles.get()
4040
val targetDirectory = parameters.targetDirectory.get()
4141

42-
generateClient(targetPackage, allowDeprecated, customScalarMap, serializer, schemaFile, queryFiles).forEach {
42+
generateClient(targetPackage, allowDeprecated, customScalarMap, serializer, schemaPath, queryFiles).forEach {
4343
it.writeTo(targetDirectory)
4444
}
4545
}

plugins/graphql-kotlin-gradle-plugin/src/main/kotlin/com/expediagroup/graphql/plugin/gradle/parameters/GenerateClientParameters.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ interface GenerateClientParameters : WorkParameters {
3636
val customScalars: ListProperty<GraphQLScalar>
3737
/** Type of JSON serializer that will be used to generate the data classes. */
3838
val serializer: Property<GraphQLSerializer>
39-
/** GraphQL schema file that will be used to generate client code. */
40-
val schemaFile: Property<File>
39+
/** GraphQL schema file path that will be used to generate client code. */
40+
val schemaPath: Property<String>
4141
/** List of query files that will be processed to generate HTTP clients. */
4242
val queryFiles: ListProperty<File>
4343
/** Directory where to save the generated source files. */

plugins/graphql-kotlin-gradle-plugin/src/main/kotlin/com/expediagroup/graphql/plugin/gradle/tasks/AbstractGenerateClientTask.kt

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -146,14 +146,11 @@ abstract class AbstractGenerateClientTask : DefaultTask() {
146146
fun generateGraphQLClientAction() {
147147
logger.debug("generating GraphQL client")
148148

149-
val graphQLSchema = when {
150-
schemaFile.isPresent -> schemaFile.get().asFile
151-
schemaFileName.isPresent -> File(schemaFileName.get())
149+
val graphQLSchemaPath = when {
150+
schemaFile.isPresent -> schemaFile.get().asFile.path
151+
schemaFileName.isPresent -> schemaFileName.get()
152152
else -> throw RuntimeException("schema not available")
153153
}
154-
if (!graphQLSchema.isFile) {
155-
throw RuntimeException("specified schema file does not exist")
156-
}
157154

158155
val targetPackage = packageName.orNull ?: throw RuntimeException("package not specified")
159156
val targetQueryFiles: List<File> = when {
@@ -174,7 +171,7 @@ abstract class AbstractGenerateClientTask : DefaultTask() {
174171
throw RuntimeException("failed to generate generated source directory = $targetDirectory")
175172
}
176173

177-
logConfiguration(graphQLSchema, targetQueryFiles)
174+
logConfiguration(graphQLSchemaPath, targetQueryFiles)
178175
val workQueue: WorkQueue = getWorkerExecutor().classLoaderIsolation { workerSpec: ClassLoaderWorkerSpec ->
179176
workerSpec.classpath.from(pluginClasspath)
180177
logger.debug("worker classpath: \n${workerSpec.classpath.files.joinToString("\n")}")
@@ -185,17 +182,17 @@ abstract class AbstractGenerateClientTask : DefaultTask() {
185182
parameters.allowDeprecated.set(allowDeprecatedFields)
186183
parameters.customScalars.set(customScalars)
187184
parameters.serializer.set(serializer)
188-
parameters.schemaFile.set(graphQLSchema)
185+
parameters.schemaPath.set(graphQLSchemaPath)
189186
parameters.queryFiles.set(targetQueryFiles)
190187
parameters.targetDirectory.set(targetDirectory)
191188
}
192189
workQueue.await()
193190
logger.debug("successfully generated GraphQL HTTP client")
194191
}
195192

196-
private fun logConfiguration(schema: File, queryFiles: List<File>) {
193+
private fun logConfiguration(schemaPath: String, queryFiles: List<File>) {
197194
logger.debug("GraphQL Client generator configuration:")
198-
logger.debug(" schema file = ${schema.path}")
195+
logger.debug(" schema file = $schemaPath")
199196
logger.debug(" queries")
200197
queryFiles.forEach {
201198
logger.debug(" - ${it.name}")

plugins/graphql-kotlin-gradle-plugin/src/main/kotlin/com/expediagroup/graphql/plugin/gradle/tasks/GraphQLDownloadSDLTask.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ abstract class GraphQLDownloadSDLTask : DefaultTask() {
8383
}
8484

8585
/**
86-
* Download schema in SDL format from the specified endpoint and sve it locally in the target output file.
86+
* Download schema in SDL format from the specified endpoint and save it locally in the target output file.
8787
*/
8888
@TaskAction
8989
fun downloadSDLAction() {

plugins/graphql-kotlin-maven-plugin/src/main/kotlin/com/expediagroup/graphql/plugin/maven/GenerateClientAbstractMojo.kt

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@
1717
package com.expediagroup.graphql.plugin.maven
1818

1919
import com.expediagroup.graphql.plugin.client.generateClient
20-
import com.expediagroup.graphql.plugin.client.generator.GraphQLSerializer
2120
import com.expediagroup.graphql.plugin.client.generator.GraphQLScalar
21+
import com.expediagroup.graphql.plugin.client.generator.GraphQLSerializer
2222
import org.apache.maven.plugin.AbstractMojo
2323
import org.apache.maven.plugins.annotations.Parameter
2424
import org.apache.maven.project.MavenProject
@@ -39,7 +39,7 @@ abstract class GenerateClientAbstractMojo : AbstractMojo() {
3939
* GraphQL schema file that will be used to generate client code.
4040
*/
4141
@Parameter(defaultValue = "\${graphql.schemaFile}", name = "schemaFile")
42-
private var schemaFile: File? = null
42+
private var schemaFile: String? = null
4343

4444
/**
4545
* Target package name for generated code.
@@ -99,17 +99,18 @@ abstract class GenerateClientAbstractMojo : AbstractMojo() {
9999

100100
override fun execute() {
101101
log.debug("generating GraphQL client")
102-
val graphQLSchemaFile = schemaFile ?: File(project.build.directory, "schema.graphql")
103-
validateGraphQLSchemaExists(graphQLSchemaFile)
102+
103+
val schemaPath = schemaFile ?: File(project.build.directory, "schema.graphql").path
104+
104105
val targetQueryFiles: List<File> = locateQueryFiles(queryFiles, queryFileDirectory)
105106

106107
if (!outputDirectory.isDirectory && !outputDirectory.mkdirs()) {
107108
throw RuntimeException("failed to generate generated source directory")
108109
}
109110

110-
logConfiguration(graphQLSchemaFile, targetQueryFiles)
111+
logConfiguration(schemaPath, targetQueryFiles)
111112
val customGraphQLScalars = customScalars.map { GraphQLScalar(it.scalar, it.type, it.converter) }
112-
generateClient(packageName, allowDeprecatedFields, customGraphQLScalars, serializer, graphQLSchemaFile, targetQueryFiles).forEach {
113+
generateClient(packageName, allowDeprecatedFields, customGraphQLScalars, serializer, schemaPath, targetQueryFiles).forEach {
113114
it.writeTo(outputDirectory)
114115
}
115116

@@ -125,17 +126,11 @@ abstract class GenerateClientAbstractMojo : AbstractMojo() {
125126
return targetQueryFiles
126127
}
127128

128-
private fun validateGraphQLSchemaExists(graphQLSchemaFile: File) {
129-
if (!graphQLSchemaFile.isFile) {
130-
throw RuntimeException("specified GraphQL schema is not a file, ${graphQLSchemaFile.path}")
131-
}
132-
}
133-
134129
abstract fun configureProjectWithGeneratedSources(mavenProject: MavenProject, generatedSourcesDirectory: File)
135130

136-
private fun logConfiguration(graphQLSchemaFile: File, queryFiles: List<File>) {
131+
private fun logConfiguration(graphQLSchemaFilePath: String, queryFiles: List<File>) {
137132
log.debug("GraphQL Client generator configuration:")
138-
log.debug(" schema file = ${graphQLSchemaFile.path}")
133+
log.debug(" schema file = $graphQLSchemaFilePath")
139134
log.debug(" queries")
140135
queryFiles.forEach {
141136
log.debug(" - ${it.name}")

0 commit comments

Comments
 (0)