Skip to content

Commit

Permalink
[IJ Plugin] Consider all Gradle projects recursively (#5734)
Browse files Browse the repository at this point in the history
* Consider all Gradle projects, recursively

* Use Gradle project path everywhere

* Fix test
  • Loading branch information
BoD authored Mar 18, 2024
1 parent d0a2827 commit a1fba22
Show file tree
Hide file tree
Showing 10 changed files with 80 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,18 @@ interface ApolloKotlinServiceListener {
}

data class ApolloKotlinService(
val gradleProjectName: String,
val gradleProjectPath: String,
val serviceName: String,
val schemaPaths: List<String>,
val operationPaths: List<String>,
val endpointUrl: String?,
val endpointHeaders: Map<String, String>?,
) {
data class Id(val gradleProjectName: String, val serviceName: String) {
override fun toString() = "$gradleProjectName/$serviceName"
data class Id(val gradleProjectPath: String, val serviceName: String) {
override fun toString(): String {
val formattedPath = gradleProjectPath.split(":").filterNot { it.isEmpty() }.joinToString("-")
return "$formattedPath/$serviceName"
}

companion object {
fun fromString(string: String): Id? {
Expand All @@ -31,5 +34,5 @@ data class ApolloKotlinService(
}
}

val id = Id(gradleProjectName, serviceName)
val id = Id(gradleProjectPath, serviceName)
}
Original file line number Diff line number Diff line change
Expand Up @@ -174,16 +174,16 @@ class GradleToolingModelService(
project.telemetryService.gradleModuleCount = rootGradleProject.children.size + 1

// We're only interested in projects that apply the Apollo plugin - and thus have the codegen task registered
val allApolloGradleProjects: List<GradleProject> = (rootGradleProject.children + rootGradleProject)
val allApolloGradleProjects: List<GradleProject> = rootGradleProject.allChildrenRecursively()
.filter { gradleProject -> gradleProject.tasks.any { task -> task.name == CODEGEN_GRADLE_TASK_NAME } }
logd("allApolloGradleProjects=${allApolloGradleProjects.map { it.name }}")
logd("allApolloGradleProjects=${allApolloGradleProjects.map { it.path }}")
project.telemetryService.apolloKotlinModuleCount = allApolloGradleProjects.size

val allToolingModels = allApolloGradleProjects.mapIndexedNotNull { index, gradleProject ->
if (isAbortRequested()) return@doRun
gradleExecutionHelper.execute(gradleProject.projectDirectory.canonicalPath, executionSettings) { connection ->
gradleCancellation = GradleConnector.newCancellationTokenSource()
logd("Fetch tooling model for :${gradleProject.name}")
logd("Fetch tooling model for ${gradleProject.path}")
return@execute try {
val id = ExternalSystemTaskId.create(GRADLE_SYSTEM_ID, ExternalSystemTaskType.RESOLVE_PROJECT, project)
gradleExecutionHelper.getModelBuilder(ApolloGradleToolingModel::class.java, connection, id, executionSettings, ExternalSystemTaskNotificationListenerAdapter.NULL_OBJECT)
Expand All @@ -192,12 +192,12 @@ class GradleToolingModelService(
.takeIf {
val isCompatibleVersion = it.versionMajor == ApolloGradleToolingModel.VERSION_MAJOR
if (!isCompatibleVersion) {
logw("Incompatible version of Apollo Gradle plugin in module :${gradleProject.name}: ${it.versionMajor} != ${ApolloGradleToolingModel.VERSION_MAJOR}, ignoring")
logw("Incompatible version of Apollo Gradle plugin in module ${gradleProject.path}: ${it.versionMajor} != ${ApolloGradleToolingModel.VERSION_MAJOR}, ignoring")
}
isCompatibleVersion
}
} catch (t: Throwable) {
logw(t, "Couldn't fetch tooling model for :${gradleProject.name}")
logw(t, "Couldn't fetch tooling model for ${gradleProject.path}")
null
} finally {
gradleCancellation = null
Expand Down Expand Up @@ -228,36 +228,37 @@ class GradleToolingModelService(

private fun computeApolloKotlinServices(toolingModels: List<ApolloGradleToolingModel>) {
// Compute the ApolloKotlinServices, taking into account the dependencies between projects
val allKnownProjectNames = toolingModels.map { it.projectName }
val allKnownProjectPaths = toolingModels.map { it.projectPathCompat }
val projectServiceToApolloKotlinServices = mutableMapOf<String, ApolloKotlinService>()
fun getApolloKotlinService(projectName: String, serviceName: String): ApolloKotlinService {
val key = "$projectName/$serviceName"

fun getApolloKotlinService(projectPath: String, serviceName: String): ApolloKotlinService {
val key = "$projectPath/$serviceName"
return projectServiceToApolloKotlinServices.getOrPut(key) {
val toolingModel = toolingModels.first { it.projectName == projectName }
val toolingModel = toolingModels.first { it.projectPathCompat == projectPath }
val serviceInfo = toolingModel.serviceInfos.first { it.name == serviceName }
val dependenciesProjectFiles = serviceInfo.upstreamProjects
val upstreamApolloKotlinServices = serviceInfo.upstreamProjectPathsCompat(toolingModel)
// The tooling model for some upstream projects might not have been fetched successfully - filter them out
.filter { upstreamProject -> upstreamProject in allKnownProjectNames }
.map { getApolloKotlinService(it, serviceName) }
.filter { upstreamProjectPath -> upstreamProjectPath in allKnownProjectPaths }
.map { upstreamProjectPath -> getApolloKotlinService(upstreamProjectPath, serviceName) }
ApolloKotlinService(
gradleProjectName = projectName,
gradleProjectPath = projectPath,
serviceName = serviceName,
schemaPaths = (serviceInfo.schemaFiles.mapNotNull { it.toProjectLocalPathOrNull() } +
dependenciesProjectFiles.flatMap { it.schemaPaths })
upstreamApolloKotlinServices.flatMap { it.schemaPaths })
.distinct(),
operationPaths = (serviceInfo.graphqlSrcDirs.mapNotNull { it.toProjectLocalPathOrNull() } +
dependenciesProjectFiles.flatMap { it.operationPaths })
upstreamApolloKotlinServices.flatMap { it.operationPaths })
.distinct(),
endpointUrl = if (toolingModel.versionMinor >= ApolloGradleToolingModel.VERSION_MINOR) serviceInfo.endpointUrl else null,
endpointHeaders = if (toolingModel.versionMinor >= ApolloGradleToolingModel.VERSION_MINOR) serviceInfo.endpointHeaders else null,
endpointUrl = serviceInfo.endpointUrlCompat(toolingModel),
endpointHeaders = serviceInfo.endpointHeadersCompat(toolingModel),
)
}
}

val apolloKotlinServices = mutableListOf<ApolloKotlinService>()
for (toolingModel in toolingModels) {
for (serviceInfo in toolingModel.serviceInfos) {
apolloKotlinServices += getApolloKotlinService(toolingModel.projectName, serviceInfo.name)
apolloKotlinServices += getApolloKotlinService(toolingModel.projectPathCompat, serviceInfo.name)
}
}
this.apolloKotlinServices = apolloKotlinServices
Expand Down Expand Up @@ -293,4 +294,30 @@ class GradleToolingModelService(
}
}

private fun GradleProject.allChildrenRecursively(): List<GradleProject> {
return listOf(this) + children.flatMap { it.allChildrenRecursively() }
}

private val ApolloGradleToolingModel.projectPathCompat: String
get() = if (versionMinor >= 3) {
projectPath
} else {
@Suppress("DEPRECATION")
projectName
}

private fun ApolloGradleToolingModel.ServiceInfo.upstreamProjectPathsCompat(toolingModel: ApolloGradleToolingModel) =
if (toolingModel.versionMinor >= 3) {
upstreamProjectPaths
} else {
@Suppress("DEPRECATION")
upstreamProjects
}

private fun ApolloGradleToolingModel.ServiceInfo.endpointUrlCompat(toolingModel: ApolloGradleToolingModel) =
if (toolingModel.versionMinor >= 1) endpointUrl else null

private fun ApolloGradleToolingModel.ServiceInfo.endpointHeadersCompat(toolingModel: ApolloGradleToolingModel) =
if (toolingModel.versionMinor >= 1) endpointHeaders else null

val Project.gradleToolingModelService get() = service<GradleToolingModelService>()
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ class ApiKeysModel : AddEditRemovePanel.TableModel<ApolloKotlinServiceConfigurat
}

override fun getField(o: ApolloKotlinServiceConfiguration, columnIndex: Int) = when (columnIndex) {
0 -> o.apolloKotlinServiceId.gradleProjectName
0 -> o.apolloKotlinServiceId.gradleProjectPath
1 -> o.apolloKotlinServiceId.serviceName
2 -> "••••••••"
3 -> o.graphOsGraphName
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ class ApiKeyDialog(
val isEdit = apolloKotlinServiceId != null
title = ApolloBundle.message(if (isEdit) "settings.studio.apiKeyDialog.title.edit" else "settings.studio.apiKeyDialog.title.add")
gradleProjectName = if (isEdit) {
apolloKotlinServiceId!!.gradleProjectName
apolloKotlinServiceId!!.gradleProjectPath
} else {
getGradleProjectNames().firstOrNull() ?: ""
}
Expand Down Expand Up @@ -106,12 +106,12 @@ class ApiKeyDialog(
}.withPreferredWidth(450)

private fun getGradleProjectNames(): List<String> {
return GradleToolingModelService.getApolloKotlinServices(project).map { it.id.gradleProjectName }.distinct().sorted()
return GradleToolingModelService.getApolloKotlinServices(project).map { it.id.gradleProjectPath }.distinct().sorted()
}

private fun getApolloKotlinServiceNames(gradleProjectName: String): List<String> {
return GradleToolingModelService.getApolloKotlinServices(project)
.filter { it.id.gradleProjectName == gradleProjectName }
.filter { it.id.gradleProjectPath == gradleProjectName }
.map { it.id.serviceName }
.sorted()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ public abstract interface class com/apollographql/apollo3/gradle/api/ApolloExten
public abstract interface class com/apollographql/apollo3/gradle/api/ApolloGradleToolingModel {
public static final field Companion Lcom/apollographql/apollo3/gradle/api/ApolloGradleToolingModel$Companion;
public static final field VERSION_MAJOR I
public static final field VERSION_MINOR I
public abstract fun getProjectName ()Ljava/lang/String;
public abstract fun getProjectPath ()Ljava/lang/String;
public abstract fun getServiceInfos ()Ljava/util/List;
public abstract fun getTelemetryData ()Lcom/apollographql/apollo3/gradle/api/ApolloGradleToolingModel$TelemetryData;
public abstract fun getVersionMajor ()I
Expand All @@ -39,7 +39,6 @@ public abstract interface class com/apollographql/apollo3/gradle/api/ApolloGradl

public final class com/apollographql/apollo3/gradle/api/ApolloGradleToolingModel$Companion {
public static final field VERSION_MAJOR I
public static final field VERSION_MINOR I
}

public abstract interface class com/apollographql/apollo3/gradle/api/ApolloGradleToolingModel$ServiceInfo {
Expand All @@ -48,6 +47,7 @@ public abstract interface class com/apollographql/apollo3/gradle/api/ApolloGradl
public abstract fun getGraphqlSrcDirs ()Ljava/util/Set;
public abstract fun getName ()Ljava/lang/String;
public abstract fun getSchemaFiles ()Ljava/util/Set;
public abstract fun getUpstreamProjectPaths ()Ljava/util/Set;
public abstract fun getUpstreamProjects ()Ljava/util/Set;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@ interface ApolloGradleToolingModel {
val versionMajor: Int
val versionMinor: Int

@Deprecated("Use projectPath instead", ReplaceWith("projectPath"))
val projectName: String

// Introduced in 1.3
val projectPath: String

val serviceInfos: List<ServiceInfo>

// Introduced in 1.2
Expand All @@ -16,8 +21,12 @@ interface ApolloGradleToolingModel {
val name: String
val schemaFiles: Set<File>
val graphqlSrcDirs: Set<File>
@Deprecated("Use upstreamProjectPaths instead", ReplaceWith("upstreamProjectPaths"))
val upstreamProjects: Set<String>

// Introduced in 1.3
val upstreamProjectPaths: Set<String>

// Introduced in 1.1
val endpointUrl: String?

Expand Down Expand Up @@ -78,6 +87,6 @@ interface ApolloGradleToolingModel {
* Current minor version of the tooling model.
* Increment this value when the model changes in compatible ways (additions).
*/
const val VERSION_MINOR = 2
internal const val VERSION_MINOR = 3
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ constructor(private val toolingModelRegistry: ToolingModelBuilderRegistry) : Plu
override fun buildAll(modelName: String, project: Project): ApolloGradleToolingModel {
return DefaultApolloGradleToolingModel(
projectName = project.name,
projectPath = project.path,
serviceInfos = apolloExtension.getServiceInfos(project),
telemetryData = getTelemetryData(project, apolloExtension),
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ abstract class DefaultApolloExtension(
schemaFiles = service.schemaFilesSnapshot(project),
graphqlSrcDirs = service.graphqlSourceDirectorySet.srcDirs,
upstreamProjects = service.upstreamDependencies.filterIsInstance<ProjectDependency>().map { it.name }.toSet(),
upstreamProjectPaths = service.upstreamDependencies.filterIsInstance<ProjectDependency>().map { it.dependencyProject.path }.toSet(),
endpointUrl = service.introspection?.endpointUrl?.orNull,
endpointHeaders = service.introspection?.headers?.orNull,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import java.io.File
import java.io.Serializable

internal data class DefaultApolloGradleToolingModel(
@Deprecated("Use projectPath instead", replaceWith = ReplaceWith("projectPath"))
override val projectName: String,
override val projectPath: String,
override val serviceInfos: List<ApolloGradleToolingModel.ServiceInfo>,
override val telemetryData: ApolloGradleToolingModel.TelemetryData,
) : ApolloGradleToolingModel, Serializable {
Expand All @@ -17,7 +19,9 @@ internal data class DefaultServiceInfo(
override val name: String,
override val schemaFiles: Set<File>,
override val graphqlSrcDirs: Set<File>,
@Deprecated("Use upstreamProjectPaths instead", replaceWith = ReplaceWith("upstreamProjectPaths"))
override val upstreamProjects: Set<String>,
override val upstreamProjectPaths: Set<String>,
override val endpointUrl: String?,
override val endpointHeaders: Map<String, String>?,
) : ApolloGradleToolingModel.ServiceInfo, Serializable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,10 @@ class GradleToolingTests {
connection.getModel(ApolloGradleToolingModel::class.java)
}
Assert.assertEquals(ApolloGradleToolingModel.VERSION_MAJOR, toolingModel.versionMajor)
Assert.assertEquals(ApolloGradleToolingModel.VERSION_MINOR, toolingModel.versionMinor)
Assert.assertEquals(3, toolingModel.versionMinor)
@Suppress("DEPRECATION")
Assert.assertEquals(emptyList<String>(), toolingModel.serviceInfos.flatMap { it.upstreamProjects })
Assert.assertEquals(emptyList<String>(), toolingModel.serviceInfos.flatMap { it.upstreamProjectPaths })

val serviceInfo0 = toolingModel.serviceInfos[0]
Assert.assertEquals("starwars", serviceInfo0.name)
Expand All @@ -80,8 +82,10 @@ class GradleToolingTests {
connection.getModel(ApolloGradleToolingModel::class.java)
}
Assert.assertEquals(ApolloGradleToolingModel.VERSION_MAJOR, toolingModel.versionMajor)
Assert.assertEquals(ApolloGradleToolingModel.VERSION_MINOR, toolingModel.versionMinor)
Assert.assertEquals(3, toolingModel.versionMinor)
@Suppress("DEPRECATION")
Assert.assertEquals(listOf("node1", "node2"), toolingModel.serviceInfos.flatMap { it.upstreamProjects }.sorted())
Assert.assertEquals(listOf(":node1", ":node2"), toolingModel.serviceInfos.flatMap { it.upstreamProjectPaths }.sorted())
}
}
}

0 comments on commit a1fba22

Please sign in to comment.