Skip to content

Commit

Permalink
feat: handle LS folderConfigs [IDE-502] (#491)
Browse files Browse the repository at this point in the history
* feat: folderConfigs LS integration wip

* fix: render base branch treenode correctly

* feat: setBaseBranch command

* chore: lint

* fix: mock getFolderConfigs in tests

* fix: show base branch node if no issues found

* chroe: add missing keywords

* chore: update CHANGELOG.md

---------

Co-authored-by: Catalina Oyaneder <[email protected]>
  • Loading branch information
ShawkyZ and Catalina Oyaneder authored Jul 19, 2024
1 parent a81fded commit fcea0e8
Show file tree
Hide file tree
Showing 24 changed files with 267 additions and 39 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Snyk Security Changelog

## [2.15.0]
- Sync with LS to retrieve and persist folderConfigs changes.
- Add command to select the base branch.

## [2.14.0]
- Add UI components for selecting a base branch for delta findings for Code and Code Quality behind a feature flag.

Expand Down
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,11 @@
"default": [],
"description": "Folders to trust for Snyk scans."
},
"snyk.folderConfigs": {
"type": "array",
"default": [],
"description": "Folder configuration for Snyk scans."
},
"snyk.features.preview": {
"type": "object",
"default": {},
Expand Down
3 changes: 3 additions & 0 deletions src/snyk/base/modules/baseSnykModule.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { CommandController } from '../../common/commands/commandController';
import { FolderConfigs, IFolderConfigs } from '../../common/configuration/folderConfigs';
import { IWorkspaceTrust, WorkspaceTrust } from '../../common/configuration/trustedFolders';
import { ExperimentService } from '../../common/experiment/services/experimentService';
import { ILanguageServer } from '../../common/languageServer/languageServer';
Expand Down Expand Up @@ -63,6 +64,7 @@ export default abstract class BaseSnykModule implements IBaseSnykModule {
protected markdownStringAdapter: IMarkdownStringAdapter;
readonly workspaceTrust: IWorkspaceTrust;
readonly codeActionKindAdapter: ICodeActionKindAdapter;
readonly folderConfigs: IFolderConfigs;

constructor() {
this.statusBarItem = new SnykStatusBarItem();
Expand All @@ -74,6 +76,7 @@ export default abstract class BaseSnykModule implements IBaseSnykModule {
this.markdownStringAdapter = new MarkdownStringAdapter();
this.workspaceTrust = new WorkspaceTrust();
this.codeActionKindAdapter = new CodeActionKindAdapter();
this.folderConfigs = new FolderConfigs();
}

abstract runScan(): Promise<void>;
Expand Down
7 changes: 7 additions & 0 deletions src/snyk/common/commands/commandController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ import { IUriAdapter } from '../vscode/uri';
import { IVSCodeWindow } from '../vscode/window';
import { IVSCodeWorkspace } from '../vscode/workspace';
import { OpenCommandIssueType, OpenIssueCommandArg } from './types';
import { IFolderConfigs } from '../configuration/folderConfigs';
import { IConfiguration } from '../configuration/configuration';

export class CommandController {
private debouncedCommands: Record<string, _.DebouncedFunc<(...args: unknown[]) => Promise<unknown>>> = {};
Expand All @@ -43,6 +45,8 @@ export class CommandController {
private window: IVSCodeWindow,
private languageServer: ILanguageServer,
private logger: ILog,
private configuration: IConfiguration,
private folderConfigs: IFolderConfigs,
) {}

openBrowser(url: string): unknown {
Expand Down Expand Up @@ -75,6 +79,9 @@ export class CommandController {
ErrorHandler.handle(e, this.logger);
}
}
async setBaseBranch(folderPath: string): Promise<void> {
await this.folderConfigs.setBranch(this.window, this.configuration, folderPath);
}

openSettings(): void {
void this.commands.executeCommand(VSCODE_GO_TO_SETTINGS_COMMAND, `@ext:${SNYK_PUBLISHER}.${SNYK_NAME_EXTENSION}`);
Expand Down
27 changes: 27 additions & 0 deletions src/snyk/common/configuration/configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
YES_CRASH_REPORT_SETTING,
YES_WELCOME_NOTIFICATION_SETTING,
DELTA_FINDINGS,
FOLDER_CONFIGS,
} from '../constants/settings';
import SecretStorageAdapter from '../vscode/secretStorage';
import { IVSCodeWorkspace } from '../vscode/workspace';
Expand All @@ -36,6 +37,12 @@ export type FeaturesConfiguration = {
iacEnabled: boolean | undefined;
};

export type FolderConfig = {
folderPath: string;
baseBranch: string;
localBranches: string[] | undefined;
};

export interface IssueViewOptions {
ignoredIssues: boolean;
openIssues: boolean;
Expand Down Expand Up @@ -122,6 +129,10 @@ export interface IConfiguration {
setEndpoint(endpoint: string): Promise<void>;

getDeltaFindingsEnabled(): boolean;

getFolderConfigs(): FolderConfig[];

setFolderConfigs(folderConfig: FolderConfig[]): Promise<void>;
}

export class Configuration implements IConfiguration {
Expand Down Expand Up @@ -463,6 +474,13 @@ export class Configuration implements IConfiguration {
);
}

getFolderConfigs(): FolderConfig[] {
return (
this.workspace.getConfiguration<FolderConfig[]>(CONFIGURATION_IDENTIFIER, this.getConfigName(FOLDER_CONFIGS)) ||
[]
);
}

get scanningMode(): string | undefined {
return this.workspace.getConfiguration<string>(CONFIGURATION_IDENTIFIER, this.getConfigName(SCANNING_MODE));
}
Expand All @@ -476,5 +494,14 @@ export class Configuration implements IConfiguration {
);
}

async setFolderConfigs(folderConfigs: FolderConfig[]): Promise<void> {
await this.workspace.updateConfiguration(
CONFIGURATION_IDENTIFIER,
this.getConfigName(FOLDER_CONFIGS),
folderConfigs,
true,
);
}

private getConfigName = (setting: string) => setting.replace(`${CONFIGURATION_IDENTIFIER}.`, '');
}
69 changes: 69 additions & 0 deletions src/snyk/common/configuration/folderConfigs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { IVSCodeWindow } from '../vscode/window';
import { FolderConfig, IConfiguration } from './configuration';

export interface IFolderConfigs {
getFolderConfigs(config: IConfiguration): ReadonlyArray<FolderConfig>;
getFolderConfig(config: IConfiguration, folderPath: string): FolderConfig | undefined;
setFolderConfig(config: IConfiguration, folderConfig: FolderConfig): Promise<void>;
setBranch(window: IVSCodeWindow, config: IConfiguration, folderPath: string): Promise<void>;
resetFolderConfigsCache(): void;
}

export class FolderConfigs implements IFolderConfigs {
private folderConfigsCache?: ReadonlyArray<FolderConfig>;

getFolderConfig(config: IConfiguration, folderPath: string): FolderConfig | undefined {
const folderConfigs = this.getFolderConfigs(config);
return folderConfigs.find(i => i.folderPath === folderPath);
}

getFolderConfigs(config: IConfiguration): ReadonlyArray<FolderConfig> {
if (this.folderConfigsCache !== undefined) {
return this.folderConfigsCache;
}
const folderConfigs = config.getFolderConfigs();
this.folderConfigsCache = folderConfigs;

return folderConfigs;
}

async setBranch(window: IVSCodeWindow, config: IConfiguration, folderPath: string): Promise<void> {
const folderConfig = this.getFolderConfig(config, folderPath);

if (!folderConfig) {
return;
}

const branchName = await window.showInputBox({
placeHolder: '',
validateInput: input => {
const valid = this.validateBranchName(input, folderConfig.localBranches ?? []);
if (!valid) {
return "The chosen branch name doesn't exist.";
}
},
});
if (!branchName) {
return;
}

folderConfig.baseBranch = branchName;
await this.setFolderConfig(config, folderConfig);
}

private validateBranchName(branchName: string, branchList: string[]): boolean {
return branchList.includes(branchName);
}

async setFolderConfig(config: IConfiguration, folderConfig: FolderConfig): Promise<void> {
const currentFolderConfigs = this.getFolderConfigs(config);
const finalFolderConfigs = currentFolderConfigs.map(i =>
i.folderPath === folderConfig.folderPath ? folderConfig : i,
);
await config.setFolderConfigs(finalFolderConfigs);
}

resetFolderConfigsCache() {
this.folderConfigsCache = undefined;
}
}
1 change: 1 addition & 0 deletions src/snyk/common/constants/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export const SNYK_SHOW_OUTPUT_COMMAND = 'snyk.showOutputChannel';
export const SNYK_SHOW_LS_OUTPUT_COMMAND = 'snyk.showLsOutputChannel';
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';
// commands
export const SNYK_LOGIN_COMMAND = 'snyk.login';
export const SNYK_WORKSPACE_SCAN_COMMAND = 'snyk.workspace.scan';
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_CLI_PATH = '$/snyk.isAvailableCli';
export const SNYK_ADD_TRUSTED_FOLDERS = '$/snyk.addTrustedFolders';
export const SNYK_SCAN = '$/snyk.scan';
export const SNYK_FOLDERCONFIG = '$/snyk.folderConfigs';
1 change: 1 addition & 0 deletions src/snyk/common/constants/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export const ADVANCED_CUSTOM_LS_PATH = `${CONFIGURATION_IDENTIFIER}.advanced.lan
export const ISSUE_VIEW_OPTIONS_SETTING = `${CONFIGURATION_IDENTIFIER}.issueViewOptions`;
export const SEVERITY_FILTER_SETTING = `${CONFIGURATION_IDENTIFIER}.severity`;
export const TRUSTED_FOLDERS = `${CONFIGURATION_IDENTIFIER}.trustedFolders`;
export const FOLDER_CONFIGS = `${CONFIGURATION_IDENTIFIER}.folderConfigs`;
export const SCANNING_MODE = `${CONFIGURATION_IDENTIFIER}.scanningMode`;

export const DELTA_FINDINGS = `${FEATURES_PREVIEW_SETTING}.deltaFindings`;
11 changes: 9 additions & 2 deletions src/snyk/common/languageServer/languageServer.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import _ from 'lodash';
import { firstValueFrom, ReplaySubject, Subject } from 'rxjs';
import { IAuthenticationService } from '../../base/services/authenticationService';
import { IConfiguration } from '../configuration/configuration';
import { FolderConfig, IConfiguration } from '../configuration/configuration';
import {
SNYK_ADD_TRUSTED_FOLDERS,
SNYK_CLI_PATH,
SNYK_FOLDERCONFIG,
SNYK_HAS_AUTHENTICATED,
SNYK_LANGUAGE_SERVER_NAME,
SNYK_SCAN,
Expand Down Expand Up @@ -118,7 +119,7 @@ export class LanguageServer implements ILanguageServer {
};

// Create the language client and start the client.
this.client = this.languageClientAdapter.create('Snyk LS', SNYK_LANGUAGE_SERVER_NAME, serverOptions, clientOptions);
this.client = this.languageClientAdapter.create('SnykLS', SNYK_LANGUAGE_SERVER_NAME, serverOptions, clientOptions);

try {
// Start the client. This will also launch the server
Expand All @@ -138,6 +139,12 @@ export class LanguageServer implements ILanguageServer {
});
});

client.onNotification(SNYK_FOLDERCONFIG, ({ folderConfigs }: { folderConfigs: FolderConfig[] }) => {
this.configuration.setFolderConfigs(folderConfigs).catch((error: Error) => {
ErrorHandler.handle(error, this.logger, error.message);
});
});

client.onNotification(SNYK_CLI_PATH, ({ cliPath }: { cliPath: string }) => {
if (!cliPath) {
ErrorHandler.handle(
Expand Down
4 changes: 3 additions & 1 deletion src/snyk/common/languageServer/settings.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import _ from 'lodash';
import { CLI_INTEGRATION_NAME } from '../../cli/contants/integration';
import { Configuration, IConfiguration, SeverityFilter } from '../configuration/configuration';
import { Configuration, FolderConfig, IConfiguration, SeverityFilter } from '../configuration/configuration';
import { User } from '../user';
import { PROTOCOL_VERSION } from '../constants/languageServer';

Expand Down Expand Up @@ -41,6 +41,7 @@ export type ServerSettings = {
deviceId?: string;
requiredProtocolVersion?: string;
enableDeltaFindings?: string;
folderConfigs: FolderConfig[];
};

export class LanguageServerSettings {
Expand Down Expand Up @@ -80,6 +81,7 @@ export class LanguageServerSettings {
integrationVersion: await Configuration.getVersion(),
deviceId: user.anonymousId,
requiredProtocolVersion: `${PROTOCOL_VERSION}`,
folderConfigs: configuration.getFolderConfigs(),
};
}
}
Loading

0 comments on commit fcea0e8

Please sign in to comment.