Skip to content

Client Generation - Allow schema to be on the classpath #1136

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged

Conversation

AlexRiedler
Copy link
Contributor

@AlexRiedler AlexRiedler commented Apr 28, 2021

📝 Description

Simply allow loading the schema file from your classpath, this allows you to include a maven dependency that stores your graphql SDLs as a package.

<plugin>
	<groupId>com.expediagroup</groupId>
	<artifactId>graphql-kotlin-maven-plugin</artifactId>
	<version>4.0.0-SNAPSHOT</version>
	<dependencies>
		<dependency>
			<groupId>com.expedia.schemas</groupId>
			<artifactId>schemas</artifactId>
		</dependency>
	</dependencies>
	<executions>
		<execution>
			<goals>
				<goal>generate-client</goal>
			</goals>
			<configuration>
				<packageName>com.expedia.graphql.generated.foobarservice</packageName>
				<schemaFile>expedia-schemas/foobar-service/schema.graphql</schemaFile>
				<queryFileDirectory>${project.basedir}/src/main/resources/queries</queryFileDirectory>
			</configuration>
		</execution>
	</executions>
</plugin>

I would love to understand how to test this for Gradle as well since it changed, I have tested for maven only so far!

Feedback appreciated :)

🔗 Related Issues

Copy link
Collaborator

@dariuszkuc dariuszkuc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for raising this.

