From 8b53b616bdc96ad6eab32f944794aabf48368e5f Mon Sep 17 00:00:00 2001 From: Catalina Oyaneder Date: Thu, 2 May 2024 18:33:39 +0100 Subject: [PATCH 01/11] feat: add Snyk Code feature flag to settings and configuration - Added a new feature flag `snykCodeLsp` to enable the Snyk Code Panel Rendering via LSP. --- package.json | 8 +++++++- src/snyk/common/configuration/configuration.ts | 2 ++ src/snyk/common/languageServer/settings.ts | 2 ++ src/test/unit/common/configuration.test.ts | 2 ++ .../unit/common/languageServer/languageServer.test.ts | 2 ++ src/test/unit/common/languageServer/middleware.test.ts | 1 + src/test/unit/common/languageServer/settings.test.ts | 1 + 7 files changed, 17 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 56bc7339b..8f6866141 100644 --- a/package.json +++ b/package.json @@ -200,7 +200,13 @@ "title": "Preview feature toggles", "description": "Preview features that are currently in development. Setting keys will be removed when features become stable.", "propertyNames": true, - "properties": {} + "properties": { + "snykCodeLsp": { + "type": "boolean", + "default": false, + "description": "Enable Snyk Code Panel Rendering via Language Server Protocol." + } + } } } } diff --git a/src/snyk/common/configuration/configuration.ts b/src/snyk/common/configuration/configuration.ts index fec4f0e08..ced511d51 100644 --- a/src/snyk/common/configuration/configuration.ts +++ b/src/snyk/common/configuration/configuration.ts @@ -45,6 +45,7 @@ export interface SeverityFilter { export type PreviewFeatures = { advisor: boolean | undefined; + snykCodeLsp: boolean | undefined; }; export interface IConfiguration { @@ -423,6 +424,7 @@ export class Configuration implements IConfiguration { getPreviewFeatures(): PreviewFeatures { const defaultSetting: PreviewFeatures = { advisor: false, + snykCodeLsp: false, }; const userSetting = diff --git a/src/snyk/common/languageServer/settings.ts b/src/snyk/common/languageServer/settings.ts index eb7809ca6..e7d76f069 100644 --- a/src/snyk/common/languageServer/settings.ts +++ b/src/snyk/common/languageServer/settings.ts @@ -33,6 +33,7 @@ export type ServerSettings = { // Trusted folders feature enableTrustedFoldersFeature?: string; trustedFolders?: string[]; + isSnykCodeLsp?: boolean; // Snyk integration settings integrationName?: string; @@ -75,6 +76,7 @@ export class LanguageServerSettings { integrationName: CLI_INTEGRATION_NAME, integrationVersion: await Configuration.getVersion(), deviceId: user.anonymousId, + isSnykCodeLsp: configuration.getPreviewFeatures().snykCodeLsp, }; } } diff --git a/src/test/unit/common/configuration.test.ts b/src/test/unit/common/configuration.test.ts index 797956f7b..0bb3fae67 100644 --- a/src/test/unit/common/configuration.test.ts +++ b/src/test/unit/common/configuration.test.ts @@ -186,12 +186,14 @@ suite('Configuration', () => { deepStrictEqual(configuration.getPreviewFeatures(), { advisor: false, + snykCodeLsp: false, } as PreviewFeatures); }); test('Preview features: some features enabled', () => { const previewFeatures = { advisor: false, + snykCodeLsp: false, } as PreviewFeatures; const workspace = stubWorkspaceConfiguration(FEATURES_PREVIEW_SETTING, previewFeatures); diff --git a/src/test/unit/common/languageServer/languageServer.test.ts b/src/test/unit/common/languageServer/languageServer.test.ts index 18c6a4b00..bbc71db02 100644 --- a/src/test/unit/common/languageServer/languageServer.test.ts +++ b/src/test/unit/common/languageServer/languageServer.test.ts @@ -58,6 +58,7 @@ suite('Language Server', () => { getPreviewFeatures() { return { advisor: false, + snykCodeLsp: false, }; }, getFeaturesConfiguration() { @@ -218,6 +219,7 @@ suite('Language Server', () => { trustedFolders: ['/trusted/test/folder'], insecure: 'true', scanningMode: 'auto', + isSnykCodeLsp: false, }; deepStrictEqual(await languageServer.getInitializationOptions(), expectedInitializationOptions); diff --git a/src/test/unit/common/languageServer/middleware.test.ts b/src/test/unit/common/languageServer/middleware.test.ts index dc13aaed2..a34c8c764 100644 --- a/src/test/unit/common/languageServer/middleware.test.ts +++ b/src/test/unit/common/languageServer/middleware.test.ts @@ -34,6 +34,7 @@ suite('Language Server: Middleware', () => { getPreviewFeatures: () => { return { advisor: false, + snykCodeLsp: false, }; }, getFeaturesConfiguration() { diff --git a/src/test/unit/common/languageServer/settings.test.ts b/src/test/unit/common/languageServer/settings.test.ts index 1d72d991c..a716779ac 100644 --- a/src/test/unit/common/languageServer/settings.test.ts +++ b/src/test/unit/common/languageServer/settings.test.ts @@ -14,6 +14,7 @@ suite('LanguageServerSettings', () => { // eslint-disable-next-line @typescript-eslint/require-await getToken: async () => 'snyk-token', getFeaturesConfiguration: () => ({}), // iacEnabled, codeSecurityEnabled, codeQualityEnabled are undefined + getPreviewFeatures: () => ({ snykCodeLsp: true }), getCliPath: () => '/path/to/cli', getAdditionalCliParameters: () => '--all-projects -d', getTrustedFolders: () => ['/trusted/path'], From 068628d1300ce5720196d57188fcdff829491acf Mon Sep 17 00:00:00 2001 From: Catalina Oyaneder Date: Thu, 9 May 2024 09:53:34 +0100 Subject: [PATCH 02/11] feat: scaffolding Code panel rendering based on FF --- package.json | 5 ++ src/snyk/common/constants/commands.ts | 1 + src/snyk/common/constants/views.ts | 1 + src/snyk/common/types.ts | 5 ++ src/snyk/extension.ts | 75 ++++++++++++++++----------- 5 files changed, 57 insertions(+), 30 deletions(-) diff --git a/package.json b/package.json index 8f6866141..b98058d81 100644 --- a/package.json +++ b/package.json @@ -237,6 +237,11 @@ "name": "Code Security", "when": "snyk:initialized && snyk:loggedIn && snyk:codeEnabled && snyk:workspaceFound && !snyk:error" }, + { + "id": "snyk.views.analysis.code.security.languageServer", + "name": "Code Security", + "when": "snyk:initialized && snyk:loggedIn && snyk:codeEnabled && snyk:workspaceFound && !snyk:error" + }, { "id": "snyk.views.analysis.configuration", "name": "Configuration Issues", diff --git a/src/snyk/common/constants/commands.ts b/src/snyk/common/constants/commands.ts index ee3420077..6a118f9d4 100644 --- a/src/snyk/common/constants/commands.ts +++ b/src/snyk/common/constants/commands.ts @@ -24,6 +24,7 @@ export const SNYK_WORKSPACE_SCAN_COMMAND = 'snyk.workspace.scan'; export const SNYK_TRUST_WORKSPACE_FOLDERS_COMMAND = 'snyk.trustWorkspaceFolders'; export const SNYK_GET_ACTIVE_USER = 'snyk.getActiveUser'; export const SNYK_CODE_FIX_DIFFS_COMMAND = 'snyk.code.fixDiffs'; +export const SNYK_FEATURE_FLAG_COMMAND = 'snyk.getFeatureFlagStatus'; // custom Snyk constants used in commands export const SNYK_CONTEXT_PREFIX = 'snyk:'; diff --git a/src/snyk/common/constants/views.ts b/src/snyk/common/constants/views.ts index d2e6819fb..329d09750 100644 --- a/src/snyk/common/constants/views.ts +++ b/src/snyk/common/constants/views.ts @@ -1,6 +1,7 @@ export const SNYK_VIEW_WELCOME = 'snyk.views.welcome'; 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_SECURITY_LANGUAGE_SERVER = 'snyk.views.analysis.code.security.languageServer'; export const SNYK_VIEW_ANALYSIS_CODE_QUALITY = 'snyk.views.analysis.code.quality'; export const SNYK_VIEW_ANALYSIS_OSS = 'snyk.views.analysis.oss'; export const SNYK_VIEW_SUPPORT = 'snyk.views.support'; diff --git a/src/snyk/common/types.ts b/src/snyk/common/types.ts index 7a5a667c4..0e39872f2 100644 --- a/src/snyk/common/types.ts +++ b/src/snyk/common/types.ts @@ -37,3 +37,8 @@ export function languageToString(language: Language): string { return PJSON; } } + +export type FeatureFlagStatus = { + ok: boolean; + userMessage?: string; +}; diff --git a/src/snyk/extension.ts b/src/snyk/extension.ts index 99a102d4c..e56bb4d7d 100644 --- a/src/snyk/extension.ts +++ b/src/snyk/extension.ts @@ -30,6 +30,7 @@ import { SNYK_VIEW_ANALYSIS_CODE_ENABLEMENT, SNYK_VIEW_ANALYSIS_CODE_QUALITY, SNYK_VIEW_ANALYSIS_CODE_SECURITY, + SNYK_VIEW_ANALYSIS_CODE_SECURITY_LANGUAGE_SERVER, SNYK_VIEW_ANALYSIS_IAC, SNYK_VIEW_ANALYSIS_OSS, SNYK_VIEW_SUPPORT, @@ -74,8 +75,11 @@ import { OssDetailPanelProvider } from './snykOss/providers/ossDetailPanelProvid import { OssVulnerabilityCountProvider } from './snykOss/providers/ossVulnerabilityCountProvider'; import OssIssueTreeProvider from './snykOss/providers/ossVulnerabilityTreeProvider'; import { OssVulnerabilityCountService } from './snykOss/services/vulnerabilityCount/ossVulnerabilityCountService'; +import type { FeatureFlagStatus } from './common/types'; class SnykExtension extends SnykLib implements IExtension { + private featureFlagStatus: FeatureFlagStatus | undefined; + public async activate(vscodeContext: vscode.ExtensionContext): Promise { extensionContext.setContext(vscodeContext); this.context = extensionContext; @@ -243,36 +247,6 @@ class SnykExtension extends SnykLib implements IExtension { ); this.registerCommands(vscodeContext); - const codeSecurityIssueProvider = new CodeSecurityIssueTreeProvider( - this.viewManagerService, - this.contextService, - this.snykCode, - configuration, - vsCodeLanguages, - ); - - const codeQualityIssueProvider = new CodeQualityIssueTreeProvider( - this.viewManagerService, - this.contextService, - this.snykCode, - configuration, - vsCodeLanguages, - ); - - const codeSecurityTree = vscode.window.createTreeView(SNYK_VIEW_ANALYSIS_CODE_SECURITY, { - treeDataProvider: codeSecurityIssueProvider, - }); - const codeQualityTree = vscode.window.createTreeView(SNYK_VIEW_ANALYSIS_CODE_QUALITY, { - treeDataProvider: codeQualityIssueProvider, - }); - - vscodeContext.subscriptions.push( - vscode.window.registerTreeDataProvider(SNYK_VIEW_ANALYSIS_CODE_SECURITY, codeSecurityIssueProvider), - vscode.window.registerTreeDataProvider(SNYK_VIEW_ANALYSIS_CODE_QUALITY, codeQualityIssueProvider), - codeSecurityTree, - codeQualityTree, - ); - vscodeContext.subscriptions.push(vscode.window.registerTreeDataProvider(SNYK_VIEW_SUPPORT, new SupportProvider())); const welcomeTree = vscode.window.createTreeView(SNYK_VIEW_WELCOME, { @@ -366,6 +340,47 @@ class SnykExtension extends SnykLib implements IExtension { // The codeEnabled context depends on an LS command await this.languageServer.start(); + // Fetch feature flag to determine whether to use the new LSP-based rendering. + this.featureFlagStatus = await vsCodeCommands.executeCommand( + 'snyk.getFeatureFlagStatus', + 'snykCodeConsistentIgnores', + ); + + const codeSecurityIssueProvider = new CodeSecurityIssueTreeProvider( + this.viewManagerService, + this.contextService, + this.snykCode, + configuration, + vsCodeLanguages, + ); + + const codeQualityIssueProvider = new CodeQualityIssueTreeProvider( + this.viewManagerService, + this.contextService, + this.snykCode, + configuration, + vsCodeLanguages, + ); + + // Choose view based on the feature flag status. + const SNYK_CODE_VIEW = this.featureFlagStatus?.ok + ? SNYK_VIEW_ANALYSIS_CODE_SECURITY_LANGUAGE_SERVER + : SNYK_VIEW_ANALYSIS_CODE_SECURITY; + + const codeSecurityTree = vscode.window.createTreeView(SNYK_CODE_VIEW, { + treeDataProvider: codeSecurityIssueProvider, + }); + const codeQualityTree = vscode.window.createTreeView(SNYK_VIEW_ANALYSIS_CODE_QUALITY, { + treeDataProvider: codeQualityIssueProvider, + }); + + vscodeContext.subscriptions.push( + vscode.window.registerTreeDataProvider(SNYK_CODE_VIEW, codeSecurityIssueProvider), + vscode.window.registerTreeDataProvider(SNYK_VIEW_ANALYSIS_CODE_QUALITY, codeQualityIssueProvider), + codeSecurityTree, + codeQualityTree, + ); + // initialize contexts await this.contextService.setContext(SNYK_CONTEXT.INITIALIZED, true); From 23fbe8e931b7b9f363f621421e1363b0bfefd203 Mon Sep 17 00:00:00 2001 From: Catalina Oyaneder Date: Fri, 10 May 2024 16:56:54 +0100 Subject: [PATCH 03/11] fix: remove unnecessary view for migration --- package.json | 5 ----- src/snyk/common/constants/views.ts | 1 - src/snyk/extension.ts | 10 ++-------- 3 files changed, 2 insertions(+), 14 deletions(-) diff --git a/package.json b/package.json index b98058d81..8f6866141 100644 --- a/package.json +++ b/package.json @@ -237,11 +237,6 @@ "name": "Code Security", "when": "snyk:initialized && snyk:loggedIn && snyk:codeEnabled && snyk:workspaceFound && !snyk:error" }, - { - "id": "snyk.views.analysis.code.security.languageServer", - "name": "Code Security", - "when": "snyk:initialized && snyk:loggedIn && snyk:codeEnabled && snyk:workspaceFound && !snyk:error" - }, { "id": "snyk.views.analysis.configuration", "name": "Configuration Issues", diff --git a/src/snyk/common/constants/views.ts b/src/snyk/common/constants/views.ts index 329d09750..d2e6819fb 100644 --- a/src/snyk/common/constants/views.ts +++ b/src/snyk/common/constants/views.ts @@ -1,7 +1,6 @@ export const SNYK_VIEW_WELCOME = 'snyk.views.welcome'; 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_SECURITY_LANGUAGE_SERVER = 'snyk.views.analysis.code.security.languageServer'; export const SNYK_VIEW_ANALYSIS_CODE_QUALITY = 'snyk.views.analysis.code.quality'; export const SNYK_VIEW_ANALYSIS_OSS = 'snyk.views.analysis.oss'; export const SNYK_VIEW_SUPPORT = 'snyk.views.support'; diff --git a/src/snyk/extension.ts b/src/snyk/extension.ts index e56bb4d7d..89743877e 100644 --- a/src/snyk/extension.ts +++ b/src/snyk/extension.ts @@ -30,7 +30,6 @@ import { SNYK_VIEW_ANALYSIS_CODE_ENABLEMENT, SNYK_VIEW_ANALYSIS_CODE_QUALITY, SNYK_VIEW_ANALYSIS_CODE_SECURITY, - SNYK_VIEW_ANALYSIS_CODE_SECURITY_LANGUAGE_SERVER, SNYK_VIEW_ANALYSIS_IAC, SNYK_VIEW_ANALYSIS_OSS, SNYK_VIEW_SUPPORT, @@ -362,12 +361,7 @@ class SnykExtension extends SnykLib implements IExtension { vsCodeLanguages, ); - // Choose view based on the feature flag status. - const SNYK_CODE_VIEW = this.featureFlagStatus?.ok - ? SNYK_VIEW_ANALYSIS_CODE_SECURITY_LANGUAGE_SERVER - : SNYK_VIEW_ANALYSIS_CODE_SECURITY; - - const codeSecurityTree = vscode.window.createTreeView(SNYK_CODE_VIEW, { + const codeSecurityTree = vscode.window.createTreeView(SNYK_VIEW_ANALYSIS_CODE_SECURITY, { treeDataProvider: codeSecurityIssueProvider, }); const codeQualityTree = vscode.window.createTreeView(SNYK_VIEW_ANALYSIS_CODE_QUALITY, { @@ -375,7 +369,7 @@ class SnykExtension extends SnykLib implements IExtension { }); vscodeContext.subscriptions.push( - vscode.window.registerTreeDataProvider(SNYK_CODE_VIEW, codeSecurityIssueProvider), + vscode.window.registerTreeDataProvider(SNYK_VIEW_ANALYSIS_CODE_SECURITY, codeSecurityIssueProvider), vscode.window.registerTreeDataProvider(SNYK_VIEW_ANALYSIS_CODE_QUALITY, codeQualityIssueProvider), codeSecurityTree, codeQualityTree, From 657afafaab6d43af3684b9915bbdcfd7363f1f70 Mon Sep 17 00:00:00 2001 From: Catalina Oyaneder Date: Fri, 10 May 2024 16:59:45 +0100 Subject: [PATCH 04/11] feat: add `details` field to `CodeIssueData` --- src/snyk/common/languageServer/types.ts | 1 + src/test/unit/common/services/learnService.test.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/src/snyk/common/languageServer/types.ts b/src/snyk/common/languageServer/types.ts index ea5219a0e..34075ff6f 100644 --- a/src/snyk/common/languageServer/types.ts +++ b/src/snyk/common/languageServer/types.ts @@ -51,6 +51,7 @@ export type CodeIssueData = { isSecurityType: boolean; priorityScore: number; hasAIFix: boolean; + details: string; // HTML from the LSP }; export type ExampleCommitFix = { diff --git a/src/test/unit/common/services/learnService.test.ts b/src/test/unit/common/services/learnService.test.ts index 8607f06e0..779a38106 100644 --- a/src/test/unit/common/services/learnService.test.ts +++ b/src/test/unit/common/services/learnService.test.ts @@ -36,6 +36,7 @@ suite('LearnService', () => { isSecurityType: true, priorityScore: 880, hasAIFix: false, + details: 'not used', }, title: 'not used', severity: IssueSeverity.Critical, From 6716400fb09d3c3b9617d05d89dcebe03c9fdb00 Mon Sep 17 00:00:00 2001 From: Catalina Oyaneder Date: Fri, 10 May 2024 22:17:43 +0100 Subject: [PATCH 05/11] feat: add new webview provider for feature flag-driven panel --- src/snyk/common/services/productService.ts | 2 +- src/snyk/extension.ts | 4 + src/snyk/snykCode/codeService.ts | 6 ++ .../suggestion/codeDetailPanelProvider.ts | 77 +++++++++++++++++++ src/test/unit/snykCode/codeService.test.ts | 2 + 5 files changed, 90 insertions(+), 1 deletion(-) create mode 100644 src/snyk/snykCode/views/suggestion/codeDetailPanelProvider.ts diff --git a/src/snyk/common/services/productService.ts b/src/snyk/common/services/productService.ts index efb4f06b1..d3ad5cb9a 100644 --- a/src/snyk/common/services/productService.ts +++ b/src/snyk/common/services/productService.ts @@ -42,7 +42,7 @@ export abstract class ProductService extends AnalysisStatusProvider implement constructor( readonly extensionContext: ExtensionContext, private readonly config: IConfiguration, - private readonly suggestionProvider: IProductWebviewProvider>, + protected readonly suggestionProvider: IProductWebviewProvider>, protected readonly viewManagerService: IViewManagerService, readonly workspace: IVSCodeWorkspace, private readonly workspaceTrust: IWorkspaceTrust, diff --git a/src/snyk/extension.ts b/src/snyk/extension.ts index 89743877e..bbf7786a7 100644 --- a/src/snyk/extension.ts +++ b/src/snyk/extension.ts @@ -75,6 +75,7 @@ import { OssVulnerabilityCountProvider } from './snykOss/providers/ossVulnerabil import OssIssueTreeProvider from './snykOss/providers/ossVulnerabilityTreeProvider'; import { OssVulnerabilityCountService } from './snykOss/services/vulnerabilityCount/ossVulnerabilityCountService'; import type { FeatureFlagStatus } from './common/types'; +import { CodeDetailPanelProvider } from './snykCode/views/suggestion/codeDetailPanelProvider'; class SnykExtension extends SnykLib implements IExtension { private featureFlagStatus: FeatureFlagStatus | undefined; @@ -173,9 +174,12 @@ class SnykExtension extends SnykLib implements IExtension { this.learnService, ); + const codeDetailPanelProvider = new CodeDetailPanelProvider(vsCodeWindow, extensionContext, Logger); + this.snykCode = new SnykCodeService( this.context, configuration, + codeDetailPanelProvider, codeSuggestionProvider, new CodeActionAdapter(), this.codeActionKindAdapter, diff --git a/src/snyk/snykCode/codeService.ts b/src/snyk/snykCode/codeService.ts index 946a2fdb9..74780d908 100644 --- a/src/snyk/snykCode/codeService.ts +++ b/src/snyk/snykCode/codeService.ts @@ -12,11 +12,15 @@ import { IVSCodeLanguages } from '../common/vscode/languages'; import { IVSCodeWorkspace } from '../common/vscode/workspace'; import { SnykCodeActionsProvider } from './codeActions/codeIssuesActionsProvider'; import { ICodeSuggestionWebviewProvider } from './views/interfaces'; +import { CodeDetailPanelProvider } from './views/suggestion/codeDetailPanelProvider'; export class SnykCodeService extends ProductService { + private detailProvider: CodeDetailPanelProvider; + constructor( extensionContext: ExtensionContext, config: IConfiguration, + detailProvider: CodeDetailPanelProvider, suggestionProvider: ICodeSuggestionWebviewProvider, readonly codeActionAdapter: ICodeActionAdapter, readonly codeActionKindAdapter: ICodeActionKindAdapter, @@ -39,6 +43,8 @@ export class SnykCodeService extends ProductService { logger, ); + this.detailProvider = detailProvider; + this.registerCodeActionsProvider( new SnykCodeActionsProvider(this.result, codeActionAdapter, codeActionKindAdapter, languages), ); diff --git a/src/snyk/snykCode/views/suggestion/codeDetailPanelProvider.ts b/src/snyk/snykCode/views/suggestion/codeDetailPanelProvider.ts new file mode 100644 index 000000000..466a44406 --- /dev/null +++ b/src/snyk/snykCode/views/suggestion/codeDetailPanelProvider.ts @@ -0,0 +1,77 @@ +import * as vscode from 'vscode'; + +import { Issue, CodeIssueData } from '../../../common/languageServer/types'; +import { WebviewProvider, IWebViewProvider } from '../../../common/views/webviewProvider'; +import { ErrorHandler } from '../../../common/error/errorHandler'; +import { SNYK_VIEW_SUGGESTION_CODE } from '../../../common/constants/views'; +import { IVSCodeWindow } from '../../../common/vscode/window'; +import { ExtensionContext } from '../../../common/vscode/extensionContext'; +import { ILog } from '../../../common/logger/interfaces'; +import { WebviewPanelSerializer } from '../webviewPanelSerializer'; + +export class CodeDetailPanelProvider + extends WebviewProvider> + implements IWebViewProvider> +{ + private issue: Issue | undefined; + + constructor( + private readonly window: IVSCodeWindow, + protected readonly context: ExtensionContext, + protected readonly logger: ILog, + ) { + super(context, logger); + } + + protected getHtmlForWebview(_webview: vscode.Webview): string { + throw new Error('Method not implemented.'); + } + + activate(): void { + this.context.addDisposables( + this.window.registerWebviewPanelSerializer(SNYK_VIEW_SUGGESTION_CODE, new WebviewPanelSerializer(this)), + ); + } + + get openIssueId(): string | undefined { + return this.issue?.id; + } + + async showPanel(issue: Issue): Promise { + try { + await this.focusSecondEditorGroup(); + if (this.panel) { + this.panel.reveal(vscode.ViewColumn.Two, true); + // We will render HTML here. Let's start with a simple one. + this.panel.webview.html = issue.additionalData.details; + } else { + this.panel = vscode.window.createWebviewPanel( + SNYK_VIEW_SUGGESTION_CODE, + 'Snyk Code Issue', + { + viewColumn: vscode.ViewColumn.Two, + preserveFocus: true, + }, + this.getWebviewOptions(), + ); + this.registerListeners(); + } + this.issue = issue; + } catch (error) { + ErrorHandler.handle(error, this.logger, 'messages.errors.suggestionViewShowFailed'); + } + } + + protected registerListeners(): void { + if (!this.panel) return; + + this.panel.onDidDispose(() => this.onPanelDispose(), null, this.disposables); + this.panel.onDidChangeViewState(() => this.checkVisibility(), undefined, this.disposables); + // Handle messages from the webview + // this.panel.webview.onDidReceiveMessage( + // (msg: SuggestionMessage) => this.handleMessage(msg), + // undefined, + // this.disposables, + // ); + } +} diff --git a/src/test/unit/snykCode/codeService.test.ts b/src/test/unit/snykCode/codeService.test.ts index 261930d26..491b865ce 100644 --- a/src/test/unit/snykCode/codeService.test.ts +++ b/src/test/unit/snykCode/codeService.test.ts @@ -14,6 +14,7 @@ import { ICodeSuggestionWebviewProvider } from '../../../snyk/snykCode/views/int import { LanguageServerMock } from '../mocks/languageServer.mock'; import { languagesMock } from '../mocks/languages.mock'; import { LoggerMock } from '../mocks/logger.mock'; +import { CodeDetailPanelProvider } from '../../../snyk/snykCode/views/suggestion/codeDetailPanelProvider'; suite('Code Service', () => { let ls: ILanguageServer; @@ -31,6 +32,7 @@ suite('Code Service', () => { service = new SnykCodeService( {} as ExtensionContext, {} as IConfiguration, + {} as CodeDetailPanelProvider, {} as ICodeSuggestionWebviewProvider, {} as ICodeActionAdapter, { From 187b4569140b9725a0ae3fffe4f8c317e8024bf8 Mon Sep 17 00:00:00 2001 From: Catalina Oyaneder Date: Sun, 12 May 2024 11:58:32 +0100 Subject: [PATCH 06/11] feat: handle feature flag in config file to react to changes in the orgs --- .../common/configuration/configuration.ts | 28 +++++++++++++++++++ src/snyk/common/constants/featureFlags.ts | 3 ++ src/snyk/common/constants/settings.ts | 1 + .../common/watchers/configurationWatcher.ts | 6 ++++ src/snyk/extension.ts | 9 ++---- 5 files changed, 40 insertions(+), 7 deletions(-) create mode 100644 src/snyk/common/constants/featureFlags.ts diff --git a/src/snyk/common/configuration/configuration.ts b/src/snyk/common/configuration/configuration.ts index ced511d51..7d0d64c7f 100644 --- a/src/snyk/common/configuration/configuration.ts +++ b/src/snyk/common/configuration/configuration.ts @@ -2,6 +2,7 @@ import _ from 'lodash'; import path from 'path'; import { URL } from 'url'; import { IDE_NAME_SHORT, SNYK_TOKEN_KEY } from '../constants/general'; +import { SNYK_FEATURE_FLAG_COMMAND } from '../constants/commands'; import { ADVANCED_ADDITIONAL_PARAMETERS_SETTING, ADVANCED_ADVANCED_MODE_SETTING, @@ -26,6 +27,8 @@ import { } from '../constants/settings'; import SecretStorageAdapter from '../vscode/secretStorage'; import { IVSCodeWorkspace } from '../vscode/workspace'; +import { vsCodeCommands } from '../vscode/commands'; +import type { FeatureFlagStatus } from '../types'; export type FeaturesConfiguration = { ossEnabled: boolean | undefined; @@ -56,6 +59,12 @@ export interface IConfiguration { authHost: string; baseApiUrl: string; + fetchAndSetFeatureFlag(flagName: string): Promise; + + getFeatureFlag(flagName: string): boolean; + + setFeatureFlag(flagName: string, value: boolean): void; + getToken(): Promise; setToken(token: string | undefined): Promise; @@ -121,6 +130,8 @@ export class Configuration implements IConfiguration { private readonly defaultBaseApiHost = 'https://api.snyk.io'; private readonly analyticsPermittedEnvironments = { 'app.snyk.io': true, 'app.us.snyk.io': true }; + private featureFlag: { [key: string]: boolean } = {}; + constructor(private processEnv: NodeJS.ProcessEnv = process.env, private workspace: IVSCodeWorkspace) {} getInsecure(): boolean { @@ -140,6 +151,23 @@ export class Configuration implements IConfiguration { return preview; } + public async fetchAndSetFeatureFlag(flagName: string): Promise { + try { + const ffStatus = await vsCodeCommands.executeCommand(SNYK_FEATURE_FLAG_COMMAND, flagName); + this.setFeatureFlag(flagName, ffStatus?.ok ?? false); + } catch (error) { + console.warn(`Failed to fetch feature flag ${flagName}: ${error}`); + } + } + + getFeatureFlag(flagName: string): boolean { + return this.featureFlag[flagName] ?? false; + } + + setFeatureFlag(flagName: string, value: boolean): void { + this.featureFlag[flagName] = value; + } + private static async getPackageJsonConfig(): Promise<{ version: string; preview: boolean }> { return (await import(path.join('../../../..', 'package.json'))) as { version: string; preview: boolean }; } diff --git a/src/snyk/common/constants/featureFlags.ts b/src/snyk/common/constants/featureFlags.ts new file mode 100644 index 000000000..d6281b34e --- /dev/null +++ b/src/snyk/common/constants/featureFlags.ts @@ -0,0 +1,3 @@ +export const FEATURE_FLAGS = { + consistentIgnores: 'snykCodeConsistentIgnores', +}; diff --git a/src/snyk/common/constants/settings.ts b/src/snyk/common/constants/settings.ts index ab1440629..34f21e29b 100644 --- a/src/snyk/common/constants/settings.ts +++ b/src/snyk/common/constants/settings.ts @@ -20,6 +20,7 @@ export const ADVANCED_ORGANIZATION = `${CONFIGURATION_IDENTIFIER}.advanced.organ export const ADVANCED_AUTOMATIC_DEPENDENCY_MANAGEMENT = `${CONFIGURATION_IDENTIFIER}.advanced.automaticDependencyManagement`; export const ADVANCED_CLI_PATH = `${CONFIGURATION_IDENTIFIER}.advanced.cliPath`; export const ADVANCED_CUSTOM_LS_PATH = `${CONFIGURATION_IDENTIFIER}.advanced.languageServerPath`; +export const ADVANCED_ORGANIZATION_SETTING = `${CONFIGURATION_IDENTIFIER}.advanced.organization`; export const SEVERITY_FILTER_SETTING = `${CONFIGURATION_IDENTIFIER}.severity`; export const TRUSTED_FOLDERS = `${CONFIGURATION_IDENTIFIER}.trustedFolders`; diff --git a/src/snyk/common/watchers/configurationWatcher.ts b/src/snyk/common/watchers/configurationWatcher.ts index 265611129..d2b98d532 100644 --- a/src/snyk/common/watchers/configurationWatcher.ts +++ b/src/snyk/common/watchers/configurationWatcher.ts @@ -11,10 +11,12 @@ import { CODE_QUALITY_ENABLED_SETTING, CODE_SECURITY_ENABLED_SETTING, IAC_ENABLED_SETTING, + ADVANCED_ORGANIZATION_SETTING, OSS_ENABLED_SETTING, SEVERITY_FILTER_SETTING, TRUSTED_FOLDERS, } from '../constants/settings'; +import { FEATURE_FLAGS } from '../constants/featureFlags'; import { ErrorHandler } from '../error/errorHandler'; import { ILog } from '../logger/interfaces'; import { errorsLogs } from '../messages/errors'; @@ -25,6 +27,9 @@ class ConfigurationWatcher implements IWatcher { constructor(private readonly logger: ILog) {} private async onChangeConfiguration(extension: IExtension, key: string): Promise { + if (key === ADVANCED_ORGANIZATION_SETTING) { + await configuration.fetchAndSetFeatureFlag(FEATURE_FLAGS.consistentIgnores); + } if (key === ADVANCED_ADVANCED_MODE_SETTING) { return extension.checkAdvancedMode(); } else if (key === OSS_ENABLED_SETTING) { @@ -63,6 +68,7 @@ class ConfigurationWatcher implements IWatcher { const change = [ ADVANCED_ADVANCED_MODE_SETTING, ADVANCED_AUTOSCAN_OSS_SETTING, + ADVANCED_ORGANIZATION_SETTING, OSS_ENABLED_SETTING, CODE_SECURITY_ENABLED_SETTING, CODE_QUALITY_ENABLED_SETTING, diff --git a/src/snyk/extension.ts b/src/snyk/extension.ts index bbf7786a7..4f3f67223 100644 --- a/src/snyk/extension.ts +++ b/src/snyk/extension.ts @@ -35,6 +35,7 @@ import { SNYK_VIEW_SUPPORT, SNYK_VIEW_WELCOME, } from './common/constants/views'; +import { FEATURE_FLAGS } from './common/constants/featureFlags'; import { ErrorHandler } from './common/error/errorHandler'; import { ErrorReporter } from './common/error/errorReporter'; import { ExperimentService } from './common/experiment/services/experimentService'; @@ -74,12 +75,9 @@ import { OssDetailPanelProvider } from './snykOss/providers/ossDetailPanelProvid import { OssVulnerabilityCountProvider } from './snykOss/providers/ossVulnerabilityCountProvider'; import OssIssueTreeProvider from './snykOss/providers/ossVulnerabilityTreeProvider'; import { OssVulnerabilityCountService } from './snykOss/services/vulnerabilityCount/ossVulnerabilityCountService'; -import type { FeatureFlagStatus } from './common/types'; import { CodeDetailPanelProvider } from './snykCode/views/suggestion/codeDetailPanelProvider'; class SnykExtension extends SnykLib implements IExtension { - private featureFlagStatus: FeatureFlagStatus | undefined; - public async activate(vscodeContext: vscode.ExtensionContext): Promise { extensionContext.setContext(vscodeContext); this.context = extensionContext; @@ -344,10 +342,7 @@ class SnykExtension extends SnykLib implements IExtension { await this.languageServer.start(); // Fetch feature flag to determine whether to use the new LSP-based rendering. - this.featureFlagStatus = await vsCodeCommands.executeCommand( - 'snyk.getFeatureFlagStatus', - 'snykCodeConsistentIgnores', - ); + await configuration.fetchAndSetFeatureFlag(FEATURE_FLAGS.consistentIgnores); const codeSecurityIssueProvider = new CodeSecurityIssueTreeProvider( this.viewManagerService, From b4dbf12385922559082e8f1df2a3edc1e57d78ce Mon Sep 17 00:00:00 2001 From: Catalina Oyaneder Date: Sun, 12 May 2024 20:00:45 +0100 Subject: [PATCH 07/11] chore: revert providers initialisation order --- src/snyk/extension.ts | 60 +++++++++++++++++++++---------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/src/snyk/extension.ts b/src/snyk/extension.ts index 4f3f67223..0be675641 100644 --- a/src/snyk/extension.ts +++ b/src/snyk/extension.ts @@ -248,6 +248,36 @@ class SnykExtension extends SnykLib implements IExtension { ); this.registerCommands(vscodeContext); + const codeSecurityIssueProvider = new CodeSecurityIssueTreeProvider( + this.viewManagerService, + this.contextService, + this.snykCode, + configuration, + vsCodeLanguages, + ); + + const codeQualityIssueProvider = new CodeQualityIssueTreeProvider( + this.viewManagerService, + this.contextService, + this.snykCode, + configuration, + vsCodeLanguages, + ); + + const codeSecurityTree = vscode.window.createTreeView(SNYK_VIEW_ANALYSIS_CODE_SECURITY, { + treeDataProvider: codeSecurityIssueProvider, + }); + const codeQualityTree = vscode.window.createTreeView(SNYK_VIEW_ANALYSIS_CODE_QUALITY, { + treeDataProvider: codeQualityIssueProvider, + }); + + vscodeContext.subscriptions.push( + vscode.window.registerTreeDataProvider(SNYK_VIEW_ANALYSIS_CODE_SECURITY, codeSecurityIssueProvider), + vscode.window.registerTreeDataProvider(SNYK_VIEW_ANALYSIS_CODE_QUALITY, codeQualityIssueProvider), + codeSecurityTree, + codeQualityTree, + ); + vscodeContext.subscriptions.push(vscode.window.registerTreeDataProvider(SNYK_VIEW_SUPPORT, new SupportProvider())); const welcomeTree = vscode.window.createTreeView(SNYK_VIEW_WELCOME, { @@ -344,36 +374,6 @@ class SnykExtension extends SnykLib implements IExtension { // Fetch feature flag to determine whether to use the new LSP-based rendering. await configuration.fetchAndSetFeatureFlag(FEATURE_FLAGS.consistentIgnores); - const codeSecurityIssueProvider = new CodeSecurityIssueTreeProvider( - this.viewManagerService, - this.contextService, - this.snykCode, - configuration, - vsCodeLanguages, - ); - - const codeQualityIssueProvider = new CodeQualityIssueTreeProvider( - this.viewManagerService, - this.contextService, - this.snykCode, - configuration, - vsCodeLanguages, - ); - - const codeSecurityTree = vscode.window.createTreeView(SNYK_VIEW_ANALYSIS_CODE_SECURITY, { - treeDataProvider: codeSecurityIssueProvider, - }); - const codeQualityTree = vscode.window.createTreeView(SNYK_VIEW_ANALYSIS_CODE_QUALITY, { - treeDataProvider: codeQualityIssueProvider, - }); - - vscodeContext.subscriptions.push( - vscode.window.registerTreeDataProvider(SNYK_VIEW_ANALYSIS_CODE_SECURITY, codeSecurityIssueProvider), - vscode.window.registerTreeDataProvider(SNYK_VIEW_ANALYSIS_CODE_QUALITY, codeQualityIssueProvider), - codeSecurityTree, - codeQualityTree, - ); - // initialize contexts await this.contextService.setContext(SNYK_CONTEXT.INITIALIZED, true); From fb03aa5298a51afed71f67d87588597bf7817e0e Mon Sep 17 00:00:00 2001 From: Catalina Oyaneder Date: Sun, 12 May 2024 20:03:16 +0100 Subject: [PATCH 08/11] revert: addition feature preview in settings and configuration --- package.json | 8 +------- src/snyk/common/configuration/configuration.ts | 2 -- src/snyk/common/languageServer/settings.ts | 2 -- src/test/unit/common/configuration.test.ts | 2 -- .../unit/common/languageServer/languageServer.test.ts | 2 -- src/test/unit/common/languageServer/middleware.test.ts | 1 - src/test/unit/common/languageServer/settings.test.ts | 1 - 7 files changed, 1 insertion(+), 17 deletions(-) diff --git a/package.json b/package.json index 8f6866141..56bc7339b 100644 --- a/package.json +++ b/package.json @@ -200,13 +200,7 @@ "title": "Preview feature toggles", "description": "Preview features that are currently in development. Setting keys will be removed when features become stable.", "propertyNames": true, - "properties": { - "snykCodeLsp": { - "type": "boolean", - "default": false, - "description": "Enable Snyk Code Panel Rendering via Language Server Protocol." - } - } + "properties": {} } } } diff --git a/src/snyk/common/configuration/configuration.ts b/src/snyk/common/configuration/configuration.ts index 7d0d64c7f..ceae2c2f8 100644 --- a/src/snyk/common/configuration/configuration.ts +++ b/src/snyk/common/configuration/configuration.ts @@ -48,7 +48,6 @@ export interface SeverityFilter { export type PreviewFeatures = { advisor: boolean | undefined; - snykCodeLsp: boolean | undefined; }; export interface IConfiguration { @@ -452,7 +451,6 @@ export class Configuration implements IConfiguration { getPreviewFeatures(): PreviewFeatures { const defaultSetting: PreviewFeatures = { advisor: false, - snykCodeLsp: false, }; const userSetting = diff --git a/src/snyk/common/languageServer/settings.ts b/src/snyk/common/languageServer/settings.ts index e7d76f069..eb7809ca6 100644 --- a/src/snyk/common/languageServer/settings.ts +++ b/src/snyk/common/languageServer/settings.ts @@ -33,7 +33,6 @@ export type ServerSettings = { // Trusted folders feature enableTrustedFoldersFeature?: string; trustedFolders?: string[]; - isSnykCodeLsp?: boolean; // Snyk integration settings integrationName?: string; @@ -76,7 +75,6 @@ export class LanguageServerSettings { integrationName: CLI_INTEGRATION_NAME, integrationVersion: await Configuration.getVersion(), deviceId: user.anonymousId, - isSnykCodeLsp: configuration.getPreviewFeatures().snykCodeLsp, }; } } diff --git a/src/test/unit/common/configuration.test.ts b/src/test/unit/common/configuration.test.ts index 0bb3fae67..797956f7b 100644 --- a/src/test/unit/common/configuration.test.ts +++ b/src/test/unit/common/configuration.test.ts @@ -186,14 +186,12 @@ suite('Configuration', () => { deepStrictEqual(configuration.getPreviewFeatures(), { advisor: false, - snykCodeLsp: false, } as PreviewFeatures); }); test('Preview features: some features enabled', () => { const previewFeatures = { advisor: false, - snykCodeLsp: false, } as PreviewFeatures; const workspace = stubWorkspaceConfiguration(FEATURES_PREVIEW_SETTING, previewFeatures); diff --git a/src/test/unit/common/languageServer/languageServer.test.ts b/src/test/unit/common/languageServer/languageServer.test.ts index bbc71db02..18c6a4b00 100644 --- a/src/test/unit/common/languageServer/languageServer.test.ts +++ b/src/test/unit/common/languageServer/languageServer.test.ts @@ -58,7 +58,6 @@ suite('Language Server', () => { getPreviewFeatures() { return { advisor: false, - snykCodeLsp: false, }; }, getFeaturesConfiguration() { @@ -219,7 +218,6 @@ suite('Language Server', () => { trustedFolders: ['/trusted/test/folder'], insecure: 'true', scanningMode: 'auto', - isSnykCodeLsp: false, }; deepStrictEqual(await languageServer.getInitializationOptions(), expectedInitializationOptions); diff --git a/src/test/unit/common/languageServer/middleware.test.ts b/src/test/unit/common/languageServer/middleware.test.ts index a34c8c764..dc13aaed2 100644 --- a/src/test/unit/common/languageServer/middleware.test.ts +++ b/src/test/unit/common/languageServer/middleware.test.ts @@ -34,7 +34,6 @@ suite('Language Server: Middleware', () => { getPreviewFeatures: () => { return { advisor: false, - snykCodeLsp: false, }; }, getFeaturesConfiguration() { diff --git a/src/test/unit/common/languageServer/settings.test.ts b/src/test/unit/common/languageServer/settings.test.ts index a716779ac..1d72d991c 100644 --- a/src/test/unit/common/languageServer/settings.test.ts +++ b/src/test/unit/common/languageServer/settings.test.ts @@ -14,7 +14,6 @@ suite('LanguageServerSettings', () => { // eslint-disable-next-line @typescript-eslint/require-await getToken: async () => 'snyk-token', getFeaturesConfiguration: () => ({}), // iacEnabled, codeSecurityEnabled, codeQualityEnabled are undefined - getPreviewFeatures: () => ({ snykCodeLsp: true }), getCliPath: () => '/path/to/cli', getAdditionalCliParameters: () => '--all-projects -d', getTrustedFolders: () => ['/trusted/path'], From 042d0866e22c7840df20b6ce0fccc192f30092ba Mon Sep 17 00:00:00 2001 From: Catalina Oyaneder Date: Sun, 12 May 2024 20:49:55 +0100 Subject: [PATCH 09/11] refactor: remove unnecessary `CodeDetailPanelProvider` - The conditional rendering logic can be achieved in `CodeSuggestionWebviewProvider` --- src/snyk/common/services/productService.ts | 2 +- src/snyk/extension.ts | 4 - src/snyk/snykCode/codeService.ts | 6 -- .../suggestion/codeDetailPanelProvider.ts | 77 ------------------- src/test/unit/snykCode/codeService.test.ts | 2 - 5 files changed, 1 insertion(+), 90 deletions(-) delete mode 100644 src/snyk/snykCode/views/suggestion/codeDetailPanelProvider.ts diff --git a/src/snyk/common/services/productService.ts b/src/snyk/common/services/productService.ts index d3ad5cb9a..efb4f06b1 100644 --- a/src/snyk/common/services/productService.ts +++ b/src/snyk/common/services/productService.ts @@ -42,7 +42,7 @@ export abstract class ProductService extends AnalysisStatusProvider implement constructor( readonly extensionContext: ExtensionContext, private readonly config: IConfiguration, - protected readonly suggestionProvider: IProductWebviewProvider>, + private readonly suggestionProvider: IProductWebviewProvider>, protected readonly viewManagerService: IViewManagerService, readonly workspace: IVSCodeWorkspace, private readonly workspaceTrust: IWorkspaceTrust, diff --git a/src/snyk/extension.ts b/src/snyk/extension.ts index 0be675641..deafb6372 100644 --- a/src/snyk/extension.ts +++ b/src/snyk/extension.ts @@ -75,7 +75,6 @@ import { OssDetailPanelProvider } from './snykOss/providers/ossDetailPanelProvid import { OssVulnerabilityCountProvider } from './snykOss/providers/ossVulnerabilityCountProvider'; import OssIssueTreeProvider from './snykOss/providers/ossVulnerabilityTreeProvider'; import { OssVulnerabilityCountService } from './snykOss/services/vulnerabilityCount/ossVulnerabilityCountService'; -import { CodeDetailPanelProvider } from './snykCode/views/suggestion/codeDetailPanelProvider'; class SnykExtension extends SnykLib implements IExtension { public async activate(vscodeContext: vscode.ExtensionContext): Promise { @@ -172,12 +171,9 @@ class SnykExtension extends SnykLib implements IExtension { this.learnService, ); - const codeDetailPanelProvider = new CodeDetailPanelProvider(vsCodeWindow, extensionContext, Logger); - this.snykCode = new SnykCodeService( this.context, configuration, - codeDetailPanelProvider, codeSuggestionProvider, new CodeActionAdapter(), this.codeActionKindAdapter, diff --git a/src/snyk/snykCode/codeService.ts b/src/snyk/snykCode/codeService.ts index 74780d908..946a2fdb9 100644 --- a/src/snyk/snykCode/codeService.ts +++ b/src/snyk/snykCode/codeService.ts @@ -12,15 +12,11 @@ import { IVSCodeLanguages } from '../common/vscode/languages'; import { IVSCodeWorkspace } from '../common/vscode/workspace'; import { SnykCodeActionsProvider } from './codeActions/codeIssuesActionsProvider'; import { ICodeSuggestionWebviewProvider } from './views/interfaces'; -import { CodeDetailPanelProvider } from './views/suggestion/codeDetailPanelProvider'; export class SnykCodeService extends ProductService { - private detailProvider: CodeDetailPanelProvider; - constructor( extensionContext: ExtensionContext, config: IConfiguration, - detailProvider: CodeDetailPanelProvider, suggestionProvider: ICodeSuggestionWebviewProvider, readonly codeActionAdapter: ICodeActionAdapter, readonly codeActionKindAdapter: ICodeActionKindAdapter, @@ -43,8 +39,6 @@ export class SnykCodeService extends ProductService { logger, ); - this.detailProvider = detailProvider; - this.registerCodeActionsProvider( new SnykCodeActionsProvider(this.result, codeActionAdapter, codeActionKindAdapter, languages), ); diff --git a/src/snyk/snykCode/views/suggestion/codeDetailPanelProvider.ts b/src/snyk/snykCode/views/suggestion/codeDetailPanelProvider.ts deleted file mode 100644 index 466a44406..000000000 --- a/src/snyk/snykCode/views/suggestion/codeDetailPanelProvider.ts +++ /dev/null @@ -1,77 +0,0 @@ -import * as vscode from 'vscode'; - -import { Issue, CodeIssueData } from '../../../common/languageServer/types'; -import { WebviewProvider, IWebViewProvider } from '../../../common/views/webviewProvider'; -import { ErrorHandler } from '../../../common/error/errorHandler'; -import { SNYK_VIEW_SUGGESTION_CODE } from '../../../common/constants/views'; -import { IVSCodeWindow } from '../../../common/vscode/window'; -import { ExtensionContext } from '../../../common/vscode/extensionContext'; -import { ILog } from '../../../common/logger/interfaces'; -import { WebviewPanelSerializer } from '../webviewPanelSerializer'; - -export class CodeDetailPanelProvider - extends WebviewProvider> - implements IWebViewProvider> -{ - private issue: Issue | undefined; - - constructor( - private readonly window: IVSCodeWindow, - protected readonly context: ExtensionContext, - protected readonly logger: ILog, - ) { - super(context, logger); - } - - protected getHtmlForWebview(_webview: vscode.Webview): string { - throw new Error('Method not implemented.'); - } - - activate(): void { - this.context.addDisposables( - this.window.registerWebviewPanelSerializer(SNYK_VIEW_SUGGESTION_CODE, new WebviewPanelSerializer(this)), - ); - } - - get openIssueId(): string | undefined { - return this.issue?.id; - } - - async showPanel(issue: Issue): Promise { - try { - await this.focusSecondEditorGroup(); - if (this.panel) { - this.panel.reveal(vscode.ViewColumn.Two, true); - // We will render HTML here. Let's start with a simple one. - this.panel.webview.html = issue.additionalData.details; - } else { - this.panel = vscode.window.createWebviewPanel( - SNYK_VIEW_SUGGESTION_CODE, - 'Snyk Code Issue', - { - viewColumn: vscode.ViewColumn.Two, - preserveFocus: true, - }, - this.getWebviewOptions(), - ); - this.registerListeners(); - } - this.issue = issue; - } catch (error) { - ErrorHandler.handle(error, this.logger, 'messages.errors.suggestionViewShowFailed'); - } - } - - protected registerListeners(): void { - if (!this.panel) return; - - this.panel.onDidDispose(() => this.onPanelDispose(), null, this.disposables); - this.panel.onDidChangeViewState(() => this.checkVisibility(), undefined, this.disposables); - // Handle messages from the webview - // this.panel.webview.onDidReceiveMessage( - // (msg: SuggestionMessage) => this.handleMessage(msg), - // undefined, - // this.disposables, - // ); - } -} diff --git a/src/test/unit/snykCode/codeService.test.ts b/src/test/unit/snykCode/codeService.test.ts index 491b865ce..261930d26 100644 --- a/src/test/unit/snykCode/codeService.test.ts +++ b/src/test/unit/snykCode/codeService.test.ts @@ -14,7 +14,6 @@ import { ICodeSuggestionWebviewProvider } from '../../../snyk/snykCode/views/int import { LanguageServerMock } from '../mocks/languageServer.mock'; import { languagesMock } from '../mocks/languages.mock'; import { LoggerMock } from '../mocks/logger.mock'; -import { CodeDetailPanelProvider } from '../../../snyk/snykCode/views/suggestion/codeDetailPanelProvider'; suite('Code Service', () => { let ls: ILanguageServer; @@ -32,7 +31,6 @@ suite('Code Service', () => { service = new SnykCodeService( {} as ExtensionContext, {} as IConfiguration, - {} as CodeDetailPanelProvider, {} as ICodeSuggestionWebviewProvider, {} as ICodeActionAdapter, { From c063763281ed9ce33e4cbbf34b2f65d08a1d18ca Mon Sep 17 00:00:00 2001 From: Catalina Oyaneder Date: Mon, 13 May 2024 10:17:57 +0100 Subject: [PATCH 10/11] fix: remove unnecessary var declaration --- src/snyk/common/constants/settings.ts | 1 - src/snyk/common/watchers/configurationWatcher.ts | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/snyk/common/constants/settings.ts b/src/snyk/common/constants/settings.ts index 34f21e29b..ab1440629 100644 --- a/src/snyk/common/constants/settings.ts +++ b/src/snyk/common/constants/settings.ts @@ -20,7 +20,6 @@ export const ADVANCED_ORGANIZATION = `${CONFIGURATION_IDENTIFIER}.advanced.organ export const ADVANCED_AUTOMATIC_DEPENDENCY_MANAGEMENT = `${CONFIGURATION_IDENTIFIER}.advanced.automaticDependencyManagement`; export const ADVANCED_CLI_PATH = `${CONFIGURATION_IDENTIFIER}.advanced.cliPath`; export const ADVANCED_CUSTOM_LS_PATH = `${CONFIGURATION_IDENTIFIER}.advanced.languageServerPath`; -export const ADVANCED_ORGANIZATION_SETTING = `${CONFIGURATION_IDENTIFIER}.advanced.organization`; export const SEVERITY_FILTER_SETTING = `${CONFIGURATION_IDENTIFIER}.severity`; export const TRUSTED_FOLDERS = `${CONFIGURATION_IDENTIFIER}.trustedFolders`; diff --git a/src/snyk/common/watchers/configurationWatcher.ts b/src/snyk/common/watchers/configurationWatcher.ts index d2b98d532..2a561d030 100644 --- a/src/snyk/common/watchers/configurationWatcher.ts +++ b/src/snyk/common/watchers/configurationWatcher.ts @@ -11,7 +11,7 @@ import { CODE_QUALITY_ENABLED_SETTING, CODE_SECURITY_ENABLED_SETTING, IAC_ENABLED_SETTING, - ADVANCED_ORGANIZATION_SETTING, + ADVANCED_ORGANIZATION, OSS_ENABLED_SETTING, SEVERITY_FILTER_SETTING, TRUSTED_FOLDERS, @@ -27,7 +27,7 @@ class ConfigurationWatcher implements IWatcher { constructor(private readonly logger: ILog) {} private async onChangeConfiguration(extension: IExtension, key: string): Promise { - if (key === ADVANCED_ORGANIZATION_SETTING) { + if (key === ADVANCED_ORGANIZATION) { await configuration.fetchAndSetFeatureFlag(FEATURE_FLAGS.consistentIgnores); } if (key === ADVANCED_ADVANCED_MODE_SETTING) { @@ -68,7 +68,7 @@ class ConfigurationWatcher implements IWatcher { const change = [ ADVANCED_ADVANCED_MODE_SETTING, ADVANCED_AUTOSCAN_OSS_SETTING, - ADVANCED_ORGANIZATION_SETTING, + ADVANCED_ORGANIZATION, OSS_ENABLED_SETTING, CODE_SECURITY_ENABLED_SETTING, CODE_QUALITY_ENABLED_SETTING, From cf8ff1ae15205f4b68f9ca4237b14e951e4b35e7 Mon Sep 17 00:00:00 2001 From: Catalina Oyaneder Date: Mon, 13 May 2024 15:00:14 +0100 Subject: [PATCH 11/11] refactor: move feature flag fetching to `ConfigurationWatcher` --- .../common/configuration/configuration.ts | 13 ------------ .../common/watchers/configurationWatcher.ts | 21 +++++++++++++++++-- src/snyk/extension.ts | 3 +-- 3 files changed, 20 insertions(+), 17 deletions(-) diff --git a/src/snyk/common/configuration/configuration.ts b/src/snyk/common/configuration/configuration.ts index c271e792e..cbf876683 100644 --- a/src/snyk/common/configuration/configuration.ts +++ b/src/snyk/common/configuration/configuration.ts @@ -27,8 +27,6 @@ import { } from '../constants/settings'; import SecretStorageAdapter from '../vscode/secretStorage'; import { IVSCodeWorkspace } from '../vscode/workspace'; -import { vsCodeCommands } from '../vscode/commands'; -import type { FeatureFlagStatus } from '../types'; export type FeaturesConfiguration = { ossEnabled: boolean | undefined; @@ -57,8 +55,6 @@ export interface IConfiguration { authHost: string; - fetchAndSetFeatureFlag(flagName: string): Promise; - getFeatureFlag(flagName: string): boolean; setFeatureFlag(flagName: string, value: boolean): void; @@ -145,15 +141,6 @@ export class Configuration implements IConfiguration { return preview; } - public async fetchAndSetFeatureFlag(flagName: string): Promise { - try { - const ffStatus = await vsCodeCommands.executeCommand(SNYK_FEATURE_FLAG_COMMAND, flagName); - this.setFeatureFlag(flagName, ffStatus?.ok ?? false); - } catch (error) { - console.warn(`Failed to fetch feature flag ${flagName}: ${error}`); - } - } - getFeatureFlag(flagName: string): boolean { return this.featureFlag[flagName] ?? false; } diff --git a/src/snyk/common/watchers/configurationWatcher.ts b/src/snyk/common/watchers/configurationWatcher.ts index 2a561d030..022fcea26 100644 --- a/src/snyk/common/watchers/configurationWatcher.ts +++ b/src/snyk/common/watchers/configurationWatcher.ts @@ -22,13 +22,17 @@ import { ILog } from '../logger/interfaces'; import { errorsLogs } from '../messages/errors'; import SecretStorageAdapter from '../vscode/secretStorage'; import { IWatcher } from './interfaces'; +import { IVSCodeCommands } from '../vscode/commands'; +import { SNYK_FEATURE_FLAG_COMMAND } from '../constants/commands'; +import { FeatureFlagStatus } from '../types'; class ConfigurationWatcher implements IWatcher { - constructor(private readonly logger: ILog) {} + constructor(private readonly logger: ILog, private commandExecutor: IVSCodeCommands) {} private async onChangeConfiguration(extension: IExtension, key: string): Promise { if (key === ADVANCED_ORGANIZATION) { - await configuration.fetchAndSetFeatureFlag(FEATURE_FLAGS.consistentIgnores); + const isEnabled = await this.fetchFeatureFlag(FEATURE_FLAGS.consistentIgnores); + configuration.setFeatureFlag(FEATURE_FLAGS.consistentIgnores, isEnabled); } if (key === ADVANCED_ADVANCED_MODE_SETTING) { return extension.checkAdvancedMode(); @@ -94,6 +98,19 @@ class ConfigurationWatcher implements IWatcher { } }); } + + private async fetchFeatureFlag(flagName: string): Promise { + try { + const ffStatus = await this.commandExecutor.executeCommand( + SNYK_FEATURE_FLAG_COMMAND, + flagName, + ); + return ffStatus?.ok ?? false; + } catch (error) { + console.warn(`Failed to fetch feature flag ${flagName}: ${error}`); + return false; + } + } } export default ConfigurationWatcher; diff --git a/src/snyk/extension.ts b/src/snyk/extension.ts index deafb6372..60f25db5d 100644 --- a/src/snyk/extension.ts +++ b/src/snyk/extension.ts @@ -119,7 +119,7 @@ class SnykExtension extends SnykLib implements IExtension { SecretStorageAdapter.init(vscodeContext); - this.configurationWatcher = new ConfigurationWatcher(Logger); + this.configurationWatcher = new ConfigurationWatcher(Logger, vsCodeCommands); this.notificationService = new NotificationService(vsCodeWindow, vsCodeCommands, configuration, Logger); this.statusBarItem.show(); @@ -368,7 +368,6 @@ class SnykExtension extends SnykLib implements IExtension { await this.languageServer.start(); // Fetch feature flag to determine whether to use the new LSP-based rendering. - await configuration.fetchAndSetFeatureFlag(FEATURE_FLAGS.consistentIgnores); // initialize contexts await this.contextService.setContext(SNYK_CONTEXT.INITIALIZED, true);