Skip to content
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

Add ApolloCompilerPluginProvider #5865

Merged
merged 7 commits into from
Apr 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions docs/source/migration/4.0.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -330,9 +330,46 @@ If you are using `packageNamesFromFilePaths` and `schemaFile`, you'll need to us
```

<Note>

Apollo Kotlin 3 was using the operation root directories to compute the schema normalized path which could be wrong in some edge cases. Using fileTree ensures the normalized path is consistent.
</Note>

### Migrating to ApolloCompilerPlugin

4.0 introduces `ApolloCompilerPlugin` as a way to customize code generation. `ApolloCompilerPlugin` are loaded using the [ServiceLoader](https://docs.oracle.com/javase/8/docs/api/java/util/ServiceLoader.html) API and run in a separate classloader from your Gradle build. As a result, using `Service.operationIdGenerator`/`Service.operationOutputGenerator` together with `ApolloCompilerPlugin` is not possible.

`Service.operationIdGenerator`/`Service.operationOutputGenerator` are deprecated and will be removed in a future release. You can migrate to `ApolloCompilerPlugin` using the instructions [from the dedicated page](https://www.apollographql.com/docs/kotlin/v4/advanced/compiler-plugins) and the `ApolloCompilerPlugin.operationIds()` method:

```kotlin
// Replace (in your build scripts)
val operationOutputGenerator = object: OperationOutputGenerator {
override fun generate(operationDescriptorList: Collection<OperationDescriptor>): OperationOutput {
return operationDescriptorList.associateBy {
it.source.sha1()
}
}
override val version: String = "v1"
}

// Or, if using OperationIdGenerator, replace
val operationIdGenerator = object: OperationIdGenerator {
override fun apply(operationDocument: String, operationName: String): String {
return operationDocument.sha1()
}

override val version: String = "v1"
}

// With (in your build compiler plugin module)
class MyPlugin: ApolloCompilerPlugin {
override fun operationIds(descriptors: List<OperationDescriptor>): List<OperationId>? {
return descriptors.map {
OperationId(it.source.sha1(), it.name)
}
}
}
```

### Misc

* Publishing is no longer configured automatically.
Expand Down Expand Up @@ -526,6 +563,7 @@ apolloClient.query().rxSingle()
apolloClient.query().toFlow().asFlowable().firstOrError()
```


## Example of a migration

If you are looking for inspiration, we updated the [3.x integration tests to use 4.0](https://github.com/apollographql/apollo-kotlin/pull/5418/). If you have an open source project that migrated, feel free to share it and we'll include it here.
Expand Down
3 changes: 3 additions & 0 deletions libraries/apollo-compiler/api/apollo-compiler.api
Original file line number Diff line number Diff line change
Expand Up @@ -653,6 +653,9 @@ public final class com/apollographql/apollo3/compiler/codegen/kotlin/helpers/Add
public static final fun addInternal (Lcom/squareup/kotlinpoet/FileSpec$Builder;Ljava/util/List;)Lcom/squareup/kotlinpoet/FileSpec$Builder;
}

public final class com/apollographql/apollo3/compiler/internal/GradleCompilerPluginLogger$Companion {
}

