Skip to content

Commit

Permalink
feat: added summary panel (#573)
Browse files Browse the repository at this point in the history
* feat: added summary panel

---------

Co-authored-by: DariusZdroba <[email protected]>
Co-authored-by: Abdelrahman Shawki Hassan <[email protected]>
  • Loading branch information
3 people authored Jan 30, 2025
1 parent 8bed8bf commit 53e51f1
Show file tree
Hide file tree
Showing 16 changed files with 202 additions and 6 deletions.
11 changes: 9 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,13 @@
"name": "Snyk",
"when": "!snyk:loggedIn || snyk:error || !snyk:workspaceFound || snyk:authenticationChanged"
},
{
"type": "webview",
"id": "snyk.views.summary",
"name": "SUMMARY",
"when": "snyk:initialized && snyk:loggedIn && snyk:workspaceFound && !snyk:error",
"content": "${scanSummaryHtml}"
},
{
"id": "snyk.views.analysis.oss",
"name": "Open Source Security",
Expand Down Expand Up @@ -417,12 +424,12 @@
"view/title": [
{
"command": "snyk.start",
"when": "view == 'snyk.views.analysis.code.security' || view == 'snyk.views.analysis.code.security.delta' || view == 'snyk.views.analysis.code.quality' || view == 'snyk.views.analysis.code.quality.delta' || view == 'snyk.views.analysis.oss' || view == 'snyk.views.analysis.configuration'",
"when": "view == 'snyk.views.summary' || view == 'snyk.views.analysis.code.security' || view == 'snyk.views.analysis.code.security.delta' || view == 'snyk.views.analysis.code.quality' || view == 'snyk.views.analysis.code.quality.delta' || view == 'snyk.views.analysis.oss' || view == 'snyk.views.analysis.configuration'",
"group": "navigation"
},
{
"command": "snyk.settings",
"when": "view == 'snyk.views.analysis.code.security' || view == 'snyk.views.analysis.code.security.delta' || view == 'snyk.views.analysis.code.quality' || view == 'snyk.views.analysis.code.quality.delta' || view == 'snyk.views.analysis.oss' || view == 'snyk.views.welcome' || view == 'snyk.views.analysis.configuration'",
"when": "view == 'snyk.views.summary' || view == 'snyk.views.analysis.code.security' || view == 'snyk.views.analysis.code.security.delta' || view == 'snyk.views.analysis.code.quality' || view == 'snyk.views.analysis.code.quality.delta' || view == 'snyk.views.analysis.oss' || view == 'snyk.views.welcome' || view == 'snyk.views.analysis.configuration'",
"group": "navigation"
}
],
Expand Down
3 changes: 2 additions & 1 deletion src/snyk/base/modules/baseSnykModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { OssVulnerabilityCountService } from '../../snykOss/services/vulnerabili
import { IAuthenticationService } from '../services/authenticationService';
import { ScanModeService } from '../services/scanModeService';
import SnykStatusBarItem, { IStatusBarItem } from '../statusBarItem/statusBarItem';
import { ISummaryProviderService } from '../summary/summaryProviderService';
import { ILoadingBadge, LoadingBadge } from '../views/loadingBadge';
import { IBaseSnykModule } from './interfaces';

Expand All @@ -35,7 +36,7 @@ export default abstract class BaseSnykModule implements IBaseSnykModule {

protected readonly editorsWatcher: IWatcher;
protected configurationWatcher: IWatcher;

protected summaryProviderService: ISummaryProviderService;
readonly contextService: IContextService;
cacheService: IClearCacheService;
readonly openerService: IOpenerService;
Expand Down
24 changes: 24 additions & 0 deletions src/snyk/base/summary/summaryProviderService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { ILog } from '../../common/logger/interfaces';
import { SummaryWebviewViewProvider } from '../../common/views/summaryWebviewProvider';

export interface ISummaryProviderService {
updateSummaryPanel(scanSummary: string): void;
}

export class SummaryProviderService implements ISummaryProviderService {
constructor(
private readonly logger: ILog,
private readonly summaryWebviewViewProvider: SummaryWebviewViewProvider | undefined,
) {}
public updateSummaryPanel(scanSummary: string) {
if (!this.summaryWebviewViewProvider) {
this.logger.error('Summary Webview Provider was not initialized.');
return;
}
try {
this.summaryWebviewViewProvider.updateWebviewContent(scanSummary);
} catch (error) {
this.logger.error('Failed to update Summary panel');
}
}
}
4 changes: 4 additions & 0 deletions src/snyk/common/commands/commandController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,10 @@ export class CommandController {
await this.folderConfigs.setBranch(this.window, this.configuration, folderPath);
}

async toggleDelta(isEnabled: boolean): Promise<void> {
await this.configuration.setDeltaFindingsEnabled(isEnabled);
}

openSettings(): void {
void this.commands.executeCommand(VSCODE_GO_TO_SETTINGS_COMMAND, `@ext:${this.configuration.getExtensionId()}`);
}
Expand Down
15 changes: 15 additions & 0 deletions src/snyk/common/configuration/configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import { IVSCodeWorkspace } from '../vscode/workspace';
import { CliExecutable } from '../../cli/cliExecutable';

const NEWISSUES = 'Net new issues';
const ALLISSUES = 'All issues';

export type FeaturesConfiguration = {
ossEnabled: boolean | undefined;
Expand Down Expand Up @@ -143,6 +144,7 @@ export interface IConfiguration {
setEndpoint(endpoint: string): Promise<void>;

getDeltaFindingsEnabled(): boolean;
setDeltaFindingsEnabled(isEnabled: boolean): Promise<void>;

getOssQuickFixCodeActionsEnabled(): boolean;

Expand Down Expand Up @@ -361,6 +363,19 @@ export class Configuration implements IConfiguration {
);
}

async setDeltaFindingsEnabled(isEnabled: boolean): Promise<void> {
let deltaValue = NEWISSUES;
if (!isEnabled) {
deltaValue = ALLISSUES;
}
await this.workspace.updateConfiguration(
CONFIGURATION_IDENTIFIER,
this.getConfigName(DELTA_FINDINGS),
deltaValue,
true,
);
}

async clearToken(): Promise<void> {
return new Promise<void>((resolve, reject) => {
SecretStorageAdapter.instance
Expand Down
1 change: 1 addition & 0 deletions src/snyk/common/constants/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export const SNYK_SHOW_ERROR_FROM_CONTEXT_COMMAND = 'snyk.showErrorFromContext';
export const SNYK_GET_LESSON_COMMAND = 'snyk.getLearnLesson';
export const SNYK_GET_SETTINGS_SAST_ENABLED = 'snyk.getSettingsSastEnabled';
export const SNYK_SET_BASE_BRANCH_COMMAND = 'snyk.setBaseBranch';
export const SNYK_TOGGLE_DELTA = 'snyk.toggleDelta';
export const SNYK_LOGIN_COMMAND = 'snyk.login';
export const SNYK_WORKSPACE_SCAN_COMMAND = 'snyk.workspace.scan';
export const SNYK_TRUST_WORKSPACE_FOLDERS_COMMAND = 'snyk.trustWorkspaceFolders';
Expand Down
1 change: 1 addition & 0 deletions src/snyk/common/constants/languageServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ export const SNYK_HAS_AUTHENTICATED = '$/snyk.hasAuthenticated';
export const SNYK_ADD_TRUSTED_FOLDERS = '$/snyk.addTrustedFolders';
export const SNYK_SCAN = '$/snyk.scan';
export const SNYK_FOLDERCONFIG = '$/snyk.folderConfigs';
export const SNYK_SCANSUMMARY = '$/snyk.scanSummary';
2 changes: 2 additions & 0 deletions src/snyk/common/constants/views.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// see https://code.visualstudio.com/api/references/contribution-points#contributes.viewsWelcome

export const SNYK_VIEW_WELCOME = 'snyk.views.welcome';
export const SNYK_VIEW_SUMMARY = 'snyk.views.summary';
export const SNYK_VIEW_ANALYSIS_CODE_ENABLEMENT = 'snyk.views.analysis.code.enablement';
export const SNYK_VIEW_ANALYSIS_CODE_SECURITY = 'snyk.views.analysis.code.security';
export const SNYK_VIEW_ANALYSIS_CODE_QUALITY = 'snyk.views.analysis.code.quality';
Expand All @@ -25,6 +26,7 @@ export const SNYK_CONTEXT = {
MODE: 'mode',
ADVANCED: 'advanced',
DELTA_FINDINGS_ENABLED: 'deltaFindingsEnabled',
SCANSUMMARY: 'scanSummaryHtml',
};

export const SNYK_ANALYSIS_STATUS = {
Expand Down
8 changes: 7 additions & 1 deletion src/snyk/common/languageServer/languageServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
SNYK_HAS_AUTHENTICATED,
SNYK_LANGUAGE_SERVER_NAME,
SNYK_SCAN,
SNYK_SCANSUMMARY,
} from '../constants/languageServer';
import { CONFIGURATION_IDENTIFIER } from '../constants/settings';
import { ErrorHandler } from '../error/errorHandler';
Expand All @@ -19,11 +20,11 @@ import { ILanguageClientAdapter } from '../vscode/languageClient';
import { LanguageClient, LanguageClientOptions, ServerOptions } from '../vscode/types';
import { IVSCodeWindow } from '../vscode/window';
import { IVSCodeWorkspace } from '../vscode/workspace';
import { CliExecutable } from '../../cli/cliExecutable';
import { LanguageClientMiddleware } from './middleware';
import { LanguageServerSettings, ServerSettings } from './settings';
import { CodeIssueData, IacIssueData, OssIssueData, Scan } from './types';
import { ExtensionContext } from '../vscode/extensionContext';
import { ISummaryProviderService } from '../../base/summary/summaryProviderService';

export interface ILanguageServer {
start(): Promise<void>;
Expand Down Expand Up @@ -51,6 +52,7 @@ export class LanguageServer implements ILanguageServer {
private readonly logger: ILog,
private downloadService: DownloadService,
private extensionContext: ExtensionContext,
private summaryProvider: ISummaryProviderService,
) {
this.downloadService = downloadService;
}
Expand Down Expand Up @@ -156,6 +158,10 @@ export class LanguageServer implements ILanguageServer {
this.logger.info(`${_.capitalize(scan.product)} scan for ${scan.folderPath}: ${scan.status}.`);
this.scan$.next(scan);
});

client.onNotification(SNYK_SCANSUMMARY, ({ scanSummary }: { scanSummary: string }) => {
this.summaryProvider.updateSummaryPanel(scanSummary);
});
}

// Initialization options are not semantically equal to server settings, thus separated here
Expand Down
11 changes: 11 additions & 0 deletions src/snyk/common/languageServer/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,3 +135,14 @@ export type AutofixUnifiedDiffSuggestion = {
fixId: string;
unifiedDiffsPerFile: { [key: string]: string };
};

export type Summary = {
toggleDelta: boolean;
};

export type SummaryMessage = {
type: string;
args: {
summary: Summary;
};
};
2 changes: 1 addition & 1 deletion src/snyk/common/services/downloadService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ export class DownloadService {
}

async isCliInstalled() {
const cliExecutableExists = await CliExecutable.exists(this.extensionContext.extensionPath);
const cliExecutableExists = await CliExecutable.exists(await this.configuration.getCliPath());
const cliChecksumWritten = !!this.getCliChecksum();

return cliExecutableExists && cliChecksumWritten;
Expand Down
71 changes: 71 additions & 0 deletions src/snyk/common/views/summaryWebviewProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import * as vscode from 'vscode';
import { readFileSync } from 'fs';
import { getNonce } from './nonce';
import { SummaryMessage } from '../languageServer/types';
import { SNYK_TOGGLE_DELTA } from '../constants/commands';
import { Logger } from '../logger/logger';
export class SummaryWebviewViewProvider implements vscode.WebviewViewProvider {
private static instance: SummaryWebviewViewProvider;
private webviewView: vscode.WebviewView | undefined;
private context: vscode.ExtensionContext;

private constructor(context: vscode.ExtensionContext) {
this.context = context;
}

public static getInstance(extensionContext?: vscode.ExtensionContext): SummaryWebviewViewProvider | undefined {
if (!SummaryWebviewViewProvider.instance) {
if (!extensionContext) {
console.log('ExtensionContext is required for the first initialization of SnykDiagnosticsWebviewViewProvider');
return undefined;
} else {
SummaryWebviewViewProvider.instance = new SummaryWebviewViewProvider(extensionContext);
}
}
return SummaryWebviewViewProvider.instance;
}

resolveWebviewView(webviewView: vscode.WebviewView) {
this.webviewView = webviewView;
webviewView.webview.options = {
enableScripts: true,
};
this.webviewView.webview.onDidReceiveMessage((msg: SummaryMessage) => this.handleMessage(msg));
}

private async handleMessage(message: SummaryMessage) {
try {
switch (message.type) {
case 'sendSummaryParams': {
const { summary } = message.args;
await vscode.commands.executeCommand(SNYK_TOGGLE_DELTA, summary.toggleDelta);
break;
}
}
} catch (error) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
Logger.error(error);
}
}

public updateWebviewContent(html: string) {
if (this.webviewView) {
const nonce = getNonce();
const ideScriptPath = vscode.Uri.joinPath(
vscode.Uri.file(this.context.extensionPath),
'out',
'snyk',
'common',
'views',
'summaryWebviewScript.js',
);
const ideScript = readFileSync(ideScriptPath.fsPath, 'utf8');

html = html.replace('${ideStyle}', `<style nonce=${nonce}>` + '' + '</style>');
html = html.replace('${ideFunc}', ideScript);
html = html.replace(/\${nonce}/g, nonce);

this.webviewView.webview.html = html;
}
}
}
28 changes: 28 additions & 0 deletions src/snyk/common/views/summaryWebviewScript.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/* eslint-disable @typescript-eslint/no-unsafe-argument */
/* eslint-disable @typescript-eslint/no-non-null-assertion */
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-call */

type SummaryMessage = {
type: 'sendSummaryParams';
args: {
summary: Summary;
};
};

type Summary = {
toggleDelta: boolean;
};
const vscode = acquireVsCodeApi();

Check warning on line 17 in src/snyk/common/views/summaryWebviewScript.ts

View workflow job for this annotation

GitHub Actions / build / Build and Test (ubuntu-latest)

Unsafe assignment of an `any` value

Check warning on line 17 in src/snyk/common/views/summaryWebviewScript.ts

View workflow job for this annotation

GitHub Actions / build / Build and Test (macos-latest)

Unsafe assignment of an `any` value

Check warning on line 17 in src/snyk/common/views/summaryWebviewScript.ts

View workflow job for this annotation

GitHub Actions / build / Build and Test (windows-latest)

Unsafe assignment of an `any` value

const summary: Summary = {
// @ts-expect-error this will be injected in a func coming from LS that has isEnabled as arg.
toggleDelta: isEnabled,

Check warning on line 21 in src/snyk/common/views/summaryWebviewScript.ts

View workflow job for this annotation

GitHub Actions / build / Build and Test (ubuntu-latest)

Unsafe assignment of an `any` value

Check warning on line 21 in src/snyk/common/views/summaryWebviewScript.ts

View workflow job for this annotation

GitHub Actions / build / Build and Test (macos-latest)

Unsafe assignment of an `any` value

Check warning on line 21 in src/snyk/common/views/summaryWebviewScript.ts

View workflow job for this annotation

GitHub Actions / build / Build and Test (windows-latest)

Unsafe assignment of an `any` value
};

const message: SummaryMessage = {
type: 'sendSummaryParams',
args: { summary },
};
vscode.postMessage(message);
2 changes: 1 addition & 1 deletion src/snyk/common/watchers/configurationWatcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ class ConfigurationWatcher implements IWatcher {
extension.initDependencyDownload();
return;
} else if (key === FOLDER_CONFIGS || key == DELTA_FINDINGS) {
extension.viewManagerService.refreshAllViews();
return extension.viewManagerService.refreshAllViews();
} else if (key === TRUSTED_FOLDERS) {
extension.workspaceTrust.resetTrustedFoldersCache();
extension.viewManagerService.refreshAllViews();
Expand Down
20 changes: 20 additions & 0 deletions src/snyk/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
SNYK_SHOW_LS_OUTPUT_COMMAND,
SNYK_SHOW_OUTPUT_COMMAND,
SNYK_START_COMMAND,
SNYK_TOGGLE_DELTA,
SNYK_WORKSPACE_SCAN_COMMAND,
} from './common/constants/commands';
import {
Expand All @@ -34,6 +35,7 @@ import {
SNYK_VIEW_ANALYSIS_CODE_SECURITY,
SNYK_VIEW_ANALYSIS_IAC,
SNYK_VIEW_ANALYSIS_OSS,
SNYK_VIEW_SUMMARY,
SNYK_VIEW_SUPPORT,
SNYK_VIEW_WELCOME,
} from './common/constants/views';
Expand Down Expand Up @@ -83,9 +85,22 @@ import { GitAPI, GitExtension, Repository } from './common/git';
import { AnalyticsSender } from './common/analytics/AnalyticsSender';
import { MEMENTO_ANALYTICS_PLUGIN_INSTALLED_SENT } from './common/constants/globalState';
import { AnalyticsEvent } from './common/analytics/AnalyticsEvent';
import { SummaryWebviewViewProvider } from './common/views/summaryWebviewProvider';
import { SummaryProviderService } from './base/summary/summaryProviderService';

class SnykExtension extends SnykLib implements IExtension {
public async activate(vscodeContext: vscode.ExtensionContext): Promise<void> {
const summaryWebviewViewProvider = SummaryWebviewViewProvider.getInstance(vscodeContext);
if (!summaryWebviewViewProvider) {
console.log('Summary panel not initialized.');
} else {
this.summaryProviderService = new SummaryProviderService(Logger, summaryWebviewViewProvider);
vscodeContext.subscriptions.push(
vscode.window.registerWebviewViewProvider(SNYK_VIEW_SUMMARY, summaryWebviewViewProvider),
);
}

SummaryWebviewViewProvider.getInstance(vscodeContext);
extensionContext.setContext(vscodeContext);
this.context = extensionContext;
const snykConfiguration = await this.getSnykConfiguration();
Expand Down Expand Up @@ -199,6 +214,7 @@ class SnykExtension extends SnykLib implements IExtension {
Logger,
this.downloadService,
this.context,
this.summaryProviderService,
);

const codeSuggestionProvider = new CodeSuggestionWebviewProvider(
Expand Down Expand Up @@ -498,6 +514,7 @@ class SnykExtension extends SnykLib implements IExtension {
),
vscode.commands.registerCommand(SNYK_START_COMMAND, async () => {
await vscode.commands.executeCommand(SNYK_WORKSPACE_SCAN_COMMAND);
await vscode.commands.executeCommand('setContext', 'scanSummaryHtml', 'scanSummary');
}),
vscode.commands.registerCommand(SNYK_SETTINGS_COMMAND, () => this.commandController.openSettings()),
vscode.commands.registerCommand(SNYK_DCIGNORE_COMMAND, (custom: boolean, path?: string) =>
Expand All @@ -512,6 +529,9 @@ class SnykExtension extends SnykLib implements IExtension {
vscode.commands.registerCommand(SNYK_SET_BASE_BRANCH_COMMAND, (folderPath: string) =>
this.commandController.setBaseBranch(folderPath),
),
vscode.commands.registerCommand(SNYK_TOGGLE_DELTA, (isEnabled: boolean) =>
this.commandController.toggleDelta(isEnabled),
),
vscode.commands.registerCommand(SNYK_SHOW_ERROR_FROM_CONTEXT_COMMAND, () => {
const err = this.contextService.viewContext[SNYK_CONTEXT.ERROR] as Error;
void this.notificationService.showErrorNotification(err.message);
Expand Down
Loading

0 comments on commit 53e51f1

Please sign in to comment.