Skip to content

Update Apollo compiler plugin to 4.3 API #169

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
merged 10 commits into from
Jun 6, 2025
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
2 changes: 2 additions & 0 deletions Writerside/topics/welcome.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ are added here instead. In the future, the main repository Normalized Cache will

{style="warning"}

The Normalized Cache requires Apollo Kotlin v4.3.0 or later.

1. Add the dependencies to your project

```kotlin
Expand Down
3 changes: 2 additions & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[versions]
kotlin-plugin = "2.1.21"
android-plugin = "8.7.0"
apollo = "4.2.0"
apollo = "4.3.0"
okio = "3.9.0"
atomicfu = "0.23.1" # Must be the same version as the one used by apollo-testing-support or native compilation will fail
sqldelight = "2.1.0"
Expand Down Expand Up @@ -49,4 +49,5 @@ sqldelight-plugin = { module = "app.cash.sqldelight:gradle-plugin", version.ref

[plugins]
kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin-plugin" }
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin-plugin" }
apollo = { id = "com.apollographql.apollo", version.ref = "apollo" }
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
public final class com/apollographql/cache/apollocompilerplugin/ApolloCacheCompilerPlugin : com/apollographql/apollo/compiler/ApolloCompilerPlugin {
public fun <init> ()V
public fun beforeCompilationStep (Lcom/apollographql/apollo/compiler/ApolloCompilerPluginEnvironment;Lcom/apollographql/apollo/compiler/ApolloCompilerRegistry;)V
}

public final class com/apollographql/cache/apollocompilerplugin/ApolloCacheCompilerPluginProvider : com/apollographql/apollo/compiler/ApolloCompilerPluginProvider {
public fun <init> ()V
public fun create (Lcom/apollographql/apollo/compiler/ApolloCompilerPluginEnvironment;)Lcom/apollographql/apollo/compiler/ApolloCompilerPlugin;
Expand Down
1 change: 1 addition & 0 deletions normalized-cache-apollo-compiler-plugin/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Librarian.module(project)
dependencies {
compileOnly(libs.apollo.compiler)
testImplementation(libs.apollo.compiler)
testImplementation(gradleTestKit())
implementation(libs.apollo.ast)
implementation(libs.kotlin.poet)
testImplementation(libs.kotlin.test)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
@file:OptIn(ApolloExperimental::class)

package com.apollographql.cache.apollocompilerplugin

import com.apollographql.apollo.annotations.ApolloExperimental
import com.apollographql.apollo.ast.ForeignSchema
import com.apollographql.apollo.compiler.ApolloCompilerPlugin
import com.apollographql.apollo.compiler.ApolloCompilerPluginEnvironment
import com.apollographql.apollo.compiler.ApolloCompilerRegistry
import com.apollographql.cache.apollocompilerplugin.internal.AddKeyFieldsExecutableDocumentTransform
import com.apollographql.cache.apollocompilerplugin.internal.CacheSchemaCodeGenerator
import com.apollographql.cache.apollocompilerplugin.internal.cacheGQLDefinitions

class ApolloCacheCompilerPlugin : ApolloCompilerPlugin {
override fun beforeCompilationStep(
environment: ApolloCompilerPluginEnvironment,
registry: ApolloCompilerRegistry,
) {
registry.registerForeignSchemas(listOf(ForeignSchema("cache", "v0.1", cacheGQLDefinitions)))
registry.registerExecutableDocumentTransform("com.apollographql.cache.addKeyFields", transform = AddKeyFieldsExecutableDocumentTransform)
registry.registerSchemaCodeGenerator(CacheSchemaCodeGenerator(environment))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,25 @@
package com.apollographql.cache.apollocompilerplugin

import com.apollographql.apollo.annotations.ApolloExperimental
import com.apollographql.apollo.compiler.APOLLO_VERSION
import com.apollographql.apollo.compiler.ApolloCompilerPlugin
import com.apollographql.apollo.compiler.ApolloCompilerPluginEnvironment
import com.apollographql.apollo.compiler.ApolloCompilerPluginProvider
import com.apollographql.cache.apollocompilerplugin.internal.ApolloCacheCompilerPlugin

class ApolloCacheCompilerPluginProvider : ApolloCompilerPluginProvider {
// ApolloCacheCompilerPluginProvider is deprecated in favor of ApolloCompilerPlugin, but we want to display a nice error message
// in projects using AK < 4.3.0
class ApolloCacheCompilerPluginProvider : @Suppress("DEPRECATION") com.apollographql.apollo.compiler.ApolloCompilerPluginProvider {
override fun create(environment: ApolloCompilerPluginEnvironment): ApolloCompilerPlugin {
return ApolloCacheCompilerPlugin(environment)
checkCompilerVersion()
return ApolloCacheCompilerPlugin()
}
}

private fun checkCompilerVersion() {
val matchResult = Regex("""^(\d+)\.(\d+).*$""").matchEntire(APOLLO_VERSION)
val versionMajor = matchResult?.groupValues?.get(1)?.toIntOrNull()
val versionMinor = matchResult?.groupValues?.get(2)?.toIntOrNull()
if (versionMinor == null || versionMajor == null) error("Invalid Apollo Kotlin compiler version: $APOLLO_VERSION")
if (versionMajor < 4 || (versionMajor == 4 && versionMinor < 3)) {
error("The Apollo Cache compiler plugin requires Apollo Kotlin version 4.3.0 or higher (found $APOLLO_VERSION)")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package com.apollographql.cache.apollocompilerplugin.internal

import com.apollographql.apollo.annotations.ApolloExperimental
import com.apollographql.apollo.annotations.ApolloInternal
import com.apollographql.apollo.ast.GQLDocument
import com.apollographql.apollo.ast.GQLField
import com.apollographql.apollo.ast.GQLFragmentDefinition
import com.apollographql.apollo.ast.GQLFragmentSpread
Expand All @@ -19,18 +20,26 @@ import com.apollographql.apollo.ast.definitionFromScope
import com.apollographql.apollo.ast.rawType
import com.apollographql.apollo.ast.responseName
import com.apollographql.apollo.ast.rootTypeDefinition
import com.apollographql.apollo.compiler.DocumentTransform
import com.apollographql.apollo.compiler.ExecutableDocumentTransform

/**
* Add key fields and `__typename` to selections on types that declare them via `@typePolicy`.
* Add `__typename` to every composite selection set and key fields to selection sets on types where `@typePolicy` declare them.
*/
internal class AddKeyFieldsDocumentTransform : DocumentTransform {
override fun transform(schema: Schema, fragment: GQLFragmentDefinition): GQLFragmentDefinition {
return fragment.withRequiredFields(schema)
}

override fun transform(schema: Schema, operation: GQLOperationDefinition): GQLOperationDefinition {
return operation.withRequiredFields(schema)
internal object AddKeyFieldsExecutableDocumentTransform : ExecutableDocumentTransform {
override fun transform(
schema: Schema,
document: GQLDocument,
extraFragmentDefinitions: List<GQLFragmentDefinition>,
): GQLDocument {
return document.copy(
definitions = document.definitions.map {
when (it) {
is GQLFragmentDefinition -> it.withRequiredFields(schema)
is GQLOperationDefinition -> it.withRequiredFields(schema)
else -> it
}
}
)
}

private fun GQLOperationDefinition.withRequiredFields(schema: Schema): GQLOperationDefinition {
Expand Down Expand Up @@ -152,7 +161,6 @@ internal class AddKeyFieldsDocumentTransform : DocumentTransform {
return copy(selections = newSelectionSet)
}

@OptIn(ApolloExperimental::class)
private fun buildField(name: String): GQLField {
return GQLField(
name = name,
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
package com.apollographql.cache.apollocompilerplugin.internal

import com.apollographql.apollo.annotations.ApolloExperimental
import com.apollographql.apollo.ast.GQLDocument
import com.apollographql.apollo.ast.Schema
import com.apollographql.apollo.ast.toSchema
import com.apollographql.apollo.compiler.ApolloCompilerPluginEnvironment
import com.apollographql.apollo.compiler.SchemaListener
import com.apollographql.apollo.compiler.SchemaCodeGenerator
import com.apollographql.cache.apollocompilerplugin.VERSION
import com.squareup.kotlinpoet.ClassName
import com.squareup.kotlinpoet.CodeBlock
Expand All @@ -29,17 +31,18 @@ private object Symbols {
val TypePolicy = ClassName("com.apollographql.cache.normalized.api", "TypePolicy")
}

internal class CacheSchemaListener(
internal class CacheSchemaCodeGenerator(
private val environment: ApolloCompilerPluginEnvironment,
) : SchemaListener {
override fun onSchema(schema: Schema, outputDirectory: File) {
) : SchemaCodeGenerator {
override fun generate(schema: GQLDocument, outputDirectory: File) {
val validSchema = schema.toSchema()
val packageName = (environment.arguments["packageName"] as? String
?: throw IllegalArgumentException("packageName argument is required and must be a String")) + ".cache"
val file = FileSpec.builder(packageName, "Cache")
.addType(
TypeSpec.objectBuilder("Cache")
.addProperty(maxAgeProperty(schema))
.addProperty(typePoliciesProperty(schema))
.addProperty(maxAgeProperty(validSchema))
.addProperty(typePoliciesProperty(validSchema))
.build()
)
.addFileComment(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
package com.apollographql.cache.apollocompilerplugin.internal

import com.apollographql.apollo.annotations.ApolloExperimental
import com.apollographql.apollo.ast.GQLOperationDefinition
import com.apollographql.apollo.ast.SourceAwareException
import com.apollographql.apollo.ast.internal.SchemaValidationOptions
import com.apollographql.apollo.ast.parseAsGQLDocument
Expand All @@ -14,7 +13,7 @@ import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith

class AddKeyFieldsDocumentTransformTest {
class AddKeyFieldsExecutableDocumentTransformTest {
@Test
fun keyFieldsOnObject() {
// language=GraphQL
Expand Down Expand Up @@ -326,8 +325,8 @@ private fun checkTransform(schemaText: String, operationText: String, expected:
foreignSchemas = emptyList(),
)
).getOrThrow()
val operation = operationText.toGQLDocument().definitions.first() as GQLOperationDefinition
assertEquals(expected, AddKeyFieldsDocumentTransform().transform(schema, operation).toUtf8().trim())
val document = operationText.toGQLDocument()
assertEquals(expected, AddKeyFieldsExecutableDocumentTransform.transform(schema, document, emptyList()).toUtf8().trim())
}

private fun checkTransformThrows(schemaText: String, operationText: String, expectedMessage: String) {
Expand All @@ -339,9 +338,9 @@ private fun checkTransformThrows(schemaText: String, operationText: String, expe
foreignSchemas = emptyList(),
)
).getOrThrow()
val operation = operationText.toGQLDocument().definitions.first() as GQLOperationDefinition
val document = operationText.toGQLDocument()
assertFailsWith<SourceAwareException> {
AddKeyFieldsDocumentTransform().transform(schema, operation)
AddKeyFieldsExecutableDocumentTransform.transform(schema, document, emptyList())
}.apply {
assertEquals(expectedMessage, message)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.apollographql.cache.apollocompilerplugin.internal

import org.gradle.testkit.runner.GradleRunner
import org.gradle.testkit.runner.UnexpectedBuildFailure
import java.io.File
import kotlin.test.Test
import kotlin.test.assertTrue

class ApolloVersionTest {

@Test
fun failsBelowApollo4_3() {
val workingDir = File("build/testProject")

workingDir.deleteRecursively()
File("test-data/testProject").copyRecursively(workingDir)
try {
GradleRunner.create()
.withProjectDir(workingDir)
.withDebug(true)
.withArguments("generateServiceApolloSources")
.build()
} catch (e: UnexpectedBuildFailure) {
assertTrue(e.message!!.contains("The Apollo Cache compiler plugin requires Apollo Kotlin version 4.3.0 or higher (found 4.2.0)"))
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
A project that uses Apollo Kotlin 4.2.0 to double check we are failing with a "good" error message.
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
plugins {
alias(libs.plugins.kotlin.jvm)
id("com.apollographql.apollo").version("4.2.0")
}

apollo {
service("service") {
packageName.set("com.example")
plugin("com.apollographql.cache:normalized-cache-apollo-compiler-plugin") {
argument("packageName", packageName.get())
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
apply(from = "gradle/repositories.gradle.kts")

includeBuild("../../..")
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
query GetProduct {
product {
price
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
type Query {
product: Product
foo: Int
}

type Product @typePolicy(keyFields: "id"){
id: ID!
price: Float
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
val hello = "world"
2 changes: 0 additions & 2 deletions samples/pagination/manual/settings.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
pluginManagement {
repositories {
mavenLocal()
maven {
url = uri("https://s01.oss.sonatype.org/content/repositories/snapshots/")
}
Expand All @@ -11,7 +10,6 @@ pluginManagement {
}
dependencyResolutionManagement {
repositories {
mavenLocal()
maven {
url = uri("https://s01.oss.sonatype.org/content/repositories/snapshots/")
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
pluginManagement {
repositories {
mavenLocal()
maven {
url = uri("https://s01.oss.sonatype.org/content/repositories/snapshots/")
}
Expand All @@ -11,7 +10,6 @@ pluginManagement {
}
dependencyResolutionManagement {
repositories {
mavenLocal()
maven {
url = uri("https://s01.oss.sonatype.org/content/repositories/snapshots/")
}
Expand Down
2 changes: 0 additions & 2 deletions samples/pagination/pagination-support/settings.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
pluginManagement {
repositories {
mavenLocal()
maven {
url = uri("https://s01.oss.sonatype.org/content/repositories/snapshots/")
}
Expand All @@ -11,7 +10,6 @@ pluginManagement {
}
dependencyResolutionManagement {
repositories {
mavenLocal()
maven {
url = uri("https://s01.oss.sonatype.org/content/repositories/snapshots/")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -624,6 +624,7 @@ class CachePartialResultTest {
),
),
onUser = WithFragmentsQuery.OnUser(
__typename = "User",
lastName = "Smith",
onUser = WithFragmentsQuery.OnUser1(
nickName0 = "JS"
Expand Down Expand Up @@ -669,6 +670,7 @@ class CachePartialResultTest {
lead0 = null,
),
onUser = WithFragmentsQuery.OnUser(
__typename = "User",
lastName = "Smith",
onUser = WithFragmentsQuery.OnUser1(
nickName0 = "JS"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,7 @@ class StoreErrorsTest {
),
),
onUser = WithFragmentsQuery.OnUser(
__typename = "User",
lastName = "Smith",
onUser = WithFragmentsQuery.OnUser1(
nickName0 = "JS"
Expand Down