public final class com/apollographql/apollo3/compiler/ir/IrAccessor$Companion {
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.apollographql.apollo3.compiler

import com.apollographql.apollo3.annotations.ApolloExperimental

/**
* [ApolloCompilerPluginEnvironment] contains the environment where the Apollo compiler is run.
*/
@ApolloExperimental
class ApolloCompilerPluginEnvironment(
/**
* Arguments as passed from the Gradle plugin
*/
val arguments: Map<String, Any?>,
/**
* A logger that can be used by the plugin.
*/
val logger: ApolloCompilerPluginLogger,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.apollographql.apollo3.compiler

import com.apollographql.apollo3.annotations.ApolloExperimental

/**
* [ApolloCompilerPluginLogger] allows logging from the context of the Apollo compiler.
*
* Typically, the Apollo compiler is run from an isolated classloader and cannot use the Gradle logging functions but can respect the logging level set by the user.
*/
@ApolloExperimental
interface ApolloCompilerPluginLogger {
fun logging(message: String)
fun info(message: String)
fun warn(message: String)
fun error(message: String)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.apollographql.apollo3.compiler

import com.apollographql.apollo3.annotations.ApolloExperimental

/**
* [ApolloCompilerPluginProvider] is entry point for creating [ApolloCompilerPlugin].
*
* [ApolloCompilerPluginProvider] is created by [java.util.ServiceLoader], make sure to include a matching `META-INF/services` resource.
*/
@ApolloExperimental
fun interface ApolloCompilerPluginProvider {
/**
* Creates the [ApolloCompilerPlugin]
*/
fun create(environment: ApolloCompilerPluginEnvironment): ApolloCompilerPlugin
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.apollographql.apollo3.compiler.internal

import com.apollographql.apollo3.annotations.ApolloInternal
import com.apollographql.apollo3.compiler.ApolloCompilerPluginLogger

@ApolloInternal
class GradleCompilerPluginLogger(val loglevel: Int) : ApolloCompilerPluginLogger {

override fun logging(message: String) {
if (loglevel <= LOGGING_LEVEL_LOGGING)
println("v: [apollo] $message")
}

override fun info(message: String) {
if (loglevel <= LOGGING_LEVEL_INFO)
println("i: [apollo] $message")
}

override fun warn(message: String) {
if (loglevel <= LOGGING_LEVEL_WARN)
println("w: [apollo] $message")
}

override fun error(message: String) {
if (loglevel <= LOGGING_LEVEL_ERROR)
println("e: [apollo] $message")
}

companion object {
/**
* Matches Gradle LogLevel
* See https://github.com/gradle/gradle/blob/71f42531a742bc263c61f1d0dc21bb6570cc817b/platforms/core-runtime/logging-api/src/main/java/org/gradle/api/logging/LogLevel.java#L21
*/
const val LOGGING_LEVEL_LOGGING = 0
BoD marked this conversation as resolved.
Show resolved Hide resolved
const val LOGGING_LEVEL_INFO = 1
const val LOGGING_LEVEL_WARN = 3
const val LOGGING_LEVEL_ERROR = 5
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class OperationDescriptor(
*/
val name: String,
/**
* The source of the operation as it is sent over the wire, including fragments
* The source of the operation document as it is sent over the wire, including fragments
*/
val source: String,
/**
Expand All @@ -30,6 +30,14 @@ class OperationDescriptor(

/**
* The id of an operation associated with its name so that it can be looked up.
*
* @param id the generated id for the operation
* @param name the name of the operation, such as "FooQuery" below
* ```graphql
* query FooQuery {
* foo
* }
* ```
*/
class OperationId(val id: String, val name: String)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.apollographql.apollo3.gradle.api

import com.apollographql.apollo3.annotations.ApolloExperimental

@ApolloExperimental
interface CompilerPlugin {
/**
* Adds the given argument to the [com.apollographql.apollo3.compiler.ApolloCompilerPlugin].
* If two arguments are added with the same name, the second one overwrites the first one.
*
* @param name the name of the argument
* @param value the value of the argument. One of:
* - [String]
* - [Int]
* - [Double]
* - [Boolean]
* - [List]
* - [Map]
*/
fun argument(name: String, value: Any)
}
Original file line number Diff line number Diff line change
Expand Up @@ -907,4 +907,7 @@ interface Service {
}

fun plugin(dependencyNotation: Any)

@ApolloExperimental
fun plugin(dependencyNotation: Any, block: Action<CompilerPlugin>)
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,8 @@ import com.apollographql.apollo3.compiler.toIrOperations
import com.apollographql.apollo3.compiler.toIrOptions
import com.apollographql.apollo3.compiler.writeTo
import com.apollographql.apollo3.gradle.internal.ApolloGenerateSourcesFromIrTask.Companion.findCodegenSchemaFile
import org.gradle.api.DefaultTask
import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.tasks.Classpath
import org.gradle.api.tasks.InputFile
import org.gradle.api.tasks.InputFiles
import org.gradle.api.tasks.OutputFile
Expand All @@ -22,7 +20,7 @@ import org.gradle.workers.WorkerExecutor
import java.io.File
import javax.inject.Inject

abstract class ApolloGenerateIrOperationsTask: DefaultTask() {
abstract class ApolloGenerateIrOperationsTask: ApolloTaskWithClasspath() {
@get:InputFiles
@get:PathSensitive(PathSensitivity.RELATIVE)
abstract val codegenSchemaFiles: ConfigurableFileCollection
Expand All @@ -42,9 +40,6 @@ abstract class ApolloGenerateIrOperationsTask: DefaultTask() {
@get:OutputFile
abstract val irOperationsFile: RegularFileProperty

@get:Classpath
abstract val classpath: ConfigurableFileCollection

@Inject
abstract fun getWorkerExecutor(): WorkerExecutor

Expand All @@ -61,6 +56,8 @@ abstract class ApolloGenerateIrOperationsTask: DefaultTask() {
it.upstreamIrFiles = upstreamIrFiles.isolate()
it.irOptionsFile.set(irOptionsFile)
it.irOperationsFile.set(irOperationsFile)
it.arguments = arguments.get()
it.logLevel = logLevel.get().ordinal
}
}
}
Expand All @@ -69,7 +66,7 @@ private abstract class GenerateIrOperations : WorkAction<GenerateIrOperationsPar
override fun execute() {
with(parameters) {
val upstreamIrOperations = upstreamIrFiles.toInputFiles().map { it.file.toIrOperations() }
val plugin = apolloCompilerPlugin()
val plugin = apolloCompilerPlugin(arguments, logLevel)

ApolloCompiler.buildIrOperations(
executableFiles = graphqlFiles.toInputFiles(),
Expand All @@ -89,4 +86,6 @@ private interface GenerateIrOperationsParameters : WorkParameters {
var upstreamIrFiles: List<Pair<String, File>>
val irOptionsFile: RegularFileProperty
val irOperationsFile: RegularFileProperty
var arguments: Map<String, Any?>
var logLevel: Int
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,8 @@ import com.apollographql.apollo3.compiler.OperationOutputGenerator
import com.apollographql.apollo3.compiler.PackageNameGenerator
import com.apollographql.apollo3.compiler.codegen.SchemaAndOperationsLayout
import com.apollographql.apollo3.compiler.toCodegenOptions
import org.gradle.api.DefaultTask
import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.provider.Property
import org.gradle.api.tasks.Classpath
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.InputFile
import org.gradle.api.tasks.Internal
Expand All @@ -26,7 +22,7 @@ import org.gradle.api.tasks.PathSensitivity
import org.gradle.workers.WorkerExecutor
import javax.inject.Inject

abstract class ApolloGenerateSourcesBaseTask : DefaultTask() {
abstract class ApolloGenerateSourcesBaseTask : ApolloTaskWithClasspath() {
@get:InputFile
@get:PathSensitive(PathSensitivity.RELATIVE)
abstract val codegenOptionsFile: RegularFileProperty
Expand All @@ -50,12 +46,6 @@ abstract class ApolloGenerateSourcesBaseTask : DefaultTask() {
@get:OutputDirectory
abstract val outputDir: DirectoryProperty

@get:Classpath
abstract val classpath: ConfigurableFileCollection

@get:Input
abstract val hasPlugin: Property<Boolean>

@Inject
abstract fun getWorkerExecutor(): WorkerExecutor
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ abstract class ApolloGenerateSourcesFromIrTask : ApolloGenerateSourcesBaseTask()
it.outputDir.set(outputDir)
it.metadataOutputFile.set(metadataOutputFile)
it.hasPlugin = hasPlugin.get()
it.arguments = arguments.get()
it.logLevel = logLevel.get().ordinal
}
}
}
Expand All @@ -98,7 +100,11 @@ private abstract class GenerateSourcesFromIr : WorkAction<GenerateSourcesFromIrP
val codegenSchemaFile = codegenSchemas.findCodegenSchemaFile()

val codegenSchema = codegenSchemaFile.toCodegenSchema()
val plugin = apolloCompilerPlugin(hasPlugin)
val plugin = apolloCompilerPlugin(
arguments,
logLevel,
hasPlugin
)

ApolloCompiler.buildSchemaAndOperationsSourcesFromIr(
codegenSchema = codegenSchema,
Expand Down Expand Up @@ -127,5 +133,7 @@ private interface GenerateSourcesFromIrParameters : WorkParameters {
val operationManifestFile: RegularFileProperty
val outputDir: DirectoryProperty
val metadataOutputFile: RegularFileProperty
var arguments: Map<String, Any?>
var logLevel: Int
}

Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ abstract class ApolloGenerateSourcesTask : ApolloGenerateSourcesBaseTask() {
it.codegenOptions.set(codegenOptionsFile)
it.operationManifestFile.set(operationManifestFile)
it.outputDir.set(outputDir)
it.arguments = arguments.get()
it.logLevel = logLevel.get().ordinal
}
}
}
Expand All @@ -88,7 +90,11 @@ private abstract class GenerateSources : WorkAction<GenerateSourcesParameters> {
with(parameters) {
val schemaInputFiles = (schemaFiles.takeIf { it.isNotEmpty() } ?: fallbackSchemaFiles).toInputFiles()
val executableInputFiles = graphqlFiles.toInputFiles()
val plugin = apolloCompilerPlugin(hasPlugin)
val plugin = apolloCompilerPlugin(
arguments,
logLevel,
hasPlugin
)

ApolloCompiler.buildSchemaAndOperationsSources(
schemaFiles = schemaInputFiles,
Expand Down Expand Up @@ -123,4 +129,6 @@ private interface GenerateSourcesParameters : WorkParameters {
val irOptions: RegularFileProperty
val operationManifestFile: RegularFileProperty
val outputDir: DirectoryProperty
var arguments: Map<String, Any?>
var logLevel: Int
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.apollographql.apollo3.gradle.internal

import org.gradle.api.DefaultTask
import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.file.FileCollection
import org.gradle.api.logging.LogLevel
import org.gradle.api.provider.MapProperty
import org.gradle.api.provider.Property
import org.gradle.api.tasks.Classpath
import org.gradle.api.tasks.Input

abstract class ApolloTaskWithClasspath: DefaultTask() {
@get:Classpath
abstract val classpath: ConfigurableFileCollection

@get:Input
abstract val hasPlugin: Property<Boolean>

@get:Input
abstract val arguments: MapProperty<String, Any?>

@get:Input
abstract val logLevel: Property<LogLevel>

class Options(
val classpath: FileCollection,
val hasPlugin: Boolean,
val arguments: Map<String, Any?>,
val logLevel: LogLevel
)
}
Loading
Loading