Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: display branch selection in the tree node [IDE-446] #489

Merged
merged 11 commits into from
Jul 17, 2024
3 changes: 3 additions & 0 deletions media/images/branch_icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions media/images/pencil.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
20 changes: 18 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,12 @@
{
"id": "snyk.views.analysis.code.security",
"name": "Code Security",
"when": "snyk:initialized && snyk:loggedIn && snyk:codeEnabled && snyk:workspaceFound && !snyk:error"
"when": "snyk:initialized && snyk:loggedIn && snyk:codeEnabled && snyk:workspaceFound && !snyk:error && !snyk:deltaFindingsEnabled"
},
{
"id": "snyk.views.analysis.code.security.delta",
"name": "Code Security - New Issues",
"when": "snyk:initialized && snyk:loggedIn && snyk:codeEnabled && snyk:workspaceFound && !snyk:error && snyk:deltaFindingsEnabled"
},
{
"id": "snyk.views.analysis.configuration",
Expand All @@ -262,7 +267,12 @@
{
"id": "snyk.views.analysis.code.quality",
"name": "Code Quality",
"when": "snyk:initialized && snyk:loggedIn && snyk:codeEnabled && snyk:workspaceFound && !snyk:error"
"when": "snyk:initialized && snyk:loggedIn && snyk:codeEnabled && snyk:workspaceFound && !snyk:error && !snyk:deltaFindingsEnabled"
},
{
"id": "snyk.views.analysis.code.quality.delta",
"name": "Code Quality - New Issues",
"when": "snyk:initialized && snyk:loggedIn && snyk:codeEnabled && snyk:workspaceFound && !snyk:error && snyk:deltaFindingsEnabled"
},
{
"id": "snyk.views.analysis.code.enablement",
Expand Down Expand Up @@ -341,6 +351,12 @@
"category": "Snyk",
"icon": "$(run)"
},
{
"command": "snyk.setBaseBranch",
"title": "Set Base Branch",
"category": "Snyk",
"icon": "$(sign-in)"
},
{
"command": "snyk.settings",
"title": "Settings",
Expand Down
13 changes: 13 additions & 0 deletions src/snyk/base/services/authenticationService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@

setToken(): Promise<void>;

setBaseBranch(): Promise<void>;

updateToken(token: string): Promise<void>;
}

Expand Down Expand Up @@ -64,6 +66,17 @@
return await this.clientAdapter.getLanguageClient().sendNotification(DID_CHANGE_CONFIGURATION_METHOD, {});
}

//TODO: move this function to another service
async setBaseBranch(): Promise<void> {
const branch = await this.window.showInputBox({

Check warning on line 71 in src/snyk/base/services/authenticationService.ts

View workflow job for this annotation

GitHub Actions / Build and Test (ubuntu-latest)

'branch' is assigned a value but never used. Allowed unused vars must match /^_/u

Check warning on line 71 in src/snyk/base/services/authenticationService.ts

View workflow job for this annotation

GitHub Actions / Build and Test (macos-latest)

'branch' is assigned a value but never used. Allowed unused vars must match /^_/u

Check warning on line 71 in src/snyk/base/services/authenticationService.ts

View workflow job for this annotation

GitHub Actions / Build and Test (windows-latest)

'branch' is assigned a value but never used. Allowed unused vars must match /^_/u
//TODO: replace 'main' with the list of local branches
placeHolder: 'main',
});

//TODO: capture the value of branch, and send this to Snyk Language Server.
return;
}