@@ -32,7 +37,7 @@ fun generateClient(
allowDeprecated: Boolean = false,
customScalarsMap: List<GraphQLScalar> = emptyList(),
serializer: GraphQLSerializer = GraphQLSerializer.JACKSON,
schema: File,
schema: String, // this is the file location, not the contents
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets rename this field to schemaPath: String and drop the comment


private fun getSchemaReader(schemaPath: String): Reader =
if (schemaPath.startsWith("classpath://")) {
val resource = object {}.javaClass.getResource(URI(schemaPath).path) ?: throw RuntimeException("specified GraphQL schema not found, $schemaPath")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think anonymous objects like this might be throwing some detekt violations -> instead lets move the logic of reading from classpath to the GraphQLClientGenerator (i.e. change its constructor from accepting type registry to a path and initialize type registry in init block)

private val graphQLSchema: TypeDefinitionRegistry
init {
    graphQLSchema = parseSchema(schemaPath)
}

private fun parseSchema(path: String): TypeDefinitionRegistry {
    val schemaFile = File(path)
    return if (schemaFile.isFile) {
        SchemaParser().parse(schemaFile)
    } else {
        val schemaInputStream = this.javaClass.classLoader.getResourceAsStream(path) ?: throw RuntimeException("specified schema file=$path does not exist")
       SchemaParser().parse(schemaInputStream)
    }
}

we'll need some unit tests around this

val generator = GraphQLClientGenerator(graphQLSchema, config)
return generator.generate(queries)
}

private fun getSchemaReader(schemaPath: String): Reader =
if (schemaPath.startsWith("classpath://")) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

classpath:// is not a valid URL protocol so we should not code against this (yes Spring supports it but it is non-standard)

val graphQLSchemaFile = schemaFile ?: File(project.build.directory, "schema.graphql")
validateGraphQLSchemaExists(graphQLSchemaFile)

val schemaPath =
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can rewrite this as

Suggested change
val schemaPath =
val schemaPath = schemaFile ?: File(project.build.directory, "schema.graphql").path

which avoids the !!

@@ -66,7 +66,7 @@ abstract class AbstractGenerateClientTask : DefaultTask() {
*/
@InputFile
@Optional
val schemaFile: RegularFileProperty = project.objects.fileProperty()
val schemaFile: Property<String> = project.objects.property(String::class.java)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there already is schemaFileName property for that use case, this should remain a RegularFileProperty

@@ -147,13 +147,10 @@ abstract class AbstractGenerateClientTask : DefaultTask() {
logger.debug("generating GraphQL client")

val graphQLSchema = when {
schemaFile.isPresent -> schemaFile.get().asFile
schemaFileName.isPresent -> File(schemaFileName.get())
schemaFile.isPresent -> schemaFile.get() as String
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets rename the enclosing variable graphQLSchema -> graphQLSchemaPath

@@ -37,7 +37,7 @@ interface GenerateClientParameters : WorkParameters {
/** Type of JSON serializer that will be used to generate the data classes. */
val serializer: Property<GraphQLSerializer>
/** GraphQL schema file that will be used to generate client code. */
val schemaFile: Property<File>
val schemaFile: Property<String>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets also update javadoc to reflect this is a path to a schema file

Suggested change
val schemaFile: Property<String>
val schemaPath: Property<String>

Copy link
Collaborator

@dariuszkuc dariuszkuc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we also add a unit test for GraphQLClientGenerator to verify it will blow up with the exception when invalid path is specified?

@@ -32,7 +31,7 @@ fun generateClient(
allowDeprecated: Boolean = false,
customScalarsMap: List<GraphQLScalar> = emptyList(),
serializer: GraphQLSerializer = GraphQLSerializer.JACKSON,
schema: File,
schemaPath: String, // this is the file location, not the contents
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets remove the comment as it is unnecessary

Suggested change
schemaPath: String, // this is the file location, not the contents
schemaPath: String,

SchemaParser().parse(schemaFileStream)
}
}
internal fun testSchema(): String = "testSchema.graphql"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since this is a constant string lets change it to

Suggested change
internal fun testSchema(): String = "testSchema.graphql"
internal const val TEST_SCHEMA = "testSchema.graphql"

@@ -104,15 +104,15 @@ class GraphQLGradlePlugin : Plugin<Project> {
introspectSchemaTask.headers.convention(project.provider { extension.clientExtension.headers })
introspectSchemaTask.timeoutConfig.convention(project.provider { extension.clientExtension.timeoutConfig })
generateClientTask.dependsOn(introspectSchemaTask.path)
generateClientTask.schemaFile.convention(introspectSchemaTask.outputFile)
generateClientTask.schemaFile.convention(introspectSchemaTask.outputFile.get())
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets revert changes in this file as we should rely on the provider

return if (schemaFile.isFile) {
SchemaParser().parse(schemaFile)
} else {
val schemaInputStream = this.javaClass.classLoader.getResourceAsStream(path) ?: throw RuntimeException("specified schema file=$path does not exist")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets extract this exception to explicit SchemaUnavailableException (or something like that) under com.expediagroup.graphql.plugin.client.generator.exceptions

@AlexRiedler
Copy link
Contributor Author

AlexRiedler commented Apr 30, 2021

I put the test next to the parameterized IT test, hopefully that makes sense. Thanks for being so responsive, it has been a pleasure contributing this! learned a few things as well along the way.

One thing is the user no longer needs to specify classpath:// ; not sure if we should require that on the 2nd path; what do you think?

@dariuszkuc dariuszkuc merged commit edad0b4 into ExpediaGroup:master Apr 30, 2021
@dariuszkuc
Copy link
Collaborator

One thing is the user no longer needs to specify classpath:// ; not sure if we should require that on the 2nd path; what do you think?

Nah, current logic of just checking if it is a file and/or resource on classpath should be fine. We could technically go fancy with support for URLs (so you could point to a http resource) but that might be an overkill.

@dariuszkuc dariuszkuc added changes: patch Changes require a patch version module: plugin Issue affects the plugins code labels Apr 30, 2021
dariuszkuc pushed a commit to dariuszkuc/graphql-kotlin that referenced this pull request Aug 5, 2022
…#1136)

* Client Generation - Allow schema SDL to be on the classpath

* Suggestions and Refactors

* Minor touchup

* More improvements to the code, remove redundant edits
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
changes: patch Changes require a patch version module: plugin Issue affects the plugins code
Development

Successfully merging this pull request may close these issues.

2 participants