Skip to content

Commit 8ba9294

Browse files
feat: add product node suffixes (#224)
1 parent a2c059a commit 8ba9294

3 files changed

Lines changed: 96 additions & 37 deletions

File tree

plugin/src/main/java/io/snyk/languageserver/SnykIssueCache.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ private Collection<Issue> getIssuesForPath(String path, Map<String, Collection<I
6060
Collection<Issue> issues = cache.get(path);
6161
return issues != null ? Collections.unmodifiableCollection(issues) : Collections.emptyList();
6262
}
63-
63+
6464
public Collection<Issue> getIssues(String path, String displayProduct) {
6565
var cache = getCacheByDisplayProduct(displayProduct);
6666
return getIssuesForPath(path, cache);
@@ -237,4 +237,14 @@ public long getIgnoredCount(String displayProduct) {
237237
var issues = getFilteredIssue(displayProduct, ignoredPredicate);
238238
return issues.size();
239239
}
240+
241+
public long getIssueCountBySeverity(String displayProduct, String severity) {
242+
var issues = getFilteredIssue(displayProduct, new Predicate<Issue>() {
243+
@Override
244+
public boolean test(Issue issue) {
245+
return issue != null && issue.severity().equals(severity);
246+
}
247+
});
248+
return issues.size();
249+
}
240250
}

plugin/src/main/java/io/snyk/languageserver/protocolextension/SnykExtendedLanguageClient.java

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@
1010
import static io.snyk.eclipse.plugin.domain.ProductConstants.SCAN_STATE_ERROR;
1111
import static io.snyk.eclipse.plugin.domain.ProductConstants.SCAN_STATE_IN_PROGRESS;
1212
import static io.snyk.eclipse.plugin.domain.ProductConstants.SCAN_STATE_SUCCESS;
13+
import static io.snyk.eclipse.plugin.domain.ProductConstants.SEVERITY_CRITICAL;
14+
import static io.snyk.eclipse.plugin.domain.ProductConstants.SEVERITY_HIGH;
15+
import static io.snyk.eclipse.plugin.domain.ProductConstants.SEVERITY_LOW;
16+
import static io.snyk.eclipse.plugin.domain.ProductConstants.SEVERITY_MEDIUM;
1317
import static io.snyk.eclipse.plugin.properties.preferences.Preferences.FILTER_IGNORES_SHOW_IGNORED_ISSUES;
1418
import static io.snyk.eclipse.plugin.properties.preferences.Preferences.FILTER_IGNORES_SHOW_OPEN_ISSUES;
1519
import static io.snyk.eclipse.plugin.views.snyktoolview.ISnykToolView.CONGRATS_NO_ISSUES_FOUND;
@@ -78,6 +82,7 @@
7882
import io.snyk.eclipse.plugin.wizards.SnykWizard;
7983
import io.snyk.languageserver.IssueCacheHolder;
8084
import io.snyk.languageserver.LsCommandID;
85+
import io.snyk.languageserver.LsConfigurationUpdater;
8186
import io.snyk.languageserver.LsNotificationID;
8287
import io.snyk.languageserver.ScanInProgressKey;
8388
import io.snyk.languageserver.ScanState;
@@ -100,6 +105,7 @@ public class SnykExtendedLanguageClient extends LanguageClientImpl {
100105
private ISnykToolView toolView;
101106
// this field is for testing only
102107
private LanguageServer ls;
108+
private LsConfigurationUpdater configurationUpdater = new LsConfigurationUpdater();
103109

104110
private static SnykExtendedLanguageClient instance = null;
105111

@@ -152,6 +158,7 @@ public void triggerScan(IWorkbenchWindow window) {
152158
if (Preferences.getInstance().getAuthToken().isBlank()) {
153159
runSnykWizard();
154160
} else {
161+
openToolView();
155162
this.toolView.resetNode(this.toolView.getRoot());
156163
try {
157164
if (window == null) {
@@ -267,6 +274,8 @@ public void hasAuthenticated(HasAuthenticatedParam param) {
267274
prefs.store(Preferences.AUTH_TOKEN_KEY, newToken);
268275
}
269276

277+
configurationUpdater.configurationChanged();
278+
270279
if (!newToken.isBlank() && PlatformUI.isWorkbenchRunning()) {
271280
enableSnykViewRunActions();
272281
}
@@ -301,17 +310,8 @@ public void snykScan(SnykScanParam param) {
301310
if (!param.getFolderPath().isBlank()) {
302311
issueCache = IssueCacheHolder.getInstance().getCacheInstance(param.getFolderPath());
303312
}
304-
if (toolView == null && !Preferences.getInstance().isTest()) {
305-
Display.getDefault().syncExec(() -> {
306-
IWorkbenchPage activePage = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
307-
try {
308-
toolView = (ISnykToolView) activePage.showView(SnykToolView.ID);
309-
} catch (PartInitException e) {
310-
SnykLogger.logError(e);
311-
return;
312-
}
313-
});
314-
}
313+
314+
openToolView();
315315

316316
Set<ProductTreeNode> affectedProductTreeNodes = getAffectedProductNodes(param.getProduct(),
317317
param.getFolderPath());
@@ -337,6 +337,22 @@ public void snykScan(SnykScanParam param) {
337337
setNodeState(param.getStatus(), affectedProductTreeNodes, issueCache);
338338
}
339339

340+
private void openToolView() {
341+
// we don't want to use the UI in tests usually
342+
if (this.toolView != null || Preferences.getInstance().isTest()) {
343+
return;
344+
}
345+
Display.getDefault().syncExec(() -> {
346+
IWorkbenchPage activePage = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
347+
try {
348+
toolView = (ISnykToolView) activePage.showView(SnykToolView.ID);
349+
} catch (PartInitException e) {
350+
SnykLogger.logError(e);
351+
return;
352+
}
353+
});
354+
}
355+
340356
private Set<ProductTreeNode> getAffectedProductNodes(String snykScanProduct, String folderPath) {
341357
Set<ProductTreeNode> affectedProductTreeNodes = new HashSet<>();
342358
var displayProduct = SCAN_PARAMS_TO_DISPLAYED.get(snykScanProduct);
@@ -385,7 +401,14 @@ private void setProductNodeText(Set<ProductTreeNode> nodes, String nodeText) {
385401
}
386402

387403
public String getCountsSuffix(ProductTreeNode productTreeNode, SnykIssueCache issueCache) {
388-
return "Total: " + String.valueOf(issueCache.getTotalCount(productTreeNode.getProduct()));
404+
String product = productTreeNode.getProduct();
405+
var critical = issueCache.getIssueCountBySeverity(product, SEVERITY_CRITICAL);
406+
var high = issueCache.getIssueCountBySeverity(product, SEVERITY_HIGH);
407+
var medium = issueCache.getIssueCountBySeverity(product, SEVERITY_MEDIUM);
408+
var low = issueCache.getIssueCountBySeverity(product, SEVERITY_LOW);
409+
var total = issueCache.getTotalCount(product);
410+
return String.format("%d unique vulnerabilities: %d critical, %d high, %d medium, %d low", total, critical,
411+
high, medium, low);
389412
}
390413

391414
private SnykIssueCache getIssueCache(String filePath) {

tests/src/test/java/io/snyk/languageserver/protocolextension/SnykExtendedLanguageClientTest.java

Lines changed: 50 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
import io.snyk.languageserver.LsBaseTest;
5757
import io.snyk.languageserver.ScanInProgressKey;
5858
import io.snyk.languageserver.ScanState;
59+
import io.snyk.languageserver.SnykIssueCache;
5960
import io.snyk.languageserver.protocolextension.messageObjects.Diagnostic316;
6061
import io.snyk.languageserver.protocolextension.messageObjects.HasAuthenticatedParam;
6162
import io.snyk.languageserver.protocolextension.messageObjects.PublishDiagnostics316Param;
@@ -224,7 +225,7 @@ void testPublishDiagnosticsShouldChangeCache() {
224225
} catch (Exception ex) {
225226
fail(ex);
226227
}
227-
228+
228229
}
229230

230231
@Test
@@ -319,7 +320,7 @@ void testSnykScanSuccessAddsInfoNodes_IssuesFound_multipleFixable() {
319320

320321
pref.store(Preferences.FILTER_IGNORES_SHOW_IGNORED_ISSUES, "false");
321322
pref.store(Preferences.FILTER_IGNORES_SHOW_OPEN_ISSUES, "true");
322-
323+
323324
runInfoNodeTest(param, 4, 2, 0, 3, expectedFirstInfoNode, expectedSecondInfoNode, expectedThirdInfoNode);
324325
}
325326

@@ -366,7 +367,7 @@ void testSnykScanSuccessAddsInfoNodes_IssuesFound_onlyOpenDisplayed() {
366367
String expectedFirstInfoNode = "✋ 4 issues found by Snyk";
367368
String expectedSecondInfoNode = "⚡️ 2 issues can be fixed automatically";
368369
String expectedThirdInfoNode = "Adjust your Issue View Options to see open issues.";
369-
370+
370371
runInfoNodeTest(param, 4, 2, 0, 4, expectedFirstInfoNode, expectedSecondInfoNode, expectedThirdInfoNode);
371372
}
372373

@@ -448,7 +449,7 @@ private HashSet<ProductTreeNode> setupProductNodes(SnykScanParam param) {
448449
}
449450
return productNodes;
450451
}
451-
452+
452453
@Test
453454
void testSnykScanAddsToScanStateHashMap() {
454455
var scanState = ScanState.getInstance();
@@ -478,34 +479,34 @@ void testSnykScanAddsToScanStateHashMap() {
478479
actualState = scanState.isScanInProgress(expectedKey);
479480
assertEquals(false, actualState);
480481
}
481-
482-
@Test
482+
483+
@Test
483484
void createProgressAddsTokenToProgressManager() {
484485
ProgressManager pmMock = mock(ProgressManager.class);
485486
cut = new SnykExtendedLanguageClient();
486487
cut.setProgressMgr(pmMock);
487488
var params = new WorkDoneProgressCreateParams();
488489
params.setToken("a");
489-
490+
490491
cut.createProgress(params);
491-
492+
492493
verify(pmMock).addProgress("a");
493494
}
494-
495-
@Test
495+
496+
@Test
496497
void notifyProgress_EndProgressRemovesFromProgressManager() {
497498
ProgressManager pmMock = mock(ProgressManager.class);
498499
cut = new SnykExtendedLanguageClient();
499500
cut.setProgressMgr(pmMock);
500501
var params = cut.getEndProgressParam("a");
501502
params.setToken("a");
502-
503+
503504
cut.notifyProgress(params);
504-
505+
505506
verify(pmMock).removeProgress("a");
506507
}
507-
508-
@Test
508+
509+
@Test
509510
void notifyProgress_BeginProgressDoesNotRemoveFromProgressManager() {
510511
ProgressManager pmMock = mock(ProgressManager.class);
511512
cut = new SnykExtendedLanguageClient();
@@ -516,13 +517,13 @@ void notifyProgress_BeginProgressDoesNotRemoveFromProgressManager() {
516517
Either<String, Integer> tokenEither = Either.forLeft("a");
517518
var progressParam = new ProgressParams(tokenEither, value);
518519
progressParam.setToken("a");
519-
520+
520521
cut.notifyProgress(progressParam);
521-
522+
522523
verifyNoInteractions(pmMock);
523524
}
524-
525-
@Test
525+
526+
@Test
526527
void notifyProgress_ReportProgressDoesNotRemoveFromProgressManager() {
527528
ProgressManager pmMock = mock(ProgressManager.class);
528529
cut = new SnykExtendedLanguageClient();
@@ -533,25 +534,50 @@ void notifyProgress_ReportProgressDoesNotRemoveFromProgressManager() {
533534
Either<String, Integer> tokenEither = Either.forLeft("a");
534535
var progressParam = new ProgressParams(tokenEither, value);
535536
progressParam.setToken("a");
536-
537+
537538
cut.notifyProgress(progressParam);
538-
539+
539540
verifyNoInteractions(pmMock);
540541
}
541-
542-
@Test
542+
543+
@Test
543544
void cancelProgress_callsLSToCancel() {
544545
ProgressManager pmMock = mock(ProgressManager.class);
545546
LanguageServer lsMock = mock(LanguageServer.class);
546547
cut = new SnykExtendedLanguageClient();
547548
cut.setLs(lsMock);
548549
cut.setProgressMgr(pmMock);
549-
550+
550551
cut.cancelProgress("a");
551-
552+
552553
// progressManager should call language client, but not the other way round
553554
// else we'd have an endless loop
554555
verifyNoInteractions(pmMock);
555556
verify(lsMock).cancelProgress(Mockito.any());
556557
}
558+
559+
@Test
560+
void testGetTotal() {
561+
cut = new SnykExtendedLanguageClient();
562+
ProductTreeNode productTreeNode = new ProductTreeNode(DISPLAYED_CODE_SECURITY);
563+
SnykIssueCache issueCache = new SnykIssueCache();
564+
var issues = new HashSet<Issue>();
565+
var highIssue = Instancio.of(Issue.class).set(Select.field(Issue::additionalData), getSecurityIssue())
566+
.set(Select.field(Issue::severity), "high").create();
567+
var mediumIssue = Instancio.of(Issue.class).set(Select.field(Issue::additionalData), getSecurityIssue())
568+
.set(Select.field(Issue::severity), "medium").create();
569+
var lowIssue = Instancio.of(Issue.class).set(Select.field(Issue::additionalData), getSecurityIssue())
570+
.set(Select.field(Issue::severity), "low").create();
571+
issues.addAll(Set.of(highIssue, mediumIssue, lowIssue));
572+
573+
issueCache.addCodeIssues("a/b/c", issues);
574+
575+
var actual = cut.getCountsSuffix(productTreeNode, issueCache);
576+
577+
assertEquals("3 unique vulnerabilities: 0 critical, 1 high, 1 medium, 1 low", actual);
578+
}
579+
580+
private AdditionalData getSecurityIssue() {
581+
return Instancio.of(AdditionalData.class).set(Select.field(AdditionalData::isSecurityType), true).create();
582+
}
557583
}

0 commit comments

Comments
 (0)