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’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[IJ Plugin] Add inspection to warn about the presence of a GraphQL config file #5908

Merged
merged 7 commits into from
May 29, 2024
Merged
Show file tree
Hide file tree
Changes from 6 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
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 will automatically provide the location of your
project's operations and schema files to the GraphQL plugin, <br>
BoD marked this conversation as resolved.
Show resolved Hide resolved
</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}
Loading