validateToken(token: string) {
let valid = uuidValidate(token);
if (valid) return true;
Expand Down
5 changes: 5 additions & 0 deletions src/snyk/common/commands/commandController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { IacIssueCommandArg } from '../../snykIac/views/interfaces';
import { OssService } from '../../snykOss/ossService';
import {
SNYK_INITIATE_LOGIN_COMMAND,
SNYK_SET_BASE_BRANCH_COMMAND,
SNYK_LOGIN_COMMAND,
SNYK_OPEN_BROWSER_COMMAND,
SNYK_SET_TOKEN_COMMAND,
Expand Down Expand Up @@ -60,6 +61,10 @@ export class CommandController {
await this.executeCommand(SNYK_SET_TOKEN_COMMAND, this.authService.setToken.bind(this.authService));
}

async setBaseBranch(): Promise<void> {
await this.executeCommand(SNYK_SET_BASE_BRANCH_COMMAND, this.authService.setBaseBranch.bind(this.authService));
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this will probably not stay like this, but use a setting


async openLocal(path: Uri, range?: Range): Promise<void> {
try {
await this.window.showTextDocumentViaUri(path, { viewColumn: 1, selection: range });
Expand Down
2 changes: 2 additions & 0 deletions src/snyk/common/configuration/configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ export interface SeverityFilter {

export type PreviewFeatures = {
advisor: boolean | undefined;
deltaFindings: boolean | undefined;
};

export interface IConfiguration {
Expand Down Expand Up @@ -423,6 +424,7 @@ export class Configuration implements IConfiguration {
getPreviewFeatures(): PreviewFeatures {
const defaultSetting: PreviewFeatures = {
advisor: false,
deltaFindings: false,
};

const userSetting =
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 @@ -5,6 +5,7 @@ export const VSCODE_ADD_COMMENT_COMMAND = 'editor.action.addCommentLine';

// custom Snyk commands
export const SNYK_START_COMMAND = 'snyk.start';
export const SNYK_SET_BASE_BRANCH_COMMAND = 'snyk.setBaseBranch';
export const SNYK_INITIATE_LOGIN_COMMAND = 'snyk.initiateLogin';
export const SNYK_SET_TOKEN_COMMAND = 'snyk.setToken';
export const SNYK_ENABLE_CODE_COMMAND = 'snyk.enableCode';
Expand Down
3 changes: 3 additions & 0 deletions src/snyk/common/constants/views.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
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_WITH_DELTA = 'snyk.views.analysis.code.security.delta';
export const SNYK_VIEW_ANALYSIS_CODE_QUALITY = 'snyk.views.analysis.code.quality';
export const SNYK_VIEW_ANALYSIS_CODE_QUALITY_WITH_DELTA = 'snyk.views.analysis.code.quality.delta';
export const SNYK_VIEW_ANALYSIS_OSS = 'snyk.views.analysis.oss';
export const SNYK_VIEW_SUPPORT = 'snyk.views.support';
export const SNYK_VIEW_SUGGESTION_CODE = 'snyk.views.suggestion.code';
Expand All @@ -21,6 +23,7 @@ export const SNYK_CONTEXT = {
ERROR: 'error',
MODE: 'mode',
ADVANCED: 'advanced',
DELTA_FINDINGS_ENABLED: 'deltaFindingsEnabled',
};

export const SNYK_ERROR_CODES = {
Expand Down
8 changes: 7 additions & 1 deletion src/snyk/common/services/productService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { IConfiguration } from '../configuration/configuration';
import { IWorkspaceTrust } from '../configuration/trustedFolders';
import { CodeActionsProvider } from '../editor/codeActionsProvider';
import { ILanguageServer } from '../languageServer/languageServer';
import { Issue, Scan, ScanStatus } from '../languageServer/types';
import { Issue, Scan, ScanProduct, ScanStatus } from '../languageServer/types';
import { ILog } from '../logger/interfaces';
import { IViewManagerService } from '../services/viewManagerService';
import { IProductWebviewProvider } from '../views/webviewProvider';
Expand All @@ -29,6 +29,8 @@ export interface IProductService<T> extends AnalysisStatusProvider, Disposable {
}

export abstract class ProductService<T> extends AnalysisStatusProvider implements IProductService<T> {
abstract readonly productType: ScanProduct;

private _result: ProductResult<T>;
readonly newResultAvailable$ = new Subject<void>();

Expand Down Expand Up @@ -59,6 +61,10 @@ export abstract class ProductService<T> extends AnalysisStatusProvider implement

abstract refreshTreeView(): void;

public getSnykProductType(): ScanProduct {
return this.productType;
}

registerCodeActionsProvider(provider: CodeActionsProvider<T>) {
this.languages.registerCodeActionsProvider({ scheme: 'file', language: '*' }, provider);
}
Expand Down
26 changes: 24 additions & 2 deletions src/snyk/common/views/issueTreeProvider.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import _, { flatten } from 'lodash';
import * as vscode from 'vscode'; // todo: invert dependency
import { IConfiguration, IssueViewOptions } from '../../common/configuration/configuration';
import { Issue, IssueSeverity } from '../../common/languageServer/types';
import { Issue, IssueSeverity, ScanProduct } from '../../common/languageServer/types';
import { messages as commonMessages } from '../../common/messages/analysisMessages';
import { IContextService } from '../../common/services/contextService';
import { IProductService } from '../../common/services/productService';
import { IProductService, ProductService } from '../../common/services/productService';
import { AnalysisTreeNodeProvider } from '../../common/views/analysisTreeNodeProvider';
import { INodeIcon, InternalType, NODE_ICONS, TreeNode } from '../../common/views/treeNode';
import { IVSCodeLanguages } from '../../common/vscode/languages';
Expand Down Expand Up @@ -49,6 +49,8 @@ export abstract class ProductIssueTreeProvider<T> extends AnalysisTreeNodeProvid
filteredIssues?: Issue<T>[],
): Command;

abstract setBaseBranchCommand(): Command;

getRootChildren(): TreeNode[] {
const nodes: TreeNode[] = [];

Expand Down Expand Up @@ -94,6 +96,12 @@ export abstract class ProductIssueTreeProvider<T> extends AnalysisTreeNodeProvid
}),
this.getFixableIssuesNode(this.getFixableCount()),
];

const isSnykCodeProduct = (this.productService as ProductService<T>).getSnykProductType() === ScanProduct.Code;
if (isSnykCodeProduct) {
topNodes.unshift(this.getBaseBranch());
}

const noSeverityFiltersSelectedWarning = this.getNoSeverityFiltersSelectedTreeNode();
if (noSeverityFiltersSelectedWarning !== null) {
topNodes.push(noSeverityFiltersSelectedWarning);
Expand All @@ -109,6 +117,20 @@ export abstract class ProductIssueTreeProvider<T> extends AnalysisTreeNodeProvid
return nodes;
}

getBaseBranch(): TreeNode | null {
const deltaFindingsEnabled = this.configuration.getDeltaFindingsEnabled();

//TODO: get the actual base branch from Snyk Language Server
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should just ask a config setting, what's the base branch for the folder. LS sends a folderConfig notification for all known workspace folders when they are registered at LS.

if (deltaFindingsEnabled) {
return new TreeNode({
text: 'Base branch: main',
icon: NODE_ICONS.pencil,
command: this.setBaseBranchCommand(),
});
}
return null;
}

getFixableIssuesNode(_fixableIssueCount: number): TreeNode | null {
return null; // optionally overridden by products
}
Expand Down
10 changes: 9 additions & 1 deletion src/snyk/common/views/treeNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export interface INodeIcon {
['dark']: string;
}

type NODE_ICON_TYPE = 'critical' | 'high' | 'medium' | 'low' | 'error';
type NODE_ICON_TYPE = 'critical' | 'high' | 'medium' | 'low' | 'error' | 'branch' | 'pencil';

export const NODE_ICONS: { [key in NODE_ICON_TYPE]: INodeIcon } = {
critical: {
Expand All @@ -30,6 +30,14 @@ export const NODE_ICONS: { [key in NODE_ICON_TYPE]: INodeIcon } = {
light: path.join(__filename, '..', '..', '..', '..', '..', 'media', 'images', 'warning.svg'),
dark: path.join(__filename, '..', '..', '..', '..', '..', 'media', 'images', 'warning.svg'),
},
branch: {
light: path.join(__filename, '..', '..', '..', '..', '..', 'media', 'images', 'branch_icon.svg'),
dark: path.join(__filename, '..', '..', '..', '..', '..', 'media', 'images', 'branch_icon.svg'),
},
pencil: {
light: path.join(__filename, '..', '..', '..', '..', '..', 'media', 'images', 'branch_icon.svg'),
dark: path.join(__filename, '..', '..', '..', '..', '..', 'media', 'images', 'pencil.svg'),
},
};

export type InternalType = {
Expand Down
25 changes: 21 additions & 4 deletions src/snyk/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
SNYK_OPEN_ISSUE_COMMAND,
SNYK_OPEN_LOCAL_COMMAND,
SNYK_SET_TOKEN_COMMAND,
SNYK_SET_BASE_BRANCH_COMMAND,
SNYK_SETTINGS_COMMAND,
SNYK_SHOW_LS_OUTPUT_COMMAND,
SNYK_SHOW_OUTPUT_COMMAND,
Expand All @@ -29,7 +30,9 @@ import {
SNYK_CONTEXT,
SNYK_VIEW_ANALYSIS_CODE_ENABLEMENT,
SNYK_VIEW_ANALYSIS_CODE_QUALITY,
SNYK_VIEW_ANALYSIS_CODE_QUALITY_WITH_DELTA,
SNYK_VIEW_ANALYSIS_CODE_SECURITY,
SNYK_VIEW_ANALYSIS_CODE_SECURITY_WITH_DELTA,
SNYK_VIEW_ANALYSIS_IAC,
SNYK_VIEW_ANALYSIS_OSS,
SNYK_VIEW_SUPPORT,
Expand Down Expand Up @@ -124,6 +127,11 @@ class SnykExtension extends SnykLib implements IExtension {

this.statusBarItem.show();

const previewFeatures = configuration.getPreviewFeatures();
if (previewFeatures.deltaFindings) {
await this.contextService.setContext(SNYK_CONTEXT.DELTA_FINDINGS_ENABLED, true);
}

const languageClientAdapter = new LanguageClientAdapter();
this.authService = new AuthenticationService(
this.contextService,
Expand Down Expand Up @@ -260,16 +268,24 @@ class SnykExtension extends SnykLib implements IExtension {
vsCodeLanguages,
);

const codeSecurityTree = vscode.window.createTreeView(SNYK_VIEW_ANALYSIS_CODE_SECURITY, {
let securityCodeView = SNYK_VIEW_ANALYSIS_CODE_SECURITY;
let codeQualityView = SNYK_VIEW_ANALYSIS_CODE_QUALITY;
if (previewFeatures.deltaFindings) {
securityCodeView = SNYK_VIEW_ANALYSIS_CODE_SECURITY_WITH_DELTA;
codeQualityView = SNYK_VIEW_ANALYSIS_CODE_QUALITY_WITH_DELTA;
}

const codeSecurityTree = vscode.window.createTreeView(securityCodeView, {
treeDataProvider: codeSecurityIssueProvider,
});
const codeQualityTree = vscode.window.createTreeView(SNYK_VIEW_ANALYSIS_CODE_QUALITY, {

const codeQualityTree = vscode.window.createTreeView(codeQualityView, {
treeDataProvider: codeQualityIssueProvider,
});

vscodeContext.subscriptions.push(
vscode.window.registerTreeDataProvider(SNYK_VIEW_ANALYSIS_CODE_SECURITY, codeSecurityIssueProvider),
vscode.window.registerTreeDataProvider(SNYK_VIEW_ANALYSIS_CODE_QUALITY, codeQualityIssueProvider),
vscode.window.registerTreeDataProvider(securityCodeView, codeSecurityIssueProvider),
vscode.window.registerTreeDataProvider(codeQualityView, codeQualityIssueProvider),
codeSecurityTree,
codeQualityTree,
);
Expand Down Expand Up @@ -409,6 +425,7 @@ class SnykExtension extends SnykLib implements IExtension {
),
vscode.commands.registerCommand(SNYK_INITIATE_LOGIN_COMMAND, () => this.commandController.initiateLogin()),
vscode.commands.registerCommand(SNYK_SET_TOKEN_COMMAND, () => this.commandController.setToken()),
vscode.commands.registerCommand(SNYK_SET_BASE_BRANCH_COMMAND, () => this.commandController.setBaseBranch()),
vscode.commands.registerCommand(SNYK_ENABLE_CODE_COMMAND, () =>
this.commandController.executeCommand(SNYK_ENABLE_CODE_COMMAND, () => this.enableCode()),
),
Expand Down
2 changes: 2 additions & 0 deletions src/snyk/snykCode/codeService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import { SnykCodeActionsProvider } from './codeActions/codeIssuesActionsProvider
import { ICodeSuggestionWebviewProvider } from './views/interfaces';

export class SnykCodeService extends ProductService<CodeIssueData> {
public readonly productType = ScanProduct.Code;

constructor(
extensionContext: ExtensionContext,
config: IConfiguration,
Expand Down
9 changes: 8 additions & 1 deletion src/snyk/snykCode/views/issueTreeProvider.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Command, Range } from 'vscode';
import { OpenCommandIssueType, OpenIssueCommandArg } from '../../common/commands/types';
import { IConfiguration } from '../../common/configuration/configuration';
import { SNYK_OPEN_ISSUE_COMMAND } from '../../common/constants/commands';
import { SNYK_OPEN_ISSUE_COMMAND, SNYK_SET_BASE_BRANCH_COMMAND } from '../../common/constants/commands';
import { CodeIssueData, Issue } from '../../common/languageServer/types';
import { IContextService } from '../../common/services/contextService';
import { IProductService } from '../../common/services/productService';
Expand Down Expand Up @@ -70,6 +70,13 @@ export class IssueTreeProvider extends ProductIssueTreeProvider<CodeIssueData> {
};
}

setBaseBranchCommand(): Command {
return {
command: SNYK_SET_BASE_BRANCH_COMMAND,
title: '',
};
}

isFixableIssue(issue: Issue<CodeIssueData>): boolean {
return issue.additionalData.hasAIFix;
}
Expand Down
2 changes: 2 additions & 0 deletions src/snyk/snykCode/views/securityIssueTreeProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,13 @@ export default class CodeSecurityIssueTreeProvider extends IssueTreeProvider {
protected getIssueFoundText(nIssues: number, ignoredIssueCount: number): string {
if (nIssues > 0) {
let text;

if (nIssues === 1) {
text = `${nIssues} vulnerability found by Snyk`;
Copy link
Contributor

@bastiandoetsch bastiandoetsch Jul 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be issues, too?

Suggested change
text = `${nIssues} vulnerability found by Snyk`;
text = `${nIssues} issue found by Snyk`;

} else {
text = `✋ ${nIssues} vulnerabilities found by Snyk`;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
text = `✋ ${nIssues} vulnerabilities found by Snyk`;
text = `✋ ${nIssues} issues found by Snyk`;

}

const isIgnoresEnabled = configuration.getFeatureFlag(FEATURE_FLAGS.consistentIgnores);
if (isIgnoresEnabled) {
text += `, ${ignoredIssueCount} ignored`;
Expand Down
2 changes: 2 additions & 0 deletions src/snyk/snykIac/iacService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import { IacCodeActionsProvider } from './codeActions/iacCodeActionsProvider';
import { IIacSuggestionWebviewProvider } from './views/interfaces';

export class IacService extends ProductService<IacIssueData> {
public readonly productType = ScanProduct.InfrastructureAsCode;

constructor(
extensionContext: ExtensionContext,
config: IConfiguration,
Expand Down
9 changes: 8 additions & 1 deletion src/snyk/snykIac/views/iacIssueTreeProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Command, Range } from 'vscode';
import { OpenCommandIssueType, OpenIssueCommandArg } from '../../common/commands/types';
import { IConfiguration } from '../../common/configuration/configuration';
import { configuration } from '../../common/configuration/instance';
import { SNYK_OPEN_ISSUE_COMMAND } from '../../common/constants/commands';
import { SNYK_OPEN_ISSUE_COMMAND, SNYK_SET_BASE_BRANCH_COMMAND } from '../../common/constants/commands';
import { SNYK_ANALYSIS_STATUS } from '../../common/constants/views';
import { IacIssueData, Issue } from '../../common/languageServer/types';
import { IContextService } from '../../common/services/contextService';
Expand Down Expand Up @@ -81,4 +81,11 @@ export default class IacIssueTreeProvider extends ProductIssueTreeProvider<IacIs
],
};
}

setBaseBranchCommand(): Command {
return {
command: SNYK_SET_BASE_BRANCH_COMMAND,
title: '',
};
}
}
Loading
Loading