Skip to content

Commit

Permalink
[IJ Plugin] Add inspection to warn about the presence of a GraphQL co…
Browse files Browse the repository at this point in the history
…nfig file (#5908)

* Add inspection to warn about the presence of a GraphQL config file

* Add v3 -> v4 migration to delete GraphQL config files

* Add tests

* Tweak wording

* Change severity to warning and tweak messages

* Fix test

* Update intellij-plugin/src/main/resources/inspectionDescriptions/ApolloGraphQLConfigFilePresent.html

Co-authored-by: Martin Bonnin <martin@mbonnin.net>

---------

Co-authored-by: Martin Bonnin <martin@mbonnin.net>
  • Loading branch information
BoD and martinbonnin authored May 29, 2024
1 parent d160e98 commit 3bd84c7
Show file tree
Hide file tree
Showing 11 changed files with 235 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package com.apollographql.ijplugin.inspection

import com.apollographql.ijplugin.ApolloBundle
import com.apollographql.ijplugin.project.apolloProjectService
import com.apollographql.ijplugin.settings.projectSettingsState
import com.apollographql.ijplugin.telemetry.TelemetryEvent
import com.apollographql.ijplugin.telemetry.telemetryService
import com.intellij.codeInsight.intention.preview.IntentionPreviewInfo
import com.intellij.codeInsight.intention.preview.IntentionPreviewUtils
import com.intellij.codeInspection.LocalInspectionTool
import com.intellij.codeInspection.LocalQuickFix
import com.intellij.codeInspection.ProblemDescriptor
import com.intellij.codeInspection.ProblemsHolder
import com.intellij.lang.annotation.AnnotationHolder
import com.intellij.lang.annotation.Annotator
import com.intellij.lang.annotation.HighlightSeverity
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Condition
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiElementVisitor

private val graphQLConfigFileNames = setOf(
"graphql.config.json",
"graphql.config.js",
"graphql.config.cjs",
"graphql.config.ts",
"graphql.config.yaml",
"graphql.config.yml",
".graphqlrc",
".graphqlrc.json",
".graphqlrc.yaml",
".graphqlrc.yml",
".graphqlrc.js",
".graphqlrc.ts"
)

class ApolloGraphQLConfigFilePresentInspection : LocalInspectionTool() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
return object : PsiElementVisitor() {
override fun visitElement(element: PsiElement) {
if (!element.project.apolloProjectService.apolloVersion.isAtLeastV4 || !element.project.projectSettingsState.contributeConfigurationToGraphqlPlugin) return
if (element.containingFile.name in graphQLConfigFileNames && element.containingFile == element) {
holder.registerProblem(element, ApolloBundle.message("inspection.graphQLConfigFilePresent.reportText"), ApolloGraphQLConfigFilePresentQuickFix(element.containingFile.name))
}
}
}
}
}

private class ApolloGraphQLConfigFilePresentQuickFix(private val fileName: String) : LocalQuickFix {
override fun getName(): String {
return ApolloBundle.message("inspection.graphQLConfigFilePresent.quickFix", fileName)
}

override fun getFamilyName() = name

override fun availableInBatchMode() = false

override fun generatePreview(project: Project, previewDescriptor: ProblemDescriptor): IntentionPreviewInfo = IntentionPreviewInfo.EMPTY

override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
if (!IntentionPreviewUtils.isIntentionPreviewActive()) project.telemetryService.logEvent(TelemetryEvent.ApolloIjGraphQLConfigFilePresentQuickFix())
val psiFile = descriptor.psiElement.containingFile
psiFile.virtualFile.delete(this)
}
}

class ApolloGraphQLConfigFilePresentAnnotator : Annotator {
override fun annotate(element: PsiElement, holder: AnnotationHolder) {
if (!element.project.apolloProjectService.apolloVersion.isAtLeastV4 || !element.project.projectSettingsState.contributeConfigurationToGraphqlPlugin) return
if (element.containingFile.name in graphQLConfigFileNames && element.containingFile == element) {
holder.newAnnotation(HighlightSeverity.WARNING, ApolloBundle.message("inspection.graphQLConfigFilePresent.reportText"))
.range(element)
.create()
}
}
}

