Skip to content

Commit

Permalink
feat: ✨track quickfixes [ROAD-688] (#311)
Browse files Browse the repository at this point in the history
* chore: 🔧 regenerate events with latest tracking events [ROAD-688]

* feat: ✨ track display and invocation of quickfixes [ROAD-688]

* chore: ♻️ optimize imports [ROAD-688]

* chore: 📝 update changelog [ROAD-688]

* chore: also track quickfixes for container annotator, implement PR suggestions [ROAD-688]

* feat: ✨ add snyk icon to popup menu of quickfixes [ROAD-688]

* fix: 🐛 log message [ROAD-688]
  • Loading branch information
bastiandoetsch authored Feb 22, 2022
1 parent 6fcdd5e commit 6271301
Show file tree
Hide file tree
Showing 26 changed files with 729 additions and 222 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
- Snyk Open Source: added editor annotations for Maven, NPM, and Kotlin Gradle
- Snyk Open Source: added quickfix capability for package managers
- Snyk Code: Annotations if plugins for the language are installed
- Add amplitude events to quickfix display and invocations

### Fixed

Expand Down
5 changes: 3 additions & 2 deletions ampli.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"SourceId": "f2e16b49-6653-4183-abba-149fa04165fb",
"Path": "./src/main/java/snyk/analytics",
"Branch": "main",
"Version": "100.0.0",
"OrgId": "114775"
"Version": "137.0.0",
"OrgId": "114775",
"Runtime": "jre:java-v3"
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package snyk.common.intentionactions

import com.intellij.openapi.application.WriteAction
import com.intellij.openapi.components.service
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.util.TextRange
import com.intellij.openapi.vfs.VirtualFile
Expand All @@ -9,7 +10,9 @@ import com.intellij.testFramework.fixtures.BasePlatformTestCase
import io.mockk.every
import io.mockk.mockk
import io.mockk.unmockkAll
import io.mockk.verify
import io.snyk.plugin.pluginSettings
import io.snyk.plugin.services.SnykAnalyticsService
import org.junit.Before
import org.junit.Test
import java.nio.file.Paths
Expand All @@ -24,6 +27,7 @@ class AlwaysAvailableReplacementIntentionActionTest : BasePlatformTestCase() {
private lateinit var cut: AlwaysAvailableReplacementIntentionAction

private val fileName = "package.json"
private val analyticsMock = mockk<SnykAnalyticsService>(relaxed = true)

private lateinit var psiFile: PsiFile

Expand All @@ -43,7 +47,13 @@ class AlwaysAvailableReplacementIntentionActionTest : BasePlatformTestCase() {
pluginSettings().fileListenerEnabled = false
file = myFixture.copyFileToProject(fileName)
psiFile = WriteAction.computeAndWait<PsiFile, Throwable> { psiManager.findFile(file)!! }
cut = AlwaysAvailableReplacementIntentionAction(range, replacementText, intentionText, familyName)
cut = AlwaysAvailableReplacementIntentionAction(
range,
replacementText,
intentionText,
familyName,
analyticsService = analyticsMock
)
}

@Suppress("SwallowedException", "TooGenericExceptionCaught")
Expand All @@ -59,6 +69,7 @@ class AlwaysAvailableReplacementIntentionActionTest : BasePlatformTestCase() {
@Test
fun `test getText`() {
assertEquals(intentionText + replacementText, cut.text)
verify { analyticsMock.logQuickFixIsDisplayed(any()) }
}

@Test
Expand All @@ -76,5 +87,6 @@ class AlwaysAvailableReplacementIntentionActionTest : BasePlatformTestCase() {
cut.invoke(project, editor, psiFile)

assertEquals("b", doc.text)
verify { analyticsMock.logQuickFixIsTriggered(any()) }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package snyk.container.annotator

import com.google.gson.Gson
import com.intellij.openapi.application.WriteAction
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.util.TextRange
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.PsiFile
import com.intellij.testFramework.fixtures.BasePlatformTestCase
import io.mockk.every
import io.mockk.mockk
import io.mockk.unmockkAll
import io.mockk.verify
import io.snyk.plugin.getContainerService
import io.snyk.plugin.pluginSettings
import io.snyk.plugin.services.SnykAnalyticsService
import org.junit.Before
import org.junit.Test
import snyk.container.ContainerIssuesForImage
import snyk.container.ContainerResult
import java.nio.file.Paths

@Suppress("FunctionName")
class BaseImageRemediationFixTest : BasePlatformTestCase() {
private lateinit var file: VirtualFile
private val range = TextRange(/* startOffset = */ 0,/* endOffset = */ 4)
private val familyName = "Snyk Container"
private lateinit var cut: BaseImageRemediationFix

private val fileName = "package.json"
private val analyticsMock = mockk<SnykAnalyticsService>(relaxed = true)

private lateinit var psiFile: PsiFile

override fun getTestDataPath(): String {
val resource = BaseImageRemediationFixTest::class.java
.getResource("/test-fixtures/oss/annotator")
requireNotNull(resource) { "Make sure that the resource $resource exists!" }
return Paths.get(resource.toURI()).toString()
}

override fun isWriteActionRequired(): Boolean = true

@Before
override fun setUp() {
super.setUp()
unmockkAll()
pluginSettings().fileListenerEnabled = false
file = myFixture.copyFileToProject(fileName)
psiFile = WriteAction.computeAndWait<PsiFile, Throwable> { psiManager.findFile(file)!! }
val containerIssuesForImage = createContainerIssuesForImage()
cut = BaseImageRemediationFix(
containerIssuesForImage,
range,
analyticsService = analyticsMock
)
}

@Suppress("SwallowedException", "TooGenericExceptionCaught")
override fun tearDown() {
try {
unmockkAll()
} finally {
super.tearDown()
pluginSettings().fileListenerEnabled = true
}
}

@Test
fun `test getText`() {
assertEquals("Upgrade Image to nginx:1.21.4", cut.text)
verify { analyticsMock.logQuickFixIsDisplayed(any()) }
}

@Test
fun `test familyName`() {
assertEquals(familyName, cut.familyName)
}

@Test
fun `test invoke`() {
val doc = psiFile.viewProvider.document
doc!!.setText("abcd")
val editor = mockk<Editor>()
every { editor.document } returns doc

cut.invoke(project, editor, psiFile)

assertEquals("nginx:1.21.4", doc.text)
verify { analyticsMock.logQuickFixIsTriggered(any()) }
}

private val containerResultWithRemediationJson =
javaClass.classLoader.getResource("container-test-results/nginx-with-remediation.json")!!
.readText(Charsets.UTF_8)

private fun createContainerIssuesForImage(): ContainerIssuesForImage {
val containerResult = ContainerResult(
listOf(Gson().fromJson(containerResultWithRemediationJson, ContainerIssuesForImage::class.java)), null
)

val firstContainerIssuesForImage = containerResult.allCliIssues!![0]
val baseImageRemediationInfo =
getContainerService(project)?.convertRemediation(firstContainerIssuesForImage.docker.baseImageRemediation)
return firstContainerIssuesForImage.copy(
baseImageRemediationInfo = baseImageRemediationInfo,
workloadImages = emptyList()
)
}
}
47 changes: 22 additions & 25 deletions src/main/java/snyk/analytics/AnalysisIsReady.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,46 +13,43 @@
import java.util.HashMap;

public class AnalysisIsReady extends Event {
private static final String NAME = "Analysis Is Ready";
private static final String ID = "c9337edb-27a3-416e-a654-092fa4375feb";
private static final String VERSION = "2.0.0";
private static final String NAME = "Analysis Is Ready";
private static final String ID = "c9337edb-27a3-416e-a654-092fa4375feb";
private static final String VERSION = "2.0.2";

public enum Ide {
VISUAL_STUDIO_CODE("Visual Studio Code"), VISUAL_STUDIO("Visual Studio"), ECLIPSE("Eclipse"), JETBRAINS("JetBrains");
public enum AnalysisType {
SNYK_ADVISOR("Snyk Advisor"), SNYK_CODE_QUALITY("Snyk Code Quality"), SNYK_CODE_SECURITY("Snyk Code Security"), SNYK_OPEN_SOURCE("Snyk Open Source"), SNYK_CONTAINER("Snyk Container"), SNYK_INFRASTRUCTURE_AS_CODE("Snyk Infrastructure as Code");

private String ide;
private final String analysisType;

public String getIde()
{
return this.ide;
}
public String getAnalysisType() {
return this.analysisType;
}

Ide(String ide)
{
this.ide = ide;
}
AnalysisType(String analysisType) {
this.analysisType = analysisType;
}
}

public enum AnalysisType {
SNYK_ADVISOR("Snyk Advisor"), SNYK_CODE_QUALITY("Snyk Code Quality"), SNYK_CODE_SECURITY("Snyk Code Security"), SNYK_OPEN_SOURCE("Snyk Open Source"), SNYK_CONTAINER("Snyk Container"), SNYK_INFRASTRUCTURE_AS_CODE("Snyk Infrastructure as Code");
public enum Ide {
VISUAL_STUDIO_CODE("Visual Studio Code"), VISUAL_STUDIO("Visual Studio"), ECLIPSE("Eclipse"), JETBRAINS("JetBrains");

private String analysisType;
private final String ide;

public String getAnalysisType()
{
return this.analysisType;
}
public String getIde() {
return this.ide;
}

AnalysisType(String analysisType)
Ide(String ide)
{
this.analysisType = analysisType;
this.ide = ide;
}
}

public enum Result {
SUCCESS("Success"), ERROR("Error");

private String result;
private final String result;

public String getResult()
{
Expand Down Expand Up @@ -81,7 +78,7 @@ public AnalysisIsReady clone() {

// Inner Builder class with required properties
public static class Builder implements IAnalysisType, IIde, IResult, IBuild {
private final HashMap<String, Object> properties = new HashMap<String, Object>();
private final HashMap<String, Object> properties = new HashMap<>();

private Builder() {
this.properties.put("itly", true);
Expand Down
6 changes: 3 additions & 3 deletions src/main/java/snyk/analytics/AnalysisIsTriggered.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@
public class AnalysisIsTriggered extends Event {
private static final String NAME = "Analysis Is Triggered";
private static final String ID = "dabf569e-219c-470f-8e31-6e029723f0cd";
private static final String VERSION = "2.0.0";
private static final String VERSION = "2.0.2";

public enum Ide {
VISUAL_STUDIO_CODE("Visual Studio Code"), VISUAL_STUDIO("Visual Studio"), ECLIPSE("Eclipse"), JETBRAINS("JetBrains");

private String ide;
private final String ide;

public String getIde()
{
Expand Down Expand Up @@ -49,7 +49,7 @@ public AnalysisIsTriggered clone() {

// Inner Builder class with required properties
public static class Builder implements IAnalysisType, IIde, ITriggeredByUser, IBuild {
private final HashMap<String, Object> properties = new HashMap<String, Object>();
private final HashMap<String, Object> properties = new HashMap<>();

private Builder() {
this.properties.put("itly", true);
Expand Down
32 changes: 16 additions & 16 deletions src/main/java/snyk/analytics/AuthenticateButtonIsClicked.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,7 @@
public class AuthenticateButtonIsClicked extends Event {
private static final String NAME = "Authenticate Button Is Clicked";
private static final String ID = "2220c25f-ba76-4d5b-92f7-6d0e1c6165be";
private static final String VERSION = "1.0.0";

public enum Ide {
VISUAL_STUDIO_CODE("Visual Studio Code"), VISUAL_STUDIO("Visual Studio"), ECLIPSE("Eclipse"), JETBRAINS("JetBrains");

private final String ide;

public String getIde() {
return this.ide;
}

Ide(String ide) {
this.ide = ide;
}
}
private static final String VERSION = "1.0.2";

public enum EventSource {
ADVISOR("Advisor"), APP("App"), LEARN("Learn"), IDE("IDE");
Expand All @@ -45,6 +31,20 @@ public String getEventSource() {
}
}

public enum Ide {
VISUAL_STUDIO_CODE("Visual Studio Code"), VISUAL_STUDIO("Visual Studio"), ECLIPSE("Eclipse"), JETBRAINS("JetBrains");

private final String ide;

public String getIde() {
return this.ide;
}

Ide(String ide) {
this.ide = ide;
}
}

private AuthenticateButtonIsClicked(Builder builder) {
super(NAME, builder.properties, ID, VERSION);
}
Expand All @@ -63,7 +63,7 @@ public static IIde builder() {

// Inner Builder class with required properties
public static class Builder implements IIde, IBuild {
private final HashMap<String, Object> properties = new HashMap<String, Object>();
private final HashMap<String, Object> properties = new HashMap<>();

private Builder() {
this.properties.put("itly", true);
Expand Down
9 changes: 5 additions & 4 deletions src/main/java/snyk/analytics/DestinationsOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,14 @@ public static IBuild builder() {
}

public static class Builder implements IBuild {
private Builder() {}
private Builder() {
}


public DestinationsOptions build() {
return new DestinationsOptions(this);
}
public DestinationsOptions build() {
return new DestinationsOptions(this);
}
}

public interface IBuild {
DestinationsOptions build();
Expand Down
19 changes: 9 additions & 10 deletions src/main/java/snyk/analytics/Group.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,18 @@
import java.util.HashMap;

public class Group extends Event {
private static final String NAME = "group";
private static final String NAME = "group";
private static final String ID = "group";
private static final String VERSION = "100.0.0";
private static final String VERSION = "137.0.0";

public enum GroupType {
ORG("org"), GROUP("group"), ACCOUNT("account");
public enum GroupType {
ORG("org"), GROUP("group"), ACCOUNT("account");

private final String groupType;
private final String groupType;

public String getGroupType()
{
return this.groupType;
}
public String getGroupType() {
return this.groupType;
}

GroupType(String groupType)
{
Expand All @@ -49,7 +48,7 @@ public Group clone() {

// Inner Builder class with required properties
public static class Builder implements IBuild {
private final HashMap<String, Object> properties = new HashMap<String, Object>();
private final HashMap<String, Object> properties = new HashMap<>();

private Builder() {

Expand Down
Loading

0 comments on commit 6271301

Please sign in to comment.