From 2b35041c623c6450a2499f0b73b33b2fdbdc501a Mon Sep 17 00:00:00 2001 From: Ilona Shishov Date: Wed, 25 Oct 2023 14:36:28 +0300 Subject: [PATCH] feat: added MATCH_MANIFEST_VERSIONS to extension workspace settings and to Exhort JS API request (#653) Signed-off-by: Ilona Shishov --- package-lock.json | 41 +++++++++++++++++++++++++++--- package.json | 10 ++++++-- src/authextension.ts | 1 + src/dependencyReportPanel.ts | 38 +++++++++++++++++---------- src/multimanifestmodule.ts | 8 +++--- src/stackanalysismodule.ts | 3 ++- test/authextension.test.ts | 4 ++- test/dependencyReportPanel.test.ts | 7 +++-- test/multiManifestModule.test.ts | 25 +++++++++++------- 9 files changed, 98 insertions(+), 39 deletions(-) diff --git a/package-lock.json b/package-lock.json index 50beb592e..979a243e3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,7 @@ "dependencies": { "@fabric8-analytics/fabric8-analytics-lsp-server": "^0.7.1-ea.13", "@redhat-developer/vscode-redhat-telemetry": "^0.7.0", - "@RHEcosystemAppEng/exhort-javascript-api": "^0.0.2-ea.32", + "@RHEcosystemAppEng/exhort-javascript-api": "^0.0.2-ea.39", "fs": "^0.0.1-security", "path": "^0.12.7", "vscode-languageclient": "^8.1.0" @@ -43,6 +43,39 @@ "vscode": "^1.76.0" } }, + "../fabric8-analytics-lsp-server": { + "name": "@fabric8-analytics/fabric8-analytics-lsp-server", + "version": "0.7.1-ea.13", + "extraneous": true, + "license": "Apache-2.0", + "dependencies": { + "@RHEcosystemAppEng/exhort-javascript-api": "^0.0.2-ea.32", + "@xml-tools/ast": "^5.0.5", + "@xml-tools/parser": "^1.0.11", + "json-to-ast": "^2.1.0", + "vscode-languageserver": "^8.1.0", + "vscode-languageserver-textdocument": "1.0.8" + }, + "bin": { + "fabric8-analytics-lsp-server": "dist/server.js" + }, + "devDependencies": { + "@types/chai": "^4.3.7", + "@types/mocha": "^10.0.2", + "@types/node": "^20.8.4", + "@types/node-fetch": "^2.6.6", + "@types/uuid": "^9.0.5", + "@typescript-eslint/eslint-plugin": "^6.7.5", + "@typescript-eslint/parser": "^6.7.5", + "chai": "^4.3.10", + "eslint": "^8.51.0", + "fake-exec": "^1.1.0", + "mocha": "^10.2.0", + "nyc": "^15.1.0", + "ts-node": "^10.9.1", + "typescript": "^5.2.2" + } + }, "node_modules/@babel/code-frame": { "version": "7.22.13", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", @@ -447,9 +480,9 @@ } }, "node_modules/@RHEcosystemAppEng/exhort-javascript-api": { - "version": "0.0.2-ea.36", - "resolved": "https://npm.pkg.github.com/download/@RHEcosystemAppEng/exhort-javascript-api/0.0.2-ea.36/3cbe4318ad060f99a4c80ea8f69cae5b651a17ee", - "integrity": "sha512-kXPFJbG05tJmpqAKZbeHABjKELZtsQVdOjSISA5EGaRPnF61gv67b0Qn3zbvpSC4LybUdSu3mO1cK3OedEk/Fg==", + "version": "0.0.2-ea.39", + "resolved": "https://npm.pkg.github.com/download/@RHEcosystemAppEng/exhort-javascript-api/0.0.2-ea.39/93fd086e96f28e1393a900884338a2b5feae81c4", + "integrity": "sha512-6rvXqLnR0OvQxre73Hbla3eji8q6te6siwx8zibrYFHEztSTQmrrOHmwc2GnOW3ugtQfpKcqsUj6039ug9KBog==", "license": "Apache-2.0", "dependencies": { "@cyclonedx/cyclonedx-library": "^4.0.0", diff --git a/package.json b/package.json index 01483cc22..1abf47cf7 100644 --- a/package.json +++ b/package.json @@ -190,6 +190,12 @@ "description": "Red Hat Dependency Analytics server authentication token for Snyk.", "scope": "window" }, + "redHatDependencyAnalytics.matchManifestVersions": { + "type": "boolean", + "default": true, + "description": "Restricts RHDA from performing analysis on dependency tags that do not match the tags requested within the manifest files.", + "scope": "window" + }, "redHatDependencyAnalytics.redHatDependencyAnalyticsReportFilePath": { "type": "string", "default": "/tmp/redhatDependencyAnalyticsReport.html", @@ -278,9 +284,9 @@ "dependencies": { "@fabric8-analytics/fabric8-analytics-lsp-server": "^0.7.1-ea.13", "@redhat-developer/vscode-redhat-telemetry": "^0.7.0", - "@RHEcosystemAppEng/exhort-javascript-api": "^0.0.2-ea.32", + "@RHEcosystemAppEng/exhort-javascript-api": "^0.0.2-ea.39", "fs": "^0.0.1-security", "path": "^0.12.7", "vscode-languageclient": "^8.1.0" } -} \ No newline at end of file +} diff --git a/src/authextension.ts b/src/authextension.ts index d303d0d47..bb4e1d444 100644 --- a/src/authextension.ts +++ b/src/authextension.ts @@ -12,6 +12,7 @@ export module authextension { process.env['PROVIDE_FULLSTACK_ACTION'] = 'true'; process.env['UTM_SOURCE'] = GlobalState.UtmSource; process.env['SNYK_TOKEN'] = apiConfig.exhortSnykToken; + process.env['MATCH_MANIFEST_VERSIONS'] = apiConfig.matchManifestVersions ? 'true' : 'false'; process.env['MVN_EXECUTABLE'] = Config.getMvnExecutable(); process.env['NPM_EXECUTABLE'] = Config.getNpmExecutable(); process.env['GO_EXECUTABLE'] = Config.getGoExecutable(); diff --git a/src/dependencyReportPanel.ts b/src/dependencyReportPanel.ts index 41f69acb1..5b56d930a 100644 --- a/src/dependencyReportPanel.ts +++ b/src/dependencyReportPanel.ts @@ -23,14 +23,20 @@ export class DependencyReportPanel { private readonly _panel: vscode.WebviewPanel; private _disposables: vscode.Disposable[] = []; - public static createOrShow(extensionPath: string, data: any) { + public static createOrShowWebviewPanel() { const column = vscode.window.activeTextEditor ? vscode.window.activeTextEditor.viewColumn : undefined; - DependencyReportPanel.data = data; + DependencyReportPanel.data = null; + // If we already have a panel, show it. if (DependencyReportPanel.currentPanel) { - DependencyReportPanel.currentPanel._panel.reveal(column); + if (DependencyReportPanel.currentPanel._panel.visible) { + DependencyReportPanel.currentPanel._updateWebView(); + } else { + DependencyReportPanel.currentPanel._panel.reveal(column); + } + DependencyReportPanel.currentPanel._disposeReport(); return; } @@ -98,6 +104,7 @@ export class DependencyReportPanel { DependencyReportPanel.data = data; this._panel.webview.html = data; } else { + DependencyReportPanel.data = errorTmpl; this._panel.webview.html = errorTmpl; } } @@ -107,12 +114,7 @@ export class DependencyReportPanel { // Clean up our resources this._panel.dispose(); - const apiConfig = Config.getApiConfig(); - if (fs.existsSync(apiConfig.redHatDependencyAnalyticsReportFilePath || defaultRedhatDependencyAnalyticsReportFilePath)) { - // Delete temp stackAnalysisReport file - fs.unlinkSync(apiConfig.redHatDependencyAnalyticsReportFilePath || defaultRedhatDependencyAnalyticsReportFilePath); - console.log(`File ${apiConfig.redHatDependencyAnalyticsReportFilePath || defaultRedhatDependencyAnalyticsReportFilePath} has been deleted.`); - } + this._disposeReport(); DependencyReportPanel.data = null; while (this._disposables.length) { const x = this._disposables.pop(); @@ -123,10 +125,20 @@ export class DependencyReportPanel { } private _updateWebView() { - this._panel.title = Titles.REPORT_TITLE; let output = DependencyReportPanel.data; - this._panel.webview.html = output && /<\s*html[^>]*>/i.test(output) ? - output : - loaderTmpl; + if (output && /<\s*html[^>]*>/i.test(output)) { + this._panel.webview.html = output; + } else { + this._panel.webview.html = loaderTmpl; + } + } + + private _disposeReport() { + const apiConfig = Config.getApiConfig(); + if (fs.existsSync(apiConfig.redHatDependencyAnalyticsReportFilePath || defaultRedhatDependencyAnalyticsReportFilePath)) { + // Delete temp stackAnalysisReport file + fs.unlinkSync(apiConfig.redHatDependencyAnalyticsReportFilePath || defaultRedhatDependencyAnalyticsReportFilePath); + console.log(`File ${apiConfig.redHatDependencyAnalyticsReportFilePath || defaultRedhatDependencyAnalyticsReportFilePath} has been deleted.`); + } } } \ No newline at end of file diff --git a/src/multimanifestmodule.ts b/src/multimanifestmodule.ts index 216fd2888..503d8faca 100644 --- a/src/multimanifestmodule.ts +++ b/src/multimanifestmodule.ts @@ -61,16 +61,14 @@ export module multimanifestmodule { }; export const triggerManifestWs = context => { - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { authextension .authorize_f8_analytics(context) .then(data => { if (data) { - DependencyReportPanel.createOrShow(context.extensionPath, null); - resolve(true); + DependencyReportPanel.createOrShowWebviewPanel(); + resolve(); } - }) - .catch(err => { reject(`Unable to authenticate.`); }); }); diff --git a/src/stackanalysismodule.ts b/src/stackanalysismodule.ts index 71a0212b9..fdfac8133 100644 --- a/src/stackanalysismodule.ts +++ b/src/stackanalysismodule.ts @@ -44,7 +44,8 @@ export module stackanalysismodule { 'EXHORT_PIP_PATH': Config.getPipExecutable(), 'EXHORT_DEV_MODE': process.env.EXHORT_DEV_MODE, 'RHDA_TOKEN': process.env.TELEMETRY_ID, - 'RHDA_SOURCE': process.env.UTM_SOURCE + 'RHDA_SOURCE': process.env.UTM_SOURCE, + 'MATCH_MANIFEST_VERSIONS': apiConfig.matchManifestVersions }; if (apiConfig.exhortSnykToken !== '') { diff --git a/test/authextension.test.ts b/test/authextension.test.ts index c4cf9f385..b180195fc 100644 --- a/test/authextension.test.ts +++ b/test/authextension.test.ts @@ -21,7 +21,8 @@ suite('authextension Modules', () => { test('setContextData should set environment variables', async () => { const mockApiConfig = { - exhortSnykToken: 'mockToken' + exhortSnykToken: 'mockToken', + matchManifestVersions: true, }; authextension.setContextData(mockApiConfig); @@ -29,6 +30,7 @@ suite('authextension Modules', () => { expect(process.env['PROVIDE_FULLSTACK_ACTION']).equals('true'); expect(process.env['UTM_SOURCE']).equals('vscode'); expect(process.env['SNYK_TOKEN']).equals('mockToken'); + expect(process.env['MATCH_MANIFEST_VERSIONS']).equals('true'); expect(process.env['MVN_EXECUTABLE']).equals('mvn'); expect(process.env['NPM_EXECUTABLE']).equals('npm'); expect(process.env['GO_EXECUTABLE']).equals('go'); diff --git a/test/dependencyReportPanel.test.ts b/test/dependencyReportPanel.test.ts index 5cb39e052..2195749c0 100644 --- a/test/dependencyReportPanel.test.ts +++ b/test/dependencyReportPanel.test.ts @@ -5,7 +5,6 @@ import * as vscode from 'vscode'; import * as fs from 'fs'; import { Config } from '../src/config'; -import { context } from './vscontext.mock'; import { DependencyReportPanel } from '../src/dependencyReportPanel'; const expect = chai.expect; @@ -25,7 +24,7 @@ suite('DependencyReportPanel Modules', () => { test('createOrShow should create a new panel', async () => { const createWebviewPanelSpy = sandbox.spy(vscode.window, 'createWebviewPanel'); - DependencyReportPanel.createOrShow(context.extensionPath, null); + DependencyReportPanel.createOrShowWebviewPanel(); expect(createWebviewPanelSpy).to.be.calledOnce; expect(DependencyReportPanel.currentPanel).to.exist; @@ -34,7 +33,7 @@ suite('DependencyReportPanel Modules', () => { test('doUpdatePanel should render and update data', async () => { const data = 'Mock data'; - DependencyReportPanel.createOrShow(context.extensionPath, null); + DependencyReportPanel.createOrShowWebviewPanel(); DependencyReportPanel.currentPanel.doUpdatePanel(data); expect(DependencyReportPanel.data).equals(data); @@ -48,7 +47,7 @@ suite('DependencyReportPanel Modules', () => { const existsSyncStub = sandbox.stub(fs, 'existsSync').returns(true); const unlinkSyncStub = sandbox.stub(fs, 'unlinkSync'); - DependencyReportPanel.createOrShow(context.extensionPath, data); + DependencyReportPanel.createOrShowWebviewPanel(); DependencyReportPanel.currentPanel.dispose(); expect(existsSyncStub).to.be.calledWith('mockFilePath'); diff --git a/test/multiManifestModule.test.ts b/test/multiManifestModule.test.ts index 371b57bf7..247043915 100644 --- a/test/multiManifestModule.test.ts +++ b/test/multiManifestModule.test.ts @@ -34,19 +34,25 @@ suite('multimanifest module', () => { expect(processStackAnalysisStub.calledOnceWithExactly(context, { uri }, 'maven', uri)).to.be.true; }); - test('triggerManifestWs should resolve with true when authorized and create DependencyReportPanel', async () => { + test('triggerManifestWs should resolve when authorized and create DependencyReportPanel', async () => { let authorize_f8_analyticsStub = sandbox.stub(authextension, 'authorize_f8_analytics').resolves(true); - const createOrShowStub = sandbox.stub(DependencyReportPanel, 'createOrShow'); + const createOrShowWebviewPanelStub = sandbox.stub(DependencyReportPanel, 'createOrShowWebviewPanel'); - let result = await multimanifestmodule.triggerManifestWs(context); + try { + await multimanifestmodule.triggerManifestWs(context); + // If triggerManifestWs resolves successfully, the test will pass. + } catch (error) { + // If triggerManifestWs rejects, the test will fail with the error message. + expect.fail('Expected triggerManifestWs to resolve, but it rejected with an error: ' + error); + } - expect(result).equals(true); - expect(createOrShowStub.calledOnceWithExactly(context.extensionPath, null)).to.be.true; - expect(authorize_f8_analyticsStub).to.be.calledOnce; + expect(authorize_f8_analyticsStub.calledOnce).to.be.true; + expect(createOrShowWebviewPanelStub.calledOnce).to.be.true; }); test('triggerManifestWs should reject with "Unable to authenticate." when authorization fails', async () => { - const authStub = sandbox.stub(authextension, 'authorize_f8_analytics').rejects('Authentication failed'); + let authorize_f8_analyticsStub = sandbox.stub(authextension, 'authorize_f8_analytics').resolves(false); + const createOrShowWebviewPanelStub = sandbox.stub(DependencyReportPanel, 'createOrShowWebviewPanel'); try { await multimanifestmodule.triggerManifestWs(context); @@ -56,7 +62,8 @@ suite('multimanifest module', () => { expect(error).to.equal('Unable to authenticate.'); } - expect(authStub.calledOnceWithExactly(context)).to.be.true; + expect(authorize_f8_analyticsStub.calledOnce).to.be.true; + expect(createOrShowWebviewPanelStub.called).to.be.false; }); test('triggerTokenValidation should call validateSnykToken when provider is "snyk"', async () => { @@ -64,7 +71,7 @@ suite('multimanifest module', () => { await multimanifestmodule.triggerTokenValidation('snyk'); - expect(validateSnykTokenStub).to.be.calledOnce; + expect(validateSnykTokenStub.calledOnce).to.be.true; }); });