From 093a7223adfdd977796a97caccd88048e36bb48a Mon Sep 17 00:00:00 2001 From: Abdelrahman Shawki Hassan Date: Mon, 18 Nov 2024 15:28:56 +0100 Subject: [PATCH] fix: initialize ToolWindow when command is triggered (#324) * fix: initialize ToolWindow when command is triggered * chore: update changelog Co-authored-by: Darius-Beniamin Zdroba --- .github/workflows/pr-workflow.yml | 4 +- CHANGELOG.md | 5 + .../SnykCode/DcIgnoreServiceTest.cs | 194 ------------------ .../Service/SnykApiServiceTest.cs | 10 +- Snyk.Common/Service/SnykApiService.cs | 8 +- .../Commands/AbstractSnykCommand.cs | 9 +- .../Commands/SnykCleanPanelCommand.cs | 6 + .../Commands/SnykOpenSettingsCommand.cs | 6 +- .../Commands/SnykScanCommand.cs | 6 +- .../Commands/SnykStopCurrentTaskCommand.cs | 6 +- .../SnykVSPackage.cs | 51 ++--- .../UI/Toolwindow/SnykToolWindow.cs | 23 +++ .../UI/Toolwindow/SnykToolWindowCommand.cs | 12 +- 13 files changed, 91 insertions(+), 249 deletions(-) delete mode 100644 Snyk.Code.Library.Tests/SnykCode/DcIgnoreServiceTest.cs diff --git a/.github/workflows/pr-workflow.yml b/.github/workflows/pr-workflow.yml index 77987dd91..49ee86bd9 100644 --- a/.github/workflows/pr-workflow.yml +++ b/.github/workflows/pr-workflow.yml @@ -45,7 +45,7 @@ jobs: args: --severity-threshold=high build-project: - uses: snyk/snyk-visual-studio-plugin/.github/workflows/build-project.yml@main + uses: snyk/snyk-visual-studio-plugin/.github/workflows/build-project.yml@release/1 with: solution-file-path: . secrets: inherit @@ -74,5 +74,5 @@ jobs: run: vstest.console.exe **\*.Tests.dll /TestCaseFilter:"FullyQualifiedName!=Xunit.Instances.VisualStudio&integration!=true" #exclude integration tests and the psuedo-tests that launch a VS instance run-integration-tests: needs: build-project - uses: snyk/snyk-visual-studio-plugin/.github/workflows/integration-tests.yml@main + uses: snyk/snyk-visual-studio-plugin/.github/workflows/integration-tests.yml@release/1 secrets: inherit \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 4fb7122d6..ff199fa72 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Snyk Security Changelog +## [1.1.66] + +### Fixed +- Support VS > 17.10. + ## [1.1.63] ### Fixed diff --git a/Snyk.Code.Library.Tests/SnykCode/DcIgnoreServiceTest.cs b/Snyk.Code.Library.Tests/SnykCode/DcIgnoreServiceTest.cs deleted file mode 100644 index 4fbee74b8..000000000 --- a/Snyk.Code.Library.Tests/SnykCode/DcIgnoreServiceTest.cs +++ /dev/null @@ -1,194 +0,0 @@ -namespace Snyk.Code.Library.Tests.SnykCode -{ - using System.Collections.Generic; - using System.IO; - using System.Linq; - using Snyk.Code.Library.Service; - using Snyk.Code.Library.Tests.Api; - using Xunit; - - /// - /// Tests for . - /// - public class DcIgnoreServiceTest - { - [Fact] - public void DcIgnoreService_MultipleGitIgnoreAndDcIgnoreFilesProvided_FilterFiles() - { - var projectFiles = new List - { - "/SubProject1/Main.cs", - "/SubProject1/RestService.cs", - "/SubProject1/Debug/Main.cs", - "/SubProject1/Debug/RestService.cs", - "/SubProject1/Backup/Main.cs", - "/SubProject1/Backup/RestService.cs", - - "/SubProject2/App.cs", - "/SubProject2/AppService.cs", - "/SubProject2/obj/App.cs", - "/SubProject2/obj/AppService.cs", - "/SubProject2/bin/Main.cs", - - "/SubProject3/App.cs", - "/SubProject3/AppService.cs", - "/SubProject3/bin/App.cs", - "/SubProject3/bin/AppService.cs", - "/SubProject3/Packages/App.cs", - "/SubProject3/Packages/AppService.cs", - - "/bin/Main.cs", - "/Main.cs", - "/AppService.cs", - }; - - var dcIgnoreService = new DcIgnoreService(); - var filteredFiles = dcIgnoreService.FilterFiles(TestResource.GetFileFullPath("TestProject"), projectFiles).ToList(); - - Assert.Equal(8, filteredFiles.Count); - } - - [Fact] - public void DcIgnoreService_MultipleGitIgnoreFilesProvided_FilterFilesByMultipleGitignore() - { - var projectFiles = new List - { - "/SubProject1/Main.cs", - "/SubProject1/RestService.cs", - "/SubProject1/Debug/Main.cs", - "/SubProject1/Debug/RestService.cs", - - "/SubProject2/App.cs", - "/SubProject2/AppService.cs", - "/SubProject2/obj/App.cs", - "/SubProject2/obj/AppService.cs", - "/SubProject2/bin/Main.cs", - - "/SubProject3/App.cs", - "/SubProject3/AppService.cs", - "/SubProject3/bin/App.cs", - "/SubProject3/bin/AppService.cs", - - "/bin/Main.cs", - "/Main.cs", - "/AppService.cs", - }; - - var dcIgnoreService = new DcIgnoreService(); - var filteredFiles = dcIgnoreService.FilterFiles(TestResource.GetFileFullPath("TestProject"), projectFiles).ToList(); - - Assert.Equal(8, filteredFiles.Count); - } - - [Fact] - public void DcIgnoreService_ListOfProjectFilesProvided_CreateDcIgnoreFileAndFilterFiles() - { - string folderPath = Path.GetTempPath(); - - string dcIGnorePath = Path.Combine(folderPath, ".dcignore"); - - if (File.Exists(dcIGnorePath)) - { - File.Delete(dcIGnorePath); - } - - Assert.False(File.Exists(dcIGnorePath)); - - var projectFiles = new List - { - "/Bin/Main.cs", // Excluded by "[Bb]in/" - "/bin/Main.cs", // Excluded by "[Bb]in/" - "/Src/Main.cs", - "/.vs/App.cs", // Excluded by ".vs/" - "/DocProject/Help/html/index.js", // Excluded by "DocProject/Help/html" - "/App.cs", - "/build/Service.cs", // Excluded by one of the many rules that exclude "build" - }; - var expectedFiles = new[] { projectFiles[2], projectFiles[5] }.ToHashSet(); - - var dcIgnoreService = new DcIgnoreService(); - var filteredFiles = dcIgnoreService.FilterFiles(folderPath, projectFiles).ToList(); - - Assert.Equal(2, filteredFiles.Count); - Assert.Equal(expectedFiles.OrderBy(x => x), filteredFiles.OrderBy(x => x)); - Assert.True(File.Exists(dcIGnorePath)); - - File.Delete(dcIGnorePath); - } - - [Fact] - public void DcIgnoreService_SolutionWithoutDcIgnoreProvided_CreateDcIgnore() - { - string folderPath = Path.GetTempPath(); - - string dcIGnorePath = Path.Combine(folderPath, ".dcignore"); - - if (File.Exists(dcIGnorePath)) - { - File.Delete(dcIGnorePath); - } - - Assert.False(File.Exists(dcIGnorePath)); - - var dcIgnoreService = new DcIgnoreService(); - dcIgnoreService.CreateDcIgnoreIfNeeded(folderPath); - - Assert.True(File.Exists(dcIGnorePath)); - - File.Delete(dcIGnorePath); - } - - [Fact] - public void DcIgnoreService_ListOfProjectFilesProvided_FilterFilesByGitIgnoreCheck() - { - var projectFiles = new List - { - "/Bin/Main.cs", // Excluded by "[Bb]in/" - "/bin/Main.cs", // Excluded by "[Bb]in/" - "/Src/Main.cs", - "/.vs/App.cs", // Excluded by ".vs/" - "/DocProject/Help/html/index.js", // Excluded by "DocProject/Help/html" (no / at the end means both files and directories) - "/App.cs", - "/build/Service.cs", - }; - var expectedElements = new[] { projectFiles[2], projectFiles[5], projectFiles[6] }; - - var dcIgnoreService = new DcIgnoreService(); - var filteredFiles = dcIgnoreService.FilterFilesByGitIgnore(TestResource.GetResourcesPath(), projectFiles).ToList(); - - Assert.Equal(3, filteredFiles.Count); - Assert.Equal(expectedElements.ToHashSet(), filteredFiles.ToHashSet()); - } - - [Fact] - public void DcIgnoreService_FilePathWithUnderscoreFolderNameProvided_FilterFilesPass() - { - string folderPath = Path.GetTempPath(); - - string dcIGnorePath = Path.Combine(folderPath, ".dcignore"); - - if (File.Exists(dcIGnorePath)) - { - File.Delete(dcIGnorePath); - } - - Assert.False(File.Exists(dcIGnorePath)); - - var projectFiles = new List - { - "/Projects/_/Src/Main.cs", - "/Projects/_/Src/App.cs", - "/Projects/_/Src/Service/Service.cs", - }; - - var dcIgnoreService = new DcIgnoreService(); - var filteredFiles = dcIgnoreService.FilterFiles(folderPath, projectFiles).ToList(); - - Assert.Equal(3, filteredFiles.Count); - - Assert.True(File.Exists(dcIGnorePath)); - - File.Delete(dcIGnorePath); - } - } -} diff --git a/Snyk.Common.Tests/Service/SnykApiServiceTest.cs b/Snyk.Common.Tests/Service/SnykApiServiceTest.cs index 005d1f31f..018bb783e 100644 --- a/Snyk.Common.Tests/Service/SnykApiServiceTest.cs +++ b/Snyk.Common.Tests/Service/SnykApiServiceTest.cs @@ -23,7 +23,7 @@ public async Task SnykApiService_GetUserAsync_ReturnsUserAsync() optionsMock .Setup(options => options.CustomEndpoint) - .Returns("https://snyk.io/api"); + .Returns("https://api.snyk.io"); optionsMock .Setup(options => options.ApiToken) .Returns(TestToken); @@ -44,7 +44,7 @@ public async Task SnykApiService_CallSastEnabled_TrueAsync() optionsMock .Setup(options => options.CustomEndpoint) - .Returns("https://snyk.io/api"); + .Returns("https://api.snyk.io"); optionsMock .Setup(options => options.ApiToken) @@ -67,7 +67,7 @@ public async Task SnykApiService_SendSastSettingsRequestAsync_OrgIsInQueryParamA optionsMock .Setup(options => options.CustomEndpoint) - .Returns("https://dev.snyk.io/api"); + .Returns("https://api.dev.snyk.io"); optionsMock .Setup(options => options.ApiToken) .Returns(TestToken); @@ -89,7 +89,7 @@ public async Task SnykApiService_InvalidAuthTokenProvided_ReturnNullAsync() optionsMock .Setup(options => options.CustomEndpoint) - .Returns("https://snyk.io/api"); + .Returns("https://api.snyk.io"); optionsMock .Setup(options => options.ApiToken) @@ -109,7 +109,7 @@ public async Task SnykApiService_InvalidUrlProvided_ReturnNullAsync() optionsMock .Setup(options => options.CustomEndpoint) - .Returns("https://snyk.io/"); + .Returns("https://invalidurl.local"); optionsMock .Setup(options => options.ApiToken) diff --git a/Snyk.Common/Service/SnykApiService.cs b/Snyk.Common/Service/SnykApiService.cs index 99c153d19..0c3797291 100644 --- a/Snyk.Common/Service/SnykApiService.cs +++ b/Snyk.Common/Service/SnykApiService.cs @@ -61,13 +61,11 @@ public async Task GetSastSettingsAsync() { return null; } - - var response = await SendSastSettingsRequestAsync(); - var responseContent = await response.Content.ReadAsStringAsync(); - try { - SastSettings sastSettings = Json.Deserialize(responseContent); + var response = await SendSastSettingsRequestAsync(); + var responseContent = await response.Content.ReadAsStringAsync(); + var sastSettings = Json.Deserialize(responseContent); this.options.SastSettings = sastSettings; return sastSettings; } diff --git a/Snyk.VisualStudio.Extension.2022/Commands/AbstractSnykCommand.cs b/Snyk.VisualStudio.Extension.2022/Commands/AbstractSnykCommand.cs index c1f74a271..21c53a8ad 100644 --- a/Snyk.VisualStudio.Extension.2022/Commands/AbstractSnykCommand.cs +++ b/Snyk.VisualStudio.Extension.2022/Commands/AbstractSnykCommand.cs @@ -66,7 +66,14 @@ protected AbstractSnykCommand(AsyncPackage package, OleMenuCommandService comman /// /// Source object. /// Event args. - protected abstract void Execute(object sender, EventArgs eventArgs); + protected virtual void Execute(object sender, EventArgs eventArgs) + { + ThreadHelper.JoinableTaskFactory.Run(async () => + { + await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); + await VsPackage.EnsureInitializeToolWindowAsync(); + }); + } /// /// Get command Id. diff --git a/Snyk.VisualStudio.Extension.2022/Commands/SnykCleanPanelCommand.cs b/Snyk.VisualStudio.Extension.2022/Commands/SnykCleanPanelCommand.cs index 730aa5164..5397c0d7c 100644 --- a/Snyk.VisualStudio.Extension.2022/Commands/SnykCleanPanelCommand.cs +++ b/Snyk.VisualStudio.Extension.2022/Commands/SnykCleanPanelCommand.cs @@ -44,6 +44,11 @@ public static async Task InitializeAsync(AsyncPackage package) public override async Task UpdateStateAsync() { await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); + if (this.VsPackage.ToolWindow == null) + { + this.MenuCommand.Enabled = false; + return; + } this.MenuCommand.Enabled = SnykVSPackage.ServiceProvider.Options.ApiToken.IsValidAfterRefresh() && this.VsPackage.ToolWindowControl.IsTreeContentNotEmpty(); } @@ -55,6 +60,7 @@ public override async Task UpdateStateAsync() /// Event args. protected override void Execute(object sender, EventArgs eventArgs) { + base.Execute(sender, eventArgs); this.VsPackage.ToolWindowControl.Clean(); SnykVSPackage.ServiceProvider.SolutionService.Clean(); diff --git a/Snyk.VisualStudio.Extension.2022/Commands/SnykOpenSettingsCommand.cs b/Snyk.VisualStudio.Extension.2022/Commands/SnykOpenSettingsCommand.cs index f179c78d1..8f6a40f19 100644 --- a/Snyk.VisualStudio.Extension.2022/Commands/SnykOpenSettingsCommand.cs +++ b/Snyk.VisualStudio.Extension.2022/Commands/SnykOpenSettingsCommand.cs @@ -49,7 +49,11 @@ public static async Task InitializeAsync(AsyncPackage package) /// /// Source object. /// Event args. - protected override void Execute(object sender, EventArgs eventArgs) => this.VsPackage.ShowOptionPage(); + protected override void Execute(object sender, EventArgs eventArgs) + { + base.Execute(sender, eventArgs); + this.VsPackage.ShowOptionPage(); + } /// /// Get command Id. diff --git a/Snyk.VisualStudio.Extension.2022/Commands/SnykScanCommand.cs b/Snyk.VisualStudio.Extension.2022/Commands/SnykScanCommand.cs index 132ffea39..c96ff713e 100644 --- a/Snyk.VisualStudio.Extension.2022/Commands/SnykScanCommand.cs +++ b/Snyk.VisualStudio.Extension.2022/Commands/SnykScanCommand.cs @@ -75,7 +75,11 @@ public override async Task UpdateStateAsync() /// /// Source object. /// Event args. - protected override void Execute(object sender, EventArgs eventArgs) => ThreadHelper.JoinableTaskFactory.RunAsync(SnykTasksService.Instance.ScanAsync); + protected override void Execute(object sender, EventArgs eventArgs) + { + base.Execute(sender, eventArgs); + ThreadHelper.JoinableTaskFactory.RunAsync(SnykTasksService.Instance.ScanAsync); + } /// /// Get command Id. diff --git a/Snyk.VisualStudio.Extension.2022/Commands/SnykStopCurrentTaskCommand.cs b/Snyk.VisualStudio.Extension.2022/Commands/SnykStopCurrentTaskCommand.cs index 68b34215b..19620b514 100644 --- a/Snyk.VisualStudio.Extension.2022/Commands/SnykStopCurrentTaskCommand.cs +++ b/Snyk.VisualStudio.Extension.2022/Commands/SnykStopCurrentTaskCommand.cs @@ -54,7 +54,11 @@ public override async Task UpdateStateAsync() /// /// Source object. /// Event args. - protected override void Execute(object sender, EventArgs eventArgs) => SnykTasksService.Instance.CancelTasks(); + protected override void Execute(object sender, EventArgs eventArgs) + { + base.Execute(sender, eventArgs); + SnykTasksService.Instance.CancelTasks(); + } /// /// Get command Id. diff --git a/Snyk.VisualStudio.Extension.2022/SnykVSPackage.cs b/Snyk.VisualStudio.Extension.2022/SnykVSPackage.cs index 42f38db84..cbec56f59 100644 --- a/Snyk.VisualStudio.Extension.2022/SnykVSPackage.cs +++ b/Snyk.VisualStudio.Extension.2022/SnykVSPackage.cs @@ -85,7 +85,7 @@ public SnykVSPackage() /// /// Gets a value indicating whether ToolWindow Control. /// - public SnykToolWindowControl ToolWindowControl { get; private set; } + public SnykToolWindowControl ToolWindowControl { get; set; } /// /// Gets a value indicating whether general Options dialog. @@ -100,7 +100,7 @@ public SnykVSPackage() /// /// Gets instance. /// - public SnykToolWindow ToolWindow { get; private set; } + public SnykToolWindow ToolWindow { get; set; } /// /// True if the package was initialized successfully. @@ -134,39 +134,24 @@ public async Task CreateSnykServiceAsync(IAsyncServiceContainer containe /// Initialize tool window. /// /// Task. - public async Task InitializeToolWindowAsync() + public async Task EnsureInitializeToolWindowAsync() { - if (ToolWindow == null) - { - Logger.Information( - "ToolWindow is not initialized. Call await JoinableTaskFactory.SwitchToMainThreadAsync()."); - - await JoinableTaskFactory.SwitchToMainThreadAsync(); - - Logger.Information("Call FindToolWindow()."); + await JoinableTaskFactory.SwitchToMainThreadAsync(); + if (ToolWindow != null) return; + Logger.Information("Call FindToolWindow()."); + ToolWindow = FindToolWindow(typeof(SnykToolWindow), 0, true) as SnykToolWindow; - ToolWindow = FindToolWindow(typeof(SnykToolWindow), 0, true) as SnykToolWindow; + Logger.Information("Check ToolWindow is not null {ToolWindow}.", ToolWindow); - Logger.Information("Check ToolWindow is not null {ToolWindow}.", ToolWindow); - - if (ToolWindow == null || ToolWindow.Frame == null) - { - Logger.Error("Exception: Cannot find Snyk tool window."); - - throw new NotSupportedException("Cannot find Snyk tool window."); - } - - Logger.Information( - "Initialize ToolWindow.Content. Call await JoinableTaskFactory.SwitchToMainThreadAsync()."); - - await JoinableTaskFactory.SwitchToMainThreadAsync(); - - Logger.Information("Call ToolWindow.Content."); - - ToolWindowControl = (SnykToolWindowControl) ToolWindow.Content; + if (ToolWindow == null || ToolWindow.Frame == null) + { + Logger.Error("Exception: Cannot find Snyk tool window."); - Logger.Information("Leave InitializeToolWindowControlAsync() method"); + throw new NotSupportedException("Cannot find Snyk tool window."); } + + Logger.Information("Call ToolWindow.Content."); + ToolWindowControl = (SnykToolWindowControl)ToolWindow.Content; } /// @@ -209,13 +194,7 @@ protected override async Task InitializeAsync(CancellationToken cancellationToke await SnykCleanPanelCommand.InitializeAsync(this); await SnykOpenSettingsCommand.InitializeAsync(this); - // Initialize tool-window - Logger.Information("Initializing tool window"); - await InitializeToolWindowAsync(); VsStatusBarNotificationService.Instance.InitializeEventListeners(this.serviceProvider); - Logger.Information("Before call toolWindowControl.InitializeEventListeners() method."); - ToolWindowControl.InitializeEventListeners(this.serviceProvider); - ToolWindowControl.Initialize(this.serviceProvider); // Notify package has been initialized IsInitialized = true; diff --git a/Snyk.VisualStudio.Extension.2022/UI/Toolwindow/SnykToolWindow.cs b/Snyk.VisualStudio.Extension.2022/UI/Toolwindow/SnykToolWindow.cs index 50458da7a..71043a79e 100644 --- a/Snyk.VisualStudio.Extension.2022/UI/Toolwindow/SnykToolWindow.cs +++ b/Snyk.VisualStudio.Extension.2022/UI/Toolwindow/SnykToolWindow.cs @@ -2,12 +2,14 @@ using System.Collections.Generic; using System.ComponentModel.Design; using System.Runtime.InteropServices; +using EnvDTE; using Microsoft.VisualStudio; using Microsoft.VisualStudio.PlatformUI; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; using Microsoft.Internal.VisualStudio.PlatformUI; using Snyk.VisualStudio.Extension.Model; +using Snyk.VisualStudio.Extension.Service; namespace Snyk.VisualStudio.Extension.UI.Toolwindow { @@ -43,6 +45,27 @@ public SnykToolWindow() this.ToolBarLocation = (int)VSTWT_LOCATION.VSTWT_TOP; } + public override void OnToolWindowCreated() + { + base.OnToolWindowCreated(); + ThreadHelper.JoinableTaskFactory.Run(async () => + { + await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); + var toolWindowControl = Content as SnykToolWindowControl; + if (toolWindowControl == null) return; + var package = Package as SnykVSPackage; + if (package == null) return; + + package.ToolWindow = this; + package.ToolWindowControl = toolWindowControl; + var serviceProvider = await package.GetServiceAsync(typeof(SnykService)) as SnykService ?? + throw new InvalidOperationException("Could not find Snyk Service"); + toolWindowControl.InitializeEventListeners(serviceProvider); + toolWindowControl.Initialize(serviceProvider); + }); + + } + /// /// Gets a value indicating whether True. Enable search in tool window. /// diff --git a/Snyk.VisualStudio.Extension.2022/UI/Toolwindow/SnykToolWindowCommand.cs b/Snyk.VisualStudio.Extension.2022/UI/Toolwindow/SnykToolWindowCommand.cs index 45299a1cd..47c9b8ec2 100644 --- a/Snyk.VisualStudio.Extension.2022/UI/Toolwindow/SnykToolWindowCommand.cs +++ b/Snyk.VisualStudio.Extension.2022/UI/Toolwindow/SnykToolWindowCommand.cs @@ -1,8 +1,6 @@ using System; using System.ComponentModel.Design; using Microsoft.VisualStudio.Shell; -using Serilog; -using Snyk.Common; using Snyk.VisualStudio.Extension.Service; using Task = System.Threading.Tasks.Task; @@ -66,6 +64,14 @@ public static async Task InitializeAsync(ISnykServiceProvider serviceProvider) /// /// The event sender. /// The event args. - private void ShowToolWindow(object sender, EventArgs e) => this.serviceProvider.ToolWindow.Show(); + private void ShowToolWindow(object sender, EventArgs e) + { + ThreadHelper.JoinableTaskFactory.Run(async () => + { + await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); + await this.serviceProvider.Package.EnsureInitializeToolWindowAsync(); + this.serviceProvider.ToolWindow.Show(); + }); + } } }