class GraphQLConfigFileFilter : Condition<VirtualFile> {
override fun value(virtualFile: VirtualFile): Boolean {
return virtualFile.name in graphQLConfigFileNames
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import com.apollographql.ijplugin.refactoring.migration.item.UpdateMethodName
import com.apollographql.ijplugin.refactoring.migration.v3tov4.item.AddLinkDirective
import com.apollographql.ijplugin.refactoring.migration.v3tov4.item.EncloseInService
import com.apollographql.ijplugin.refactoring.migration.v3tov4.item.RemoveFieldInService
import com.apollographql.ijplugin.refactoring.migration.v3tov4.item.RemoveGraphqlConfigFiles
import com.apollographql.ijplugin.refactoring.migration.v3tov4.item.RemoveMethodInService
import com.apollographql.ijplugin.refactoring.migration.v3tov4.item.RemoveWatchMethodArguments
import com.apollographql.ijplugin.refactoring.migration.v3tov4.item.UpdateEnumClassUpperCase
Expand Down Expand Up @@ -99,5 +100,8 @@ class ApolloV3ToV4MigrationProcessor(project: Project) : ApolloMigrationRefactor

// Add @link to extra.graphqls
AddLinkDirective,

// Graphql Config
RemoveGraphqlConfigFiles,
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.apollographql.ijplugin.refactoring.migration.v3tov4.item

import com.apollographql.ijplugin.refactoring.migration.item.DeletesElements
import com.apollographql.ijplugin.refactoring.migration.item.MigrationItem
import com.apollographql.ijplugin.refactoring.migration.item.MigrationItemUsageInfo
import com.apollographql.ijplugin.refactoring.migration.item.toMigrationItemUsageInfo
import com.apollographql.ijplugin.util.findPsiFilesByName
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiMigration
import com.intellij.psi.search.GlobalSearchScope

private val graphQLConfigFileNames = setOf(
"graphql.config.json",
"graphql.config.js",
"graphql.config.cjs",
"graphql.config.ts",
"graphql.config.yaml",
"graphql.config.yml",
".graphqlrc",
".graphqlrc.json",
".graphqlrc.yaml",
".graphqlrc.yml",
".graphqlrc.js",
".graphqlrc.ts"
)

object RemoveGraphqlConfigFiles : MigrationItem(), DeletesElements {
override fun findUsages(project: Project, migration: PsiMigration, searchScope: GlobalSearchScope): List<MigrationItemUsageInfo> {
return graphQLConfigFileNames.flatMap {
project.findPsiFilesByName(it, searchScope)
}.map { it.toMigrationItemUsageInfo() }
}

override fun performRefactoring(project: Project, migration: PsiMigration, usage: MigrationItemUsageInfo) {
usage.element.delete()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,11 @@ sealed class TelemetryEvent(
*/
class ApolloIjApollo4AvailableQuickFix : TelemetryEvent("akij_apollo4_available_quickfix", null)

/**
* User applied the quickfix for the 'GraphQL config file present' inspection of the Apollo Kotlin IntelliJ plugin.
*/
class ApolloIjGraphQLConfigFilePresentQuickFix : TelemetryEvent("akij_graphql_config_file_present_quickfix", null)

/**
* User applied the quickfix for the 'Endpoint not configured' inspection of the Apollo Kotlin IntelliJ plugin.
*/
Expand Down
33 changes: 33 additions & 0 deletions intellij-plugin/src/main/resources/META-INF/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,39 @@
level="WARNING"
/>

<!-- "GraphQL config file present" inspection -->
<!--suppress PluginXmlCapitalization, PluginXmlExtensionRegistration -->
<localInspection
implementationClass="com.apollographql.ijplugin.inspection.ApolloGraphQLConfigFilePresentInspection"
groupPathKey="inspection.group.graphql"
groupKey="inspection.group.graphql.apolloKotlin"
key="inspection.graphQLConfigFilePresent.displayName"
enabledByDefault="true"
level="WARNING"
/>

<annotator
language="yaml"
implementationClass="com.apollographql.ijplugin.inspection.ApolloGraphQLConfigFilePresentAnnotator"/>

<annotator
language="JSON"
implementationClass="com.apollographql.ijplugin.inspection.ApolloGraphQLConfigFilePresentAnnotator"/>

<annotator
language="TEXT"
implementationClass="com.apollographql.ijplugin.inspection.ApolloGraphQLConfigFilePresentAnnotator"/>

<annotator
language="TypeScript"
implementationClass="com.apollographql.ijplugin.inspection.ApolloGraphQLConfigFilePresentAnnotator"/>

<annotator
language="ECMAScript 6"
implementationClass="com.apollographql.ijplugin.inspection.ApolloGraphQLConfigFilePresentAnnotator"/>

<problemFileHighlightFilter implementation="com.apollographql.ijplugin.inspection.GraphQLConfigFileFilter"/>

<!-- "Change input class constructor to builder" intention -->
<intentionAction>
<language>kotlin</language>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<html>
<body>
Suggests to delete GraphQL config files on projects using Apollo Kotlin 4.
<p>
It is no longer useful to have a GraphQL config file, as the Apollo plugin automatically provides the location of your
project's operations and schema files to the GraphQL plugin, <br>
</p>
<p>
Note: the Apollo plugin uses the Gradle tooling API to retrieve the project's GraphQL configuration.
</p>
<p>
<a href="https://www.apollographql.com/docs/kotlin/v4/testing/android-studio-plugin#integration-with-the-graphql-intellij-plugin">More information</a>
</p>
</body>
</html>
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,10 @@ inspection.missingGraphQLDefinitionImport.reportText.input=input object
inspection.missingGraphQLDefinitionImport.reportText.scalar=scalar
inspection.missingGraphQLDefinitionImport.quickFix=Import {0} {1}

inspection.graphQLConfigFilePresent.displayName=GraphQL config file present
inspection.graphQLConfigFilePresent.reportText=The Apollo plugin retrieves the GraphQL configuration from Gradle and doesn't use the GraphQL config file
inspection.graphQLConfigFilePresent.quickFix=Delete the {0} file
inspection.more=More...
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.apollographql.ijplugin.inspection

import com.apollographql.ijplugin.ApolloTestCase
import com.intellij.testFramework.TestDataPath
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4

@TestDataPath("\$CONTENT_ROOT/testData/inspection")
@RunWith(JUnit4::class)
class ApolloGraphQLConfigFilePresentInspectionTest : ApolloTestCase() {

override fun getTestDataPath() = "src/test/testData/inspection"

@Throws(Exception::class)
override fun setUp() {
super.setUp()
myFixture.enableInspections(ApolloGraphQLConfigFilePresentInspection())
}

@Test
fun testGraphqlConfigPresenceError() {
myFixture.configureByFile("graphql.config.yml")
checkHighlighting()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.apollographql.ijplugin.migration
import com.apollographql.ijplugin.ApolloTestCase
import com.apollographql.ijplugin.refactoring.migration.v3tov4.ApolloV3ToV4MigrationProcessor
import com.intellij.testFramework.TestDataPath
import junit.framework.TestCase
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
Expand Down Expand Up @@ -52,6 +53,13 @@ class ApolloV3ToV4MigrationTest : ApolloTestCase() {
@Test
fun testAddLinkDirective() = runMigration(extension = "graphqls", fileNameInProject = "extra.graphqls")

@Test
fun testRemoveGraphqlConfigFiles() {
myFixture.copyFileToProject("graphql.config.yml")
ApolloV3ToV4MigrationProcessor(project).run()
TestCase.assertNull(myFixture.tempDirFixture.getFile("graphql.config.yml"))
}

private fun runMigration(extension: String = "kt", fileNameInProject: String? = null) {
val fileBaseName = getTestName(true)
if (fileNameInProject != null) {
Expand Down
11 changes: 11 additions & 0 deletions intellij-plugin/src/test/testData/inspection/graphql.config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<warning descr="The Apollo plugin retrieves the GraphQL configuration from Gradle and doesn't use the GraphQL config file" textAttributesKey="WARNING_ATTRIBUTES"><warning descr="The Apollo plugin retrieves the GraphQL configuration from Gradle and doesn't use the GraphQL config file" textAttributesKey="WARNING_ATTRIBUTES">
projects:
main:
schema: src/main/graphql/schema.graphqls
documents: '**/*.graphql'
extensions:
endpoints:
main: "https://apollo-fullstack-tutorial.herokuapp.com/graphql"
apollo-key: ${APOLLO_KEY}

</warning></warning>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
projects:
main:
schema: src/main/graphql/schema.graphqls
documents: '**/*.graphql'
extensions:
endpoints:
main: "https://apollo-fullstack-tutorial.herokuapp.com/graphql"
apollo-key: ${APOLLO_KEY}

0 comments on commit 3bd84c7

Please sign in to comment.