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鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

[馃悩 gradle-plugin] Add overload for dependsOn that tries to do cross project configuration #5606

Merged
merged 1 commit into from
Feb 7, 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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -766,11 +766,36 @@ interface Service {
fun operationManifestConnection(action: Action<in OperationManifestConnection>)

/**
* Adds a given dependency for the codegen
* Adds a dependency for the codegen. Use this when some types/fragments are generated
* in upstream modules.
*/
fun dependsOn(dependencyNotation: Any)

/**
* Counterpoint of [dependsOn]. [isADependencyOf] allows a schema module to discover
* used types in downstream modules automatically without having to specify [alwaysGenerateTypesMatching].
*
* This works by setting a dependency on the IR in downstream modules.
* Because the IR task and the codegen task are separate this does not create a cycle.
*
* @see [alwaysGenerateTypesMatching]
*/
fun isADependencyOf(dependencyNotation: Any)

/**
* Same as [dependsOn] but tries to do automatic cross-project configuration.
* This is highly experimental and probably not compatible with most Gradle best practices.
*
* Use at your own risks!
*
* @param bidirectional if true and if [dependencyNotation] is a project dependency,
* this version of [dependsOn] also calls [isADependencyOf] automatically by using
* cross-project configuration. This is experimental and probably not project isolation compatible.
*
*/
@ApolloExperimental
fun dependsOn(dependencyNotation: Any, bidirectional: Boolean)

class OperationOutputConnection(
/**
* The task that produces operationOutput
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ abstract class DefaultApolloExtension(
private var hasExplicitService = false
private val adhocComponentWithVariants: AdhocComponentWithVariants
private val apolloMetadataConfiguration: Configuration
private val pendingDownstreamDependencies: MutableMap<String, List<String>> = mutableMapOf()

internal fun getServiceInfos(project: Project): List<ApolloGradleToolingModel.ServiceInfo> = services.map { service ->
DefaultServiceInfo(
Expand All @@ -66,6 +67,19 @@ abstract class DefaultApolloExtension(
)
}

internal fun registerDownstreamProject(serviceName: String, projectPath: String) {
val existingService = services.firstOrNull {
it.name == serviceName
}
if (existingService != null) {
existingService.isADependencyOf(project.rootProject.project(projectPath))
} else {
pendingDownstreamDependencies.compute(serviceName) { _, oldValue ->
oldValue.orEmpty() + projectPath
}
}
}

internal fun getServiceTelemetryData(): List<ApolloGradleToolingModel.TelemetryData.ServiceTelemetryData> = services.map { service ->
DefaultServiceTelemetryData(
codegenModels = service.codegenModels.orNull,
Expand Down Expand Up @@ -545,6 +559,13 @@ abstract class DefaultApolloExtension(
upstreamIrConsumerConfiguration.dependencies.add(it)
codegenMetadataConsumerConfiguration.dependencies.add(it)
}

val pending = pendingDownstreamDependencies.get(name)
if (pending != null) {
pending.forEach {
service.isADependencyOf(project.project(it))
}
}
service.downstreamDependencies.forEach {
downstreamIrConsumerConfiguration.dependencies.add(it)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
package com.apollographql.apollo3.gradle.internal

import com.apollographql.apollo3.annotations.ApolloDeprecatedSince
import com.apollographql.apollo3.gradle.api.ApolloExtension
import com.apollographql.apollo3.gradle.api.Introspection
import com.apollographql.apollo3.gradle.api.RegisterOperationsConfig
import com.apollographql.apollo3.gradle.api.Registry
import com.apollographql.apollo3.gradle.api.Service
import org.gradle.api.Action
import org.gradle.api.Project
import org.gradle.api.artifacts.Dependency
import org.gradle.api.artifacts.ProjectDependency
import org.gradle.api.file.ConfigurableFileTree
import org.gradle.api.file.FileCollection
import org.gradle.api.file.RegularFileProperty
Expand Down Expand Up @@ -188,7 +190,26 @@ abstract class DefaultService @Inject constructor(val project: Project, override
override fun mapScalarToUpload(graphQLName: String) = mapScalar(graphQLName, "com.apollographql.apollo3.api.Upload", "com.apollographql.apollo3.api.UploadAdapter")

override fun dependsOn(dependencyNotation: Any) {
dependsOn(dependencyNotation, false)
}

override fun dependsOn(dependencyNotation: Any, bidirectional: Boolean) {
upstreamDependencies.add(project.dependencies.create(dependencyNotation))
if (bidirectional) {
val upstreamProject = when (dependencyNotation) {
is ProjectDependency -> project.rootProject.project(dependencyNotation.dependencyProject.path)
is Project -> dependencyNotation
else -> error("dependsOn(dependencyNotation, true) requires a Project or ProjectDependency")
}

upstreamProject.plugins.withId("com.apollographql.apollo3") {
val apolloExtension = (upstreamProject.extensions.findByType(ApolloExtension::class.java) as? DefaultApolloExtension)
check(apolloExtension != null) {
"Cannot find 'apollo' extension in upstream project ${upstreamProject.name} (registered: ${upstreamProject.extensions})"
}
apolloExtension.registerDownstreamProject(name, project.path)
}
}
}

override fun isADependencyOf(dependencyNotation: Any) {
Expand Down
19 changes: 19 additions & 0 deletions tests/multi-module-1/bidirectional/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
plugins {
id("org.jetbrains.kotlin.jvm")
id("com.apollographql.apollo3")
}

apolloTest()

dependencies {
implementation(libs.apollo.runtime)
implementation(project(":multi-module-1-root"))
testImplementation(libs.kotlin.test.junit)
}

apollo {
service("service") {
packageNamesFromFilePaths()
dependsOn(project(":multi-module-1-root"), true)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
query GetA {
a {
foo
}

}
1 change: 1 addition & 0 deletions tests/multi-module-1/root/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ apolloTest()

dependencies {
implementation(libs.apollo.runtime)
testImplementation(libs.kotlin.test)
}

apollo {
Expand Down
10 changes: 10 additions & 0 deletions tests/multi-module-1/root/src/main/graphql/schema.graphqls
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
type Query {
long: Long!
a: A
b: B
}

type A {
foo: Int
}

type B {
foo: Int
}

scalar Long
Expand Down
11 changes: 11 additions & 0 deletions tests/multi-module-1/root/src/test/kotlin/MainTest.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
import kotlin.test.Test

class MainTest {
@Test
fun test() {
println(multimodule1.root.fragment.QueryDetails::class.java)
// A is used in the bidirectional module and registered automatically
println(multimodule1.root.type.A::class.java)
// B is not used at all and must not be generated
try {
val clazz = Class.forName("multimodule1.root.type.B")
error("An exception was expected but got $clazz instead")
} catch (e: ClassNotFoundException) {
}
}
}