Skip to content

Commit

Permalink
feat: add plugin installed event [IDE-736] (#211)
Browse files Browse the repository at this point in the history
* feat: add plugin installed event

* feat: add plugin installed analytics event and base url setting

* fix: improve startup

* docs: updated changelog

* fix: improve startup wizard

* fix: tests

* fix: tests

* fix: check if test in token refresher

* chore: add test for plugin sending

* chore: add second test for plugin install sending

* chore: optimize import

* chore: revert log level to info
  • Loading branch information
bastiandoetsch authored Nov 8, 2024
1 parent be26ddb commit 4ed6db1
Show file tree
Hide file tree
Showing 19 changed files with 1,010 additions and 649 deletions.
2 changes: 1 addition & 1 deletion .classpath
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-17">
<attributes>
<attribute name="module" value="true"/>
</attributes>
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
## [Unreleased]
### Changes
- process api URL from hasAuthenticated message
- add release channel preference to select which CLI is downloaded
- added plugin installed event and analytics sender

## [2.2.0] - v20241024.154007
### Changes
Expand Down
14 changes: 7 additions & 7 deletions plugin/src/main/java/io/snyk/eclipse/plugin/SnykStartup.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,10 @@
import io.snyk.languageserver.download.LsDownloader;

public class SnykStartup implements IStartup {
private LsRuntimeEnvironment runtimeEnvironment;
private static LsRuntimeEnvironment runtimeEnvironment;
private SnykView snykView = null;
private static boolean downloading = true;
private ILog logger;
private static ILog logger;

private static SnykStartup instance;

Expand Down Expand Up @@ -69,7 +69,8 @@ protected IStatus run(IProgressMonitor monitor) {
startLanguageServer();

PlatformUI.getWorkbench().getDisplay().syncExec(() -> {
if (Preferences.getInstance().getAuthToken().isBlank()) {
Preferences prefs = Preferences.getInstance();
if (prefs.getAuthToken().isBlank() && !prefs.isTest()) {
monitor.subTask("Starting Snyk Wizard to configure initial settings...");
SnykWizard wizard = new SnykWizard();
WizardDialog dialog = new WizardDialog(PlatformUI.getWorkbench().getDisplay().getActiveShell(), wizard);
Expand All @@ -83,9 +84,8 @@ protected IStatus run(IProgressMonitor monitor) {
}

private void startLanguageServer() {
var definition = LanguageServersRegistry.getInstance().getDefinition(SnykLanguageServer.LANGUAGE_SERVER_ID);
try {
LanguageServiceAccessor.startLanguageServer(definition);
SnykLanguageServer.startSnykLanguageServer();
} catch (RuntimeException e) {
logError(e);
}
Expand Down Expand Up @@ -141,12 +141,12 @@ private boolean downloadLS() {
return true;
}

LsDownloader getLsDownloader() throws URISyntaxException {
static LsDownloader getLsDownloader() throws URISyntaxException {
return new LsDownloader(HttpClientFactory.getInstance(), runtimeEnvironment, logger);
}

@SuppressWarnings("ResultOfMethodCallIgnored")
IStatus download(IProgressMonitor monitor) {
public static IStatus download(IProgressMonitor monitor) {
final File lsFile = new File(Preferences.getInstance().getCliPath());
try {
LsDownloader lsDownloader = getLsDownloader();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package io.snyk.eclipse.plugin.analytics;

public interface AbstractAnalyticsEvent {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package io.snyk.eclipse.plugin.analytics;

import java.time.Instant;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import io.snyk.eclipse.plugin.properties.preferences.Preferences;

public class AnalyticsEvent implements AbstractAnalyticsEvent {
private final String interactionType;
private final List<String> category;
private final String status;
private final String targetId;
private final long timestampMs;
private final long durationMs;
private final Map<String, Object> results;
private final List<Object> errors;
private final Map<String, Object> extension;

public AnalyticsEvent(String interactionType, List<String> category, String status, String targetId, long timestampMs, long durationMs, Map<String, Object> results, List<Object> errors, Map<String, Object> extension) {
this.interactionType = interactionType;
this.category = category;
this.status = status != null ? status : "success";
this.targetId = targetId != null ? targetId : "pkg:filesystem/scrubbed";
this.timestampMs = timestampMs != 0 ? timestampMs : Instant.now().toEpochMilli() ;
this.durationMs = durationMs;
this.results = results != null ? results : new HashMap<>();
this.errors = errors != null ? errors : new ArrayList<>();
this.extension = extension != null ? extension : new HashMap<>();
this.extension.put("device_id", Preferences.getInstance().getPref(Preferences.DEVICE_ID));
}

public AnalyticsEvent(String interactionType, List<String> category) {
this(interactionType, category, null, null, 0, 0, null, null, null);
}

public String getInteractionType() {
return interactionType;
}

public List<String> getCategory() {
return category;
}

public String getStatus() {
return status;
}

public String getTargetId() {
return targetId;
}

public long getTimestampMs() {
return timestampMs;
}

public long getDurationMs() {
return durationMs;
}

public Map<String, Object> getResults() {
return results;
}

public List<Object> getErrors() {
return errors;
}

public Map<String, Object> getExtension() {
return extension;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package io.snyk.eclipse.plugin.analytics;

import java.util.LinkedList;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.function.Consumer;

import org.apache.commons.lang3.tuple.Pair;

import io.snyk.eclipse.plugin.properties.preferences.Preferences;
import io.snyk.eclipse.plugin.utils.SnykLogger;
import io.snyk.languageserver.protocolextension.SnykExtendedLanguageClient;

public class AnalyticsSender {
// left = event, right = callback function
private final ConcurrentLinkedQueue<Pair<AbstractAnalyticsEvent, Consumer<Void>>> eventQueue = new ConcurrentLinkedQueue<>();

private AnalyticsSender() {
CompletableFuture.runAsync(() -> { start(); });
}

private static AnalyticsSender instance;

public static AnalyticsSender getInstance() {
if (instance == null) {
synchronized (AnalyticsSender.class) {
if (instance == null) {
instance = new AnalyticsSender();
}
}
}
return instance;
}

private void start() {
while (true) {
String authToken = Preferences.getInstance().getAuthToken();
var lc = SnykExtendedLanguageClient.getInstance();
if (eventQueue.isEmpty() || authToken == null || authToken.isBlank() || lc == null ) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
continue;
}
LinkedList<Pair<AbstractAnalyticsEvent, Consumer<Void>>> copyForSending = new LinkedList<>(eventQueue);
for (Pair<AbstractAnalyticsEvent, Consumer<Void>> event : copyForSending) {
try {
lc.reportAnalytics(event.getLeft());
event.getRight().accept(null);
} catch (Exception e) {
SnykLogger.logError(e);
} finally {
eventQueue.remove(event);
}
}
}
}

public void logEvent(AbstractAnalyticsEvent event, Consumer<Void> callback) {
var pair = Pair.of(event, callback);
eventQueue.add(pair);
}
}
Loading

0 comments on commit 4ed6db1

Please sign in to comment.