Skip to content

Commit aa154f6

Browse files
authored
[IJ plugin] v3 -> v4 migration: Gradle conf (#5114)
1 parent 183e505 commit aa154f6

17 files changed

+484
-32
lines changed
Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
1-
package com.apollographql.ijplugin.refactoring.migration.v2tov3.item
1+
package com.apollographql.ijplugin.refactoring.migration.item
22

3-
import com.apollographql.ijplugin.refactoring.migration.item.MigrationItem
4-
import com.apollographql.ijplugin.refactoring.migration.item.MigrationItemUsageInfo
53
import com.apollographql.ijplugin.util.addSiblingAfter
64
import com.apollographql.ijplugin.util.findPsiFilesByName
75
import com.apollographql.ijplugin.util.unquoted
@@ -27,25 +25,35 @@ object UpdateCustomTypeMappingInBuildKts : MigrationItem() {
2725
file.accept(object : KtTreeVisitorVoid() {
2826
override fun visitReferenceExpression(expression: KtReferenceExpression) {
2927
super.visitReferenceExpression(expression)
30-
if (expression.text == "customTypeMapping") {
28+
if (expression.text == "customTypeMapping" || expression.text == "customScalarsMapping") {
3129
// `customTypeMapping.set(mapOf(...))`
3230
val qualifiedExpression = expression.parent as? KtQualifiedExpression ?: return
3331
// `set(mapOf(...))`
3432
val setCallExpression = qualifiedExpression.selectorExpression
35-
?.findDescendantOfType<KtCallExpression> { it.calleeExpression?.text == "set" } ?: return
36-
// `mapOf(...)`
37-
val mapOfCallExpression = setCallExpression.valueArguments.firstOrNull()
38-
?.findDescendantOfType<KtCallExpression> { it.calleeExpression?.text == "mapOf" } ?: return
33+
?.findDescendantOfType<KtCallExpression> { it.calleeExpression?.text == "set" }
34+
if (setCallExpression != null) {
35+
// `mapOf(...)`
36+
val mapOfCallExpression = setCallExpression.valueArguments.firstOrNull()
37+
?.findDescendantOfType<KtCallExpression> { it.calleeExpression?.text == "mapOf" } ?: return
3938

40-
@Suppress("UNCHECKED_CAST")
41-
val map: Map<String, String> = mapOfCallExpression.valueArguments.associate { argument ->
42-
val binaryExpression = argument.getArgumentExpression() as? KtBinaryExpression ?: return@associate null to null
43-
val key = binaryExpression.left?.text?.unquoted()
44-
val value = binaryExpression.right?.text?.unquoted()
45-
key to value
39+
@Suppress("UNCHECKED_CAST")
40+
val map: Map<String, String> = mapOfCallExpression.valueArguments.associate { argument ->
41+
val binaryExpression = argument.getArgumentExpression() as? KtBinaryExpression ?: return@associate null to null
42+
val key = binaryExpression.left?.text?.unquoted()
43+
val value = binaryExpression.right?.text?.unquoted()
44+
key to value
45+
}
46+
.filterNot { it.key == null || it.value == null } as Map<String, String>
47+
usages.add(MigrationItemUsageInfo(this@UpdateCustomTypeMappingInBuildKts, qualifiedExpression, map))
48+
return
4649
}
47-
.filterNot { it.key == null || it.value == null } as Map<String, String>
48-
usages.add(MigrationItemUsageInfo(this@UpdateCustomTypeMappingInBuildKts, qualifiedExpression, map))
50+
51+
// `put("LocalDate", "java.time.LocalDate")`
52+
val putCallExpression = qualifiedExpression.selectorExpression
53+
?.findDescendantOfType<KtCallExpression> { it.calleeExpression?.text == "put" } ?: return
54+
val key = putCallExpression.valueArguments.getOrNull(0)?.text?.unquoted()?: return
55+
val value = putCallExpression.valueArguments.getOrNull(1)?.text?.unquoted()?: return
56+
usages.add(MigrationItemUsageInfo(this@UpdateCustomTypeMappingInBuildKts, qualifiedExpression, mapOf(key to value)))
4957
}
5058
}
5159
})

intellij-plugin/src/main/kotlin/com/apollographql/ijplugin/refactoring/migration/v2tov3/ApolloV2ToV3MigrationProcessor.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import com.apollographql.ijplugin.refactoring.migration.item.UpdatePackageName
1414
import com.apollographql.ijplugin.refactoring.migration.v2tov3.item.AddUseVersion2Compat
1515
import com.apollographql.ijplugin.refactoring.migration.v2tov3.item.RemoveDependenciesInBuildKts
1616
import com.apollographql.ijplugin.refactoring.migration.v2tov3.item.UpdateAddCustomTypeAdapter
17-
import com.apollographql.ijplugin.refactoring.migration.v2tov3.item.UpdateCustomTypeMappingInBuildKts
17+
import com.apollographql.ijplugin.refactoring.migration.item.UpdateCustomTypeMappingInBuildKts
1818
import com.apollographql.ijplugin.refactoring.migration.v2tov3.item.UpdateEnumValueUpperCase
1919
import com.apollographql.ijplugin.refactoring.migration.v2tov3.item.UpdateFileUpload
2020
import com.apollographql.ijplugin.refactoring.migration.item.UpdateGradleDependenciesBuildKts

intellij-plugin/src/main/kotlin/com/apollographql/ijplugin/refactoring/migration/v3tov4/ApolloV3ToV4MigrationProcessor.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,17 @@ import com.apollographql.ijplugin.refactoring.migration.apollo3
66
import com.apollographql.ijplugin.refactoring.migration.item.ConstructorInsteadOfBuilder
77
import com.apollographql.ijplugin.refactoring.migration.item.RemoveMethodCall
88
import com.apollographql.ijplugin.refactoring.migration.item.UpdateClassName
9+
import com.apollographql.ijplugin.refactoring.migration.item.UpdateCustomTypeMappingInBuildKts
910
import com.apollographql.ijplugin.refactoring.migration.item.UpdateFieldName
1011
import com.apollographql.ijplugin.refactoring.migration.item.UpdateGradleDependenciesBuildKts
1112
import com.apollographql.ijplugin.refactoring.migration.item.UpdateGradleDependenciesInToml
1213
import com.apollographql.ijplugin.refactoring.migration.item.UpdateGradlePluginInBuildKts
1314
import com.apollographql.ijplugin.refactoring.migration.item.UpdateMethodCall
1415
import com.apollographql.ijplugin.refactoring.migration.item.UpdateMethodName
16+
import com.apollographql.ijplugin.refactoring.migration.v3tov4.item.EncloseInService
1517
import com.apollographql.ijplugin.refactoring.migration.v3tov4.item.RemoveWatchMethodArguments
18+
import com.apollographql.ijplugin.refactoring.migration.v3tov4.item.UpdateFieldNameInService
19+
import com.apollographql.ijplugin.refactoring.migration.v3tov4.item.UpdateMultiModuleConfiguration
1620
import com.apollographql.ijplugin.refactoring.migration.v3tov4.item.UpdateWebSocketReconnectWhen
1721
import com.intellij.openapi.project.Project
1822

@@ -73,5 +77,11 @@ class ApolloV3ToV4MigrationProcessor(project: Project) : ApolloMigrationRefactor
7377
UpdateGradlePluginInBuildKts(apollo3, apollo3, apollo4LatestVersion),
7478
UpdateGradleDependenciesInToml(apollo3, apollo3, apollo4LatestVersion),
7579
UpdateGradleDependenciesBuildKts(apollo3, apollo3),
80+
81+
UpdateFieldNameInService("generateModelBuilder", "generateModelBuilders"),
82+
UpdateFieldNameInService("generateTestBuilders", "generateDataBuilders"),
83+
UpdateCustomTypeMappingInBuildKts,
84+
UpdateMultiModuleConfiguration,
85+
EncloseInService,
7686
)
7787
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
package com.apollographql.ijplugin.refactoring.migration.v3tov4.item
2+
3+
import com.apollographql.apollo3.gradle.api.Service
4+
import com.apollographql.ijplugin.refactoring.migration.item.MigrationItem
5+
import com.apollographql.ijplugin.refactoring.migration.item.MigrationItemUsageInfo
6+
import com.apollographql.ijplugin.refactoring.migration.item.toMigrationItemUsageInfo
7+
import com.apollographql.ijplugin.util.decapitalizeFirstLetter
8+
import com.apollographql.ijplugin.util.findPsiFilesByName
9+
import com.apollographql.ijplugin.util.getMethodName
10+
import com.apollographql.ijplugin.util.lambdaBlockExpression
11+
import com.intellij.openapi.project.Project
12+
import com.intellij.psi.PsiMigration
13+
import com.intellij.psi.search.GlobalSearchScope
14+
import org.jetbrains.kotlin.psi.KtBlockExpression
15+
import org.jetbrains.kotlin.psi.KtCallExpression
16+
import org.jetbrains.kotlin.psi.KtDotQualifiedExpression
17+
import org.jetbrains.kotlin.psi.KtExpression
18+
import org.jetbrains.kotlin.psi.KtFile
19+
import org.jetbrains.kotlin.psi.KtNameReferenceExpression
20+
import org.jetbrains.kotlin.psi.KtPsiFactory
21+
import org.jetbrains.kotlin.psi.KtTreeVisitorVoid
22+
23+
object EncloseInService : MigrationItem() {
24+
override fun findUsages(project: Project, migration: PsiMigration, searchScope: GlobalSearchScope): List<MigrationItemUsageInfo> {
25+
val usages = mutableListOf<MigrationItemUsageInfo>()
26+
val buildGradleKtsFiles: List<KtFile> = project.findPsiFilesByName("build.gradle.kts", searchScope).filterIsInstance<KtFile>()
27+
for (file in buildGradleKtsFiles) {
28+
file.accept(object : KtTreeVisitorVoid() {
29+
override fun visitCallExpression(expression: KtCallExpression) {
30+
super.visitCallExpression(expression)
31+
if (expression.getMethodName() == "apollo") {
32+
val blockExpression = expression.lambdaBlockExpression() ?: return
33+
val statements = blockExpression.statements
34+
// If there's already a service call, we can't automatically refactor
35+
if (statements.none { it is KtCallExpression && it.getMethodName() == "service" }) {
36+
usages.add(blockExpression.toMigrationItemUsageInfo())
37+
}
38+
}
39+
}
40+
})
41+
}
42+
return usages
43+
}
44+
45+
private val apolloServiceSymbols: Set<String> by lazy {
46+
Service::class.java.declaredMethods.map { it.name.withoutGetter() }.toMutableSet().apply {
47+
// Include the fields that existed in v3, otherwise they'll be removed from the service block
48+
add("generateModelBuilder")
49+
add("customScalarsMapping")
50+
}
51+
}
52+
53+
private fun String.withoutGetter() = if (startsWith("get")) {
54+
substring(3).decapitalizeFirstLetter()
55+
} else {
56+
this
57+
}
58+
59+
private fun KtExpression.isApolloServiceExpression(): Boolean {
60+
val referenceExpression = when (this) {
61+
// e.g. srcDir("xxx")
62+
is KtCallExpression -> calleeExpression
63+
64+
// e.g. packageName.set("xxx")
65+
is KtDotQualifiedExpression -> receiverExpression
66+
67+
else -> return false
68+
} as? KtNameReferenceExpression ?: return false
69+
return referenceExpression.getReferencedName() in apolloServiceSymbols
70+
}
71+
72+
override fun performRefactoring(project: Project, migration: PsiMigration, usage: MigrationItemUsageInfo) {
73+
val blockExpression = usage.element as KtBlockExpression
74+
val nonServiceStatements = blockExpression.statements.filter { !it.isApolloServiceExpression() }
75+
val nonServiceStatementsText = nonServiceStatements.map { it.text }
76+
nonServiceStatements.forEach { it.delete() }
77+
val ktFactory = KtPsiFactory(project)
78+
val newBlockExpression = ktFactory.createEmptyBody()
79+
for (statement in nonServiceStatementsText) {
80+
newBlockExpression.add(ktFactory.createNewLine())
81+
newBlockExpression.add(ktFactory.createExpression(statement))
82+
}
83+
newBlockExpression.add(ktFactory.createNewLine())
84+
newBlockExpression.add(ktFactory.createExpression("service(\"service\") {\n${blockExpression.text}\n}"))
85+
newBlockExpression.lBrace?.delete()
86+
newBlockExpression.rBrace?.delete()
87+
blockExpression.replace(newBlockExpression)
88+
}
89+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package com.apollographql.ijplugin.refactoring.migration.v3tov4.item
2+
3+
import com.apollographql.ijplugin.refactoring.migration.item.MigrationItem
4+
import com.apollographql.ijplugin.refactoring.migration.item.MigrationItemUsageInfo
5+
import com.apollographql.ijplugin.refactoring.migration.item.toMigrationItemUsageInfo
6+
import com.apollographql.ijplugin.util.cast
7+
import com.apollographql.ijplugin.util.findPsiFilesByName
8+
import com.apollographql.ijplugin.util.getMethodName
9+
import com.intellij.openapi.project.Project
10+
import com.intellij.psi.PsiMigration
11+
import com.intellij.psi.search.GlobalSearchScope
12+
import org.jetbrains.kotlin.psi.KtCallExpression
13+
import org.jetbrains.kotlin.psi.KtDotQualifiedExpression
14+
import org.jetbrains.kotlin.psi.KtFile
15+
import org.jetbrains.kotlin.psi.KtNameReferenceExpression
16+
import org.jetbrains.kotlin.psi.KtPsiFactory
17+
import org.jetbrains.kotlin.psi.KtTreeVisitorVoid
18+
19+
class UpdateFieldNameInService(
20+
val oldName:String,
21+
val newName:String,
22+
) : MigrationItem() {
23+
override fun findUsages(project: Project, migration: PsiMigration, searchScope: GlobalSearchScope): List<MigrationItemUsageInfo> {
24+
val usages = mutableListOf<MigrationItemUsageInfo>()
25+
val buildGradleKtsFiles: List<KtFile> = project.findPsiFilesByName("build.gradle.kts", searchScope).filterIsInstance<KtFile>()
26+
for (file in buildGradleKtsFiles) {
27+
file.accept(object : KtTreeVisitorVoid() {
28+
override fun visitCallExpression(expression: KtCallExpression) {
29+
super.visitCallExpression(expression)
30+
if (expression.getMethodName() == "apollo") {
31+
expression.accept(object : KtTreeVisitorVoid() {
32+
override fun visitDotQualifiedExpression(expression: KtDotQualifiedExpression) {
33+
super.visitDotQualifiedExpression(expression)
34+
if (expression.receiverExpression.cast<KtNameReferenceExpression>()?.getReferencedName() == oldName) {
35+
usages.add(expression.receiverExpression.toMigrationItemUsageInfo())
36+
}
37+
}
38+
})
39+
}
40+
}
41+
})
42+
}
43+
return usages
44+
}
45+
46+
override fun performRefactoring(project: Project, migration: PsiMigration, usage: MigrationItemUsageInfo) {
47+
val element = usage.element
48+
element.replace(KtPsiFactory(project).createExpression(newName))
49+
}
50+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package com.apollographql.ijplugin.refactoring.migration.v3tov4.item
2+
3+
import com.apollographql.ijplugin.refactoring.migration.item.MigrationItem
4+
import com.apollographql.ijplugin.refactoring.migration.item.MigrationItemUsageInfo
5+
import com.apollographql.ijplugin.refactoring.migration.item.toMigrationItemUsageInfo
6+
import com.apollographql.ijplugin.util.containingKtFile
7+
import com.apollographql.ijplugin.util.findPsiFilesByName
8+
import com.apollographql.ijplugin.util.getMethodName
9+
import com.apollographql.ijplugin.util.lambdaBlockExpression
10+
import com.intellij.openapi.project.Project
11+
import com.intellij.psi.PsiMigration
12+
import com.intellij.psi.search.GlobalSearchScope
13+
import org.jetbrains.kotlin.psi.KtCallExpression
14+
import org.jetbrains.kotlin.psi.KtFile
15+
import org.jetbrains.kotlin.psi.KtPsiFactory
16+
import org.jetbrains.kotlin.psi.KtTreeVisitorVoid
17+
18+
object UpdateMultiModuleConfiguration : MigrationItem() {
19+
override fun findUsages(project: Project, migration: PsiMigration, searchScope: GlobalSearchScope): List<MigrationItemUsageInfo> {
20+
val usages = mutableListOf<MigrationItemUsageInfo>()
21+
val buildGradleKtsFiles: List<KtFile> = project.findPsiFilesByName("build.gradle.kts", searchScope).filterIsInstance<KtFile>()
22+
for (file in buildGradleKtsFiles) {
23+
file.accept(object : KtTreeVisitorVoid() {
24+
override fun visitCallExpression(expression: KtCallExpression) {
25+
super.visitCallExpression(expression)
26+
if (expression.getMethodName() == "apolloMetadata") {
27+
val argumentText = expression.valueArguments.firstOrNull()?.text ?: return
28+
usages.add(expression.toMigrationItemUsageInfo(argumentText))
29+
}
30+
}
31+
})
32+
}
33+
return usages
34+
}
35+
36+
override fun performRefactoring(project: Project, migration: PsiMigration, usage: MigrationItemUsageInfo) {
37+
val file = usage.element.containingKtFile() ?: return
38+
usage.element.delete()
39+
val ktFactory = KtPsiFactory(project)
40+
41+
file.accept(object : KtTreeVisitorVoid() {
42+
override fun visitCallExpression(expression: KtCallExpression) {
43+
super.visitCallExpression(expression)
44+
var serviceFound= false
45+
if (expression.getMethodName() == "apollo") {
46+
val apolloBlockExpression = expression.lambdaBlockExpression() ?: return
47+
val apolloStatements = apolloBlockExpression.statements
48+
apolloStatements.filter { it is KtCallExpression && it.getMethodName() == "service" }.forEach { serviceCallExpression ->
49+
val serviceBlockExpression = (serviceCallExpression as KtCallExpression).lambdaBlockExpression() ?: return@forEach
50+
serviceBlockExpression.add(ktFactory.createNewLine())
51+
serviceBlockExpression.add(ktFactory.createExpression("dependsOn(${usage.attachedData<String>()})"))
52+
serviceFound = true
53+
}
54+
if (!serviceFound) {
55+
apolloBlockExpression.add(ktFactory.createNewLine())
56+
apolloBlockExpression.add(ktFactory.createExpression("dependsOn(${usage.attachedData<String>()})"))
57+
}
58+
}
59+
}
60+
})
61+
}
62+
}

intellij-plugin/src/main/kotlin/com/apollographql/ijplugin/util/Psi.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,14 @@ import com.intellij.openapi.diagnostic.ControlFlowException
44
import com.intellij.psi.PsiElement
55
import com.intellij.psi.util.PsiTreeUtil
66
import org.jetbrains.kotlin.idea.references.KtSimpleNameReference
7+
import org.jetbrains.kotlin.psi.KtBlockExpression
8+
import org.jetbrains.kotlin.psi.KtCallExpression
79
import org.jetbrains.kotlin.psi.KtClass
810
import org.jetbrains.kotlin.psi.KtConstructor
911
import org.jetbrains.kotlin.psi.KtFile
1012
import org.jetbrains.kotlin.psi.KtImportList
13+
import org.jetbrains.kotlin.psi.KtLambdaArgument
14+
import org.jetbrains.kotlin.psi.KtNameReferenceExpression
1115
import org.jetbrains.kotlin.psi.psiUtil.containingClass
1216
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
1317
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
@@ -58,3 +62,7 @@ fun PsiElement.resolveKtName(): PsiElement? = runCatching {
5862
fun PsiElement.asKtClass(): KtClass? = cast<KtClass>() ?: cast<KtConstructor<*>>()?.containingClass()
5963

6064
fun PsiElement.originalClassName(): String? = resolveKtName()?.asKtClass()?.name
65+
66+
fun KtCallExpression.getMethodName(): String? = calleeExpression.cast<KtNameReferenceExpression>()?.getReferencedName()
67+
68+
fun KtCallExpression.lambdaBlockExpression(): KtBlockExpression? = valueArguments.firstIsInstanceOrNull<KtLambdaArgument>()?.getLambdaExpression()?.bodyExpression

intellij-plugin/src/main/resources/META-INF/plugin.xml

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@
146146
/>
147147
</applicationListeners>
148148

149+
149150
<actions>
150151
<!-- Refactor / Apollo -->
151152
<!--suppress PluginXmlCapitalization -->
@@ -157,43 +158,45 @@
157158
<add-to-group group-id="RefactoringMenu" anchor="last" />
158159
</group>
159160

160-
<!-- Refactor / Apollo / Migrate to Apollo Kotlin 3 -->
161+
<!-- Tools / Apollo -->
162+
<!--suppress PluginXmlCapitalization -->
163+
<group
164+
id="ApolloToolsActionGroup"
165+
popup="true"
166+
icon="com.apollographql.ijplugin.icons.ApolloIcons.Action.ApolloColor"
167+
class="com.apollographql.ijplugin.action.ApolloToolsActionGroup"
168+
>
169+
<add-to-group group-id="ToolsMenu" anchor="last" />
170+
</group>
171+
172+
<!-- Refactor / Apollo / Migrate to Apollo Kotlin 3 (also in Tools / Apollo) -->
161173
<action
162174
id="ApolloV2ToV3MigrationAction"
163175
class="com.apollographql.ijplugin.action.ApolloV2ToV3MigrationAction"
164176
>
165177
<add-to-group group-id="ApolloRefactorActionGroup" />
178+
<add-to-group group-id="ApolloToolsActionGroup" />
166179
</action>
167180

168-
<!-- Refactor / Apollo / Migrate to Apollo Kotlin 4 -->
181+
<!-- Refactor / Apollo / Migrate to Apollo Kotlin 4 (also in Tools / Apollo) -->
169182
<action
170183
id="ApolloV3ToV4MigrationAction"
171184
class="com.apollographql.ijplugin.action.ApolloV3ToV4MigrationAction"
172185
>
173186
<add-to-group group-id="ApolloRefactorActionGroup" />
187+
<add-to-group group-id="ApolloToolsActionGroup" />
174188
</action>
175189

176-
<!-- Refactor / Apollo / Migrate to operationBased Codegen -->
190+
<!-- Refactor / Apollo / Migrate to operationBased Codegen (also in Tools / Apollo) -->
177191
<!--suppress PluginXmlCapitalization -->
178192
<action
179193
id="CompatToOperationBasedCodegenMigrationAction"
180194
class="com.apollographql.ijplugin.action.CompatToOperationBasedCodegenMigrationAction"
181195
>
182196
<add-to-group group-id="ApolloRefactorActionGroup" />
197+
<add-to-group group-id="ApolloToolsActionGroup" />
183198
</action>
184199

185-
186-
<!-- Tools / Apollo -->
187-
<!--suppress PluginXmlCapitalization -->
188-
<group
189-
id="ApolloToolsActionGroup"
190-
popup="true"
191-
icon="com.apollographql.ijplugin.icons.ApolloIcons.Action.ApolloColor"
192-
class="com.apollographql.ijplugin.action.ApolloToolsActionGroup"
193-
>
194-
<add-to-group group-id="ToolsMenu" anchor="last" />
195-
</group>
196-
197200
<!-- Tools / Apollo / Open in Apollo Sandbox -->
198201
<action
199202
id="OpenInSandboxAction"

0 commit comments

Comments
 (0)