Skip to content

Compatibility with apollo-compiler:5.0.0+ #178

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 5 commits into from
Jun 18, 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
17 changes: 17 additions & 0 deletions build-logic/src/main/kotlin/lib.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import com.gradleup.librarian.gradle.Librarian
import org.gradle.api.Project
import org.gradle.api.publish.PublishingExtension
import java.net.URI

fun Project.lib() {
Librarian.module(this)

extensions.findByType(PublishingExtension::class.java)?.apply {
repositories {
maven {
name = "local"
url = URI("file://" + rootProject.layout.buildDirectory.dir("m2").get().asFile.path)
}
}
}
}
7 changes: 6 additions & 1 deletion normalized-cache-apollo-compiler-plugin/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ plugins {
id("org.jetbrains.kotlin.jvm")
}

Librarian.module(project)
lib()

dependencies {
compileOnly(libs.apollo.compiler)
Expand All @@ -14,3 +14,8 @@ dependencies {
implementation(libs.kotlin.poet)
testImplementation(libs.kotlin.test)
}

tasks.withType(Test::class.java).configureEach {
dependsOn("publishAllPublicationsToLocalRepository")
dependsOn(":normalized-cache:publishAllPublicationsToLocalRepository")
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ 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.ApolloCompiler
import com.apollographql.apollo.compiler.ApolloCompilerPluginEnvironment
import com.apollographql.apollo.compiler.ApolloCompilerPluginLogger
import com.apollographql.apollo.compiler.SchemaCodeGenerator
import com.apollographql.cache.apollocompilerplugin.VERSION
import com.squareup.kotlinpoet.ClassName
Expand Down Expand Up @@ -58,8 +60,30 @@ internal class CacheSchemaCodeGenerator(
file.writeTo(outputDirectory)
}

private fun ApolloCompilerPluginEnvironment.logger(): ApolloCompiler.Logger {
val method = this::class.java.methods.first { it.name == "getLogger" }
if (method.returnType.name == "com.apollographql.apollo.compiler.ApolloCompiler\$Logger") {
/**
* The code is running on v5 where logger was converted to return directly ApolloCompiler.Logger.
*
* See https://github.com/apollographql/apollo-kotlin-normalized-cache/pull/178
*/
return method.invoke(this) as ApolloCompiler.Logger
}

return logger.toApolloCompilerLogger()
}

private fun ApolloCompilerPluginLogger.toApolloCompilerLogger(): ApolloCompiler.Logger {
return object : ApolloCompiler.Logger {
override fun warning(message: String) {
return this@toApolloCompilerLogger.error(message)
}
}
}

private fun maxAgeProperty(schema: Schema): PropertySpec {
val maxAges = schema.getMaxAges(environment.logger)
val maxAges = schema.getMaxAges(environment.logger())
val initializer = CodeBlock.builder().apply {
add("mapOf(\n")
withIndent {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@ import com.apollographql.apollo.ast.GQLTypeDefinition
import com.apollographql.apollo.ast.Schema
import com.apollographql.apollo.ast.SourceLocation
import com.apollographql.apollo.ast.pretty
import com.apollographql.apollo.compiler.ApolloCompiler
import com.apollographql.apollo.compiler.ApolloCompilerPluginLogger
import java.lang.reflect.Method

@OptIn(ApolloExperimental::class)
internal fun Schema.getMaxAges(logger: ApolloCompilerPluginLogger): Map<String, Int> {
internal fun Schema.getMaxAges(logger: ApolloCompiler.Logger): Map<String, Int> {
var hasIssues = false
val typeDefinitions = this.typeDefinitions
fun GQLDirective.maxAgeAndInherit(): Pair<Int?, Boolean> {
Expand Down Expand Up @@ -83,9 +85,20 @@ internal fun Schema.getMaxAges(logger: ApolloCompilerPluginLogger): Map<String,
private const val CACHE_CONTROL = "cacheControl"
private const val CACHE_CONTROL_FIELD = "cacheControlField"

private val errorMethod: Method? by lazy {
ApolloCompiler.Logger::class.java.methods.singleOrNull { it.name == "error" }
}

@OptIn(ApolloExperimental::class)
private fun ApolloCompilerPluginLogger.logIssue(message: String, sourceLocation: SourceLocation?) {
error("${sourceLocation.pretty()}: ${message}")
private fun ApolloCompiler.Logger.logIssue(message: String, sourceLocation: SourceLocation?) {
val str = "${sourceLocation.pretty()}: ${message}"
if (errorMethod != null) {
// v5+ we have an `error()` method, but it doesn't add the "e: " prefix
errorMethod!!.invoke(this, "e: [apollo] $str")
} else {
// v4- ApolloCompiler.Logger.warning() redirects to ApolloCompilerPluginLogger.error()
warning(str)
}
}

private val GQLTypeDefinition.fields
Expand Down
Original file line number Diff line number Diff line change
@@ -1,27 +1,115 @@
package com.apollographql.cache.apollocompilerplugin.internal

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

/**
* Those tests all share the same `build/testProject` directory and are therefore synchronized.
*/
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)"))
@Synchronized
fun apollo_4_2_fails() {
withProject {
it.version("4.2.0")
}.assertFailure("generateServiceApolloSources") {
assertTrue(it.message!!.contains("The Apollo Cache compiler plugin requires Apollo Kotlin version 4.3.0 or higher (found 4.2.0)"))
}
}

@Test
@Synchronized
fun apollo_4_3_succeeds() {
withProject {
it.version("4.3.0")
}.assertSuccess("build")
}

@Test
@Synchronized
@Ignore("There is no 5.0.0 release yet")
fun apollo_5_0_succeeds() {
withProject {
it.version("5.0.0-SNAPSHOT")
}.assertSuccess("build")
}

@Test
@Synchronized
fun apollo_4_3_shows_error_message() {
withProject {
it.version("4.3.0")
it.addBadCacheControl()
}.assertFailure("generateServiceApolloSources") {
assertTrue(it.message!!.contains("`maxAge` must not be negative"))
}
}

@Test
@Synchronized
@Ignore("There is no 5.0.0 release yet")
fun apollo_5_0_shows_error_message() {
withProject {
it.version("5.0.0-SNAPSHOT")
it.addBadCacheControl()
}.assertFailure("generateServiceApolloSources") {
assertTrue(it.message!!.contains("`maxAge` must not be negative"))
}
}
}

private fun withProject(block: (File) -> Unit): File {
val workingDir = File("build/testProject")

workingDir.deleteRecursively()
File("test-data/testProject").copyRecursively(workingDir)
block(workingDir)
return workingDir
}

private fun File.addBadCacheControl() {
resolve("src/main/graphql/schema.graphqls").replace("# DIRECTIVE_PLACEHOLDER", "@cacheControl(maxAge: -10)")
}

private fun File.version(version: String) {
resolve("build.gradle.kts").replace("4.2.0", version)
}
private fun File.assertFailure(taskName: String, onFailure: (UnexpectedBuildFailure) -> Unit) {
newRunner().assertFailure(taskName, onFailure)
}

private fun File.assertSuccess(taskName: String) {
newRunner().assertSuccess(taskName)
}

private fun File.newRunner(): GradleRunner {
return GradleRunner.create()
.withProjectDir(this)
}

private fun GradleRunner.assertFailure(taskName: String, onFailure: (UnexpectedBuildFailure) -> Unit) {
try {
withArguments(taskName)
.build()
} catch (e: UnexpectedBuildFailure) {
onFailure(e)
}
}

private fun GradleRunner.assertSuccess(taskName: String) {
withArguments(taskName)
.build().apply {
assertEquals(TaskOutcome.SUCCESS, task(":$taskName")!!.outcome)
}
}

private fun File.replace(str: String, replacement: String) {
writeText(readText().replace(str, replacement))
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import com.apollographql.apollo.ast.builtinForeignSchemas
import com.apollographql.apollo.ast.internal.SchemaValidationOptions
import com.apollographql.apollo.ast.parseAsGQLDocument
import com.apollographql.apollo.ast.validateAsSchema
import com.apollographql.apollo.compiler.ApolloCompiler
import com.apollographql.apollo.compiler.ApolloCompilerPlugin
import com.apollographql.apollo.compiler.ApolloCompilerPluginLogger
import kotlin.test.Test
import kotlin.test.assertEquals
Expand Down Expand Up @@ -70,20 +72,8 @@ class GetMaxAgesTest {
)
.getOrThrow()
.getMaxAges(
object : ApolloCompilerPluginLogger {
override fun error(message: String) {
fail()
}

override fun info(message: String) {
fail()
}

override fun logging(message: String) {
fail()
}

override fun warn(message: String) {
object : ApolloCompiler.Logger {
override fun warning(message: String) {
fail()
}
}
Expand Down Expand Up @@ -152,23 +142,11 @@ class GetMaxAgesTest {
)
.getOrThrow()
.getMaxAges(
object : ApolloCompilerPluginLogger {
override fun error(message: String) {
errors += message
}

override fun info(message: String) {
fail()
}

override fun logging(message: String) {
fail()
}

override fun warn(message: String) {
fail()
}
object : ApolloCompiler.Logger {
override fun warning(message: String) {
errors += message
}
}
)
}
val expectedErrors = listOf(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,16 @@ plugins {
apollo {
service("service") {
packageName.set("com.example")
plugin("com.apollographql.cache:normalized-cache-apollo-compiler-plugin") {
// We use + as a version here to avoid having to share the version with the main build
// There is also an exclusiveContent filter installed to make sure we resolve the local artifacts
plugin("com.apollographql.cache:normalized-cache-apollo-compiler-plugin:+") {
argument("packageName", packageName.get())
}
}
}

dependencies {
implementation("com.apollographql.apollo:apollo-api")
implementation("com.apollographql.cache:normalized-cache:+")
testImplementation(libs.kotlin.test)
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
apply(from = "gradle/repositories.gradle.kts")
dependencyResolutionManagement {
repositories {
exclusiveContent {
// Make sure the cache artifacts are the ones from the local maven repo
forRepository{
maven("../../../build/m2")
}
filter {
includeGroup("com.apollographql.cache")
}
}

mavenCentral()
}
}

includeBuild("../../..")
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
extend schema @link(url: "https://specs.apollo.dev/cache/v0.1/", import: ["@cacheControl"])

type Query {
product: Product
foo: Int
}

type Product @typePolicy(keyFields: "id"){
type Product
@typePolicy(keyFields: "id")
# DIRECTIVE_PLACEHOLDER
{
id: ID!
price: Float
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import com.example.cache.Cache
import kotlin.test.Test
import kotlin.test.assertEquals

class MainTest {
@Test
fun mainTest() {
assertEquals("id", Cache.typePolicies.get("Product")?.keyFields?.single())
}
}
2 changes: 1 addition & 1 deletion normalized-cache-sqlite/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ plugins {
id("app.cash.sqldelight")
}

Librarian.module(project)
lib()

kotlin {
configureKmp(
Expand Down
4 changes: 1 addition & 3 deletions normalized-cache/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
import com.gradleup.librarian.gradle.Librarian

plugins {
id("org.jetbrains.kotlin.multiplatform")
id("org.jetbrains.kotlin.plugin.atomicfu")
}

Librarian.module(project)
lib()

kotlin {
configureKmp(
Expand Down