From 81a1e614206eca34b749f441e44a9b855b54f25c Mon Sep 17 00:00:00 2001 From: Vasileios Naskos Date: Thu, 19 Sep 2024 17:36:35 +0200 Subject: [PATCH 01/12] Change sonarcloud ServerConnection Id to Uri --- .../Binding/ServerConnectionTests.cs | 5 ++-- src/Core/Binding/ServerConnection.cs | 25 +++++++++++++++---- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/Core.UnitTests/Binding/ServerConnectionTests.cs b/src/Core.UnitTests/Binding/ServerConnectionTests.cs index 4f0c753a8b..af266ddcec 100644 --- a/src/Core.UnitTests/Binding/ServerConnectionTests.cs +++ b/src/Core.UnitTests/Binding/ServerConnectionTests.cs @@ -57,17 +57,16 @@ public void Ctor_SonarCloud_NullCredentials_SetsNull() [TestMethod] public void Ctor_SonarCloud_SetsProperties() { - var expectedServerUri = new Uri("https://sonarcloud.io"); var serverConnectionSettings = new ServerConnectionSettings(false); var credentials = Substitute.For(); var sonarCloud = new ServerConnection.SonarCloud(Org, serverConnectionSettings, credentials); - sonarCloud.Id.Should().BeSameAs(Org); + sonarCloud.Id.Should().Be($"https://sonarcloud.io/organizations/{Org}"); sonarCloud.OrganizationKey.Should().BeSameAs(Org); sonarCloud.ServerUri.Should().Be(new Uri("https://sonarcloud.io")); sonarCloud.Settings.Should().BeSameAs(serverConnectionSettings); sonarCloud.Credentials.Should().BeSameAs(credentials); - sonarCloud.CredentialsUri.Should().Be(new Uri($"https://sonarcloud.io/organizations/{sonarCloud.OrganizationKey}")); + sonarCloud.CredentialsUri.Should().Be(new Uri($"https://sonarcloud.io/organizations/{Org}")); } [TestMethod] diff --git a/src/Core/Binding/ServerConnection.cs b/src/Core/Binding/ServerConnection.cs index d571418f5f..ab8f1328c9 100644 --- a/src/Core/Binding/ServerConnection.cs +++ b/src/Core/Binding/ServerConnection.cs @@ -18,6 +18,8 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +using System.IO; + namespace SonarLint.VisualStudio.Core.Binding; public abstract class ServerConnection @@ -48,16 +50,29 @@ private ServerConnection(string id, ServerConnectionSettings settings = null, IC public sealed class SonarCloud : ServerConnection { - public SonarCloud(string organizationKey, ServerConnectionSettings settings = null, ICredentials credentials = null) : base(organizationKey, settings, credentials) + private const string SonarCloudUrl = "https://sonarcloud.io"; + + public SonarCloud(string organizationKey, ServerConnectionSettings settings = null, ICredentials credentials = null) + : base(OrganizationKeyToId(organizationKey), settings, credentials) { - OrganizationKey = organizationKey ?? throw new ArgumentNullException(nameof(organizationKey)); - CredentialsUri = new Uri(ServerUri, $"organizations/{organizationKey}"); + OrganizationKey = organizationKey; + CredentialsUri = new Uri(Id); } - public string OrganizationKey { get; } + public string OrganizationKey { get; } - public override Uri ServerUri { get; } = new("https://sonarcloud.io"); + public override Uri ServerUri => new (SonarCloudUrl); public override Uri CredentialsUri { get; } + + private static string OrganizationKeyToId(string organizationKey) + { + if (string.IsNullOrWhiteSpace(organizationKey)) + { + throw new ArgumentNullException(nameof(organizationKey)); + } + + return $"{SonarCloudUrl}/organizations/{organizationKey}"; + } } public sealed class SonarQube(Uri serverUri, ServerConnectionSettings settings = null, ICredentials credentials = null) From 02dc1f99e6e6f3b342002cd060104f6eafb061e2 Mon Sep 17 00:00:00 2001 From: Vasileios Naskos Date: Fri, 20 Sep 2024 11:04:51 +0200 Subject: [PATCH 02/12] Prevent leaking UI models to backend --- src/ConnectedMode/SlCoreConnectionAdapter.cs | 58 ++++++++----------- .../UI/Credentials/ICredentialsModel.cs | 18 +++++- .../ManageBinding/ManageBindingViewModel.cs | 5 +- .../ProjectSelectionViewModel.cs | 2 +- 4 files changed, 44 insertions(+), 39 deletions(-) diff --git a/src/ConnectedMode/SlCoreConnectionAdapter.cs b/src/ConnectedMode/SlCoreConnectionAdapter.cs index e1e20b194c..23b6c6228d 100644 --- a/src/ConnectedMode/SlCoreConnectionAdapter.cs +++ b/src/ConnectedMode/SlCoreConnectionAdapter.cs @@ -40,8 +40,8 @@ public interface ISlCoreConnectionAdapter { Task ValidateConnectionAsync(ConnectionInfo connectionInfo, ICredentialsModel credentialsModel); Task>> GetOrganizationsAsync(ICredentialsModel credentialsModel); - Task> GetServerProjectByKeyAsync(ICredentials credentials, ConnectionInfo connectionInfo, string serverProjectKey); - Task>> GetAllProjectsAsync(ConnectionInfo connectionInfo, ICredentials credentials); + Task> GetServerProjectByKeyAsync(ServerConnection serverConnection, string serverProjectKey); + Task>> GetAllProjectsAsync(ServerConnection serverConnection); } public class AdapterResponseWithData(bool success, T responseData) : IResponseStatus @@ -74,8 +74,11 @@ public SlCoreConnectionAdapter(ISLCoreServiceProvider serviceProvider, IThreadHa public async Task ValidateConnectionAsync(ConnectionInfo connectionInfo, ICredentialsModel credentialsModel) { - var credentials = GetCredentialsDto(credentialsModel); - var validateConnectionParams = GetValidateConnectionParams(connectionInfo, credentials); + var credentials = credentialsModel.ToICredentials(); + ServerConnection serverConnection = connectionInfo.ServerType == ConnectionServerType.SonarCloud + ? new ServerConnection.SonarCloud(connectionInfo.Id, credentials: credentials) + : new ServerConnection.SonarQube(new Uri(connectionInfo.Id), credentials: credentials); + var validateConnectionParams = new ValidateConnectionParams(GetTransientConnectionDto(serverConnection)); return await ValidateConnectionAsync(validateConnectionParams); } @@ -90,7 +93,7 @@ public Task>> GetOrganizations try { - var credentials = GetCredentialsDto(credentialsModel); + var credentials = MapCredentials(credentialsModel.ToICredentials()); var response = await connectionConfigurationSlCoreService.ListUserOrganizationsAsync(new ListUserOrganizationsParams(credentials)); var organizationDisplays = response.userOrganizations.Select(o => new OrganizationDisplay(o.key, o.name)).ToList(); @@ -104,7 +107,7 @@ public Task>> GetOrganizations }); } - public Task> GetServerProjectByKeyAsync(ICredentials credentials, ConnectionInfo connectionInfo, string serverProjectKey) + public Task> GetServerProjectByKeyAsync(ServerConnection serverConnection, string serverProjectKey) { var failedResponse = new AdapterResponseWithData(false, null); @@ -117,8 +120,7 @@ public Task> GetServerProjectByKeyAsync(I try { - var credentialsSlCoreFormat = MapCredentials(credentials); - var transientConnection = GetTransientConnectionDto(connectionInfo, credentialsSlCoreFormat); + var transientConnection = GetTransientConnectionDto(serverConnection); var response = await connectionConfigurationSlCoreService.GetProjectNamesByKeyAsync(new GetProjectNamesByKeyParams(transientConnection, [serverProjectKey])); if (response.projectNamesByKey.TryGetValue(serverProjectKey, out var projectName) && projectName == null) @@ -137,10 +139,9 @@ public Task> GetServerProjectByKeyAsync(I }); } - public async Task>> GetAllProjectsAsync(ConnectionInfo connectionInfo, ICredentials credentials) + public async Task>> GetAllProjectsAsync(ServerConnection serverConnection) { - var credentialsDto = MapCredentials(credentials); - var validateConnectionParams = GetAllProjectsParams(connectionInfo, credentialsDto); + var validateConnectionParams = new GetAllProjectsParams(GetTransientConnectionDto(serverConnection)); return await GetAllProjectsAsync(validateConnectionParams); } @@ -203,21 +204,18 @@ private bool TryGetConnectionConfigurationSlCoreService(out IConnectionConfigura return false; } - private static ValidateConnectionParams GetValidateConnectionParams(ConnectionInfo connectionInfo, Either credentials) + private static Either GetTransientConnectionDto(ServerConnection serverConnection) { - return new ValidateConnectionParams(GetTransientConnectionDto(connectionInfo, credentials)); - } - - private static GetAllProjectsParams GetAllProjectsParams(ConnectionInfo connectionInfo, Either credentials) - { - return new GetAllProjectsParams(GetTransientConnectionDto(connectionInfo, credentials)); - } - - private static Either GetTransientConnectionDto(ConnectionInfo connectionInfo, Either credentials) - { - return connectionInfo.ServerType == ConnectionServerType.SonarQube - ? Either.CreateLeft(new TransientSonarQubeConnectionDto(connectionInfo.Id, credentials)) - : Either.CreateRight(new TransientSonarCloudConnectionDto(connectionInfo.Id, credentials)); + var credentials = MapCredentials(serverConnection.Credentials); + + return serverConnection switch + { + ServerConnection.SonarQube sonarQubeConnection => Either.CreateLeft( + new TransientSonarQubeConnectionDto(sonarQubeConnection.Id, credentials)), + ServerConnection.SonarCloud sonarCloudConnection => Either.CreateRight( + new TransientSonarCloudConnectionDto(sonarCloudConnection.OrganizationKey, credentials)), + _ => null + }; } private static Either GetEitherForToken(string token) @@ -229,16 +227,6 @@ private static Either GetEitherForUsernamePasswor { return Either.CreateRight(new UsernamePasswordDto(username, password)); } - - private static Either GetCredentialsDto(ICredentialsModel credentialsModel) - { - return credentialsModel switch - { - TokenCredentialsModel tokenCredentialsModel => GetEitherForToken(tokenCredentialsModel.Token.ToUnsecureString()), - UsernamePasswordModel usernamePasswordModel => GetEitherForUsernamePassword(usernamePasswordModel.Username, usernamePasswordModel.Password.ToUnsecureString()), - _ => throw new ArgumentException($"Unexpected {nameof(ICredentialsModel)} argument") - }; - } private static Either MapCredentials(ICredentials credentials) { diff --git a/src/ConnectedMode/UI/Credentials/ICredentialsModel.cs b/src/ConnectedMode/UI/Credentials/ICredentialsModel.cs index aa44529d4d..2e14786ec5 100644 --- a/src/ConnectedMode/UI/Credentials/ICredentialsModel.cs +++ b/src/ConnectedMode/UI/Credentials/ICredentialsModel.cs @@ -19,18 +19,34 @@ */ using System.Security; +using SonarLint.VisualStudio.ConnectedMode.Persistence; +using SonarLint.VisualStudio.Core.Binding; +using SonarQube.Client.Helpers; namespace SonarLint.VisualStudio.ConnectedMode.UI.Credentials; -public interface ICredentialsModel {} +public interface ICredentialsModel +{ + ICredentials ToICredentials(); +} public class TokenCredentialsModel(SecureString token) : ICredentialsModel { public SecureString Token { get; } = token; + + public ICredentials ToICredentials() + { + return new BasicAuthCredentials(Token.ToUnsecureString(), new SecureString()); + } } public class UsernamePasswordModel(string username, SecureString password) : ICredentialsModel { public string Username { get; } = username; public SecureString Password { get; } = password; + + public ICredentials ToICredentials() + { + return new BasicAuthCredentials(Username, Password); + } } diff --git a/src/ConnectedMode/UI/ManageBinding/ManageBindingViewModel.cs b/src/ConnectedMode/UI/ManageBinding/ManageBindingViewModel.cs index 52a84d70b1..68d356b594 100644 --- a/src/ConnectedMode/UI/ManageBinding/ManageBindingViewModel.cs +++ b/src/ConnectedMode/UI/ManageBinding/ManageBindingViewModel.cs @@ -250,9 +250,10 @@ internal bool LoadConnections() { return new AdapterResponse(false); } + + var response = await connectedModeServices.SlCoreConnectionAdapter.GetServerProjectByKeyAsync(serverConnection, boundServerProject.ServerProjectKey); + SelectedConnectionInfo = ConnectionInfo.From(serverConnection); - - var response = await connectedModeServices.SlCoreConnectionAdapter.GetServerProjectByKeyAsync(serverConnection.Credentials, SelectedConnectionInfo, boundServerProject.ServerProjectKey); SelectedProject = response.ResponseData; BoundProject = SelectedProject; return new AdapterResponse(BoundProject != null); diff --git a/src/ConnectedMode/UI/ProjectSelection/ProjectSelectionViewModel.cs b/src/ConnectedMode/UI/ProjectSelection/ProjectSelectionViewModel.cs index 924bd63f2e..2c51a5bdda 100644 --- a/src/ConnectedMode/UI/ProjectSelection/ProjectSelectionViewModel.cs +++ b/src/ConnectedMode/UI/ProjectSelection/ProjectSelectionViewModel.cs @@ -86,7 +86,7 @@ internal async Task>> AdapterGetAllP return new AdapterResponseWithData>(false, null); } - return await connectedModeServices.SlCoreConnectionAdapter.GetAllProjectsAsync(ConnectionInfo, serverConnection.Credentials); + return await connectedModeServices.SlCoreConnectionAdapter.GetAllProjectsAsync(serverConnection); } internal void InitProjects(AdapterResponseWithData> response) From 553831e2e26055b43f5a15dd2cd446c9fcac7cc6 Mon Sep 17 00:00:00 2001 From: Vasileios Naskos Date: Tue, 24 Sep 2024 16:44:13 +0200 Subject: [PATCH 03/12] Continue on separation of concerns --- .../ConnectionInfoTests.cs | 4 +- .../BindingDtoSerializationTests.cs | 2 +- .../ServerConnectionsRepositoryTests.cs | 18 +++--- ...ServerConnectionsRepositoryAdapterTests.cs | 24 ++++---- .../SlCoreConnectionAdapterTests.cs | 59 ++++++++++--------- .../ManageBindingViewModelTests.cs | 9 +-- .../ManageConnectionsViewModelTest.cs | 14 ++--- .../ProjectSelectionViewModelTests.cs | 10 ++-- src/ConnectedMode/ConnectionInfo.cs | 11 ++-- .../ServerConnectionsRepositoryAdapter.cs | 27 ++++++--- src/ConnectedMode/SlCoreConnectionAdapter.cs | 2 +- .../ManageBinding/ManageBindingViewModel.cs | 2 +- .../ManageConnectionsViewModel.cs | 2 +- .../ProjectSelectionViewModel.cs | 6 +- .../Common/Helpers/ConnectionIdHelperTests.cs | 2 +- .../State/ServerConnectionsProviderTests.cs | 6 +- 16 files changed, 109 insertions(+), 89 deletions(-) diff --git a/src/ConnectedMode.UnitTests/ConnectionInfoTests.cs b/src/ConnectedMode.UnitTests/ConnectionInfoTests.cs index 26cf0123ce..3510613c67 100644 --- a/src/ConnectedMode.UnitTests/ConnectionInfoTests.cs +++ b/src/ConnectedMode.UnitTests/ConnectionInfoTests.cs @@ -29,11 +29,11 @@ public class ConnectionInfoTests [TestMethod] public void FromServerConnection_ShouldReturnConnectionInfoWithSameId() { - var sonarCloudServerConnection = new ServerConnection.SonarCloud("id"); + var sonarCloudServerConnection = new ServerConnection.SonarCloud("organization"); var connectionInfo = ConnectionInfo.From(sonarCloudServerConnection); - connectionInfo.Id.Should().Be("id"); + connectionInfo.Id.Should().Be("organization"); } [TestMethod] diff --git a/src/ConnectedMode.UnitTests/Persistence/BindingDtoSerializationTests.cs b/src/ConnectedMode.UnitTests/Persistence/BindingDtoSerializationTests.cs index c561faae44..47b75c6f44 100644 --- a/src/ConnectedMode.UnitTests/Persistence/BindingDtoSerializationTests.cs +++ b/src/ConnectedMode.UnitTests/Persistence/BindingDtoSerializationTests.cs @@ -93,7 +93,7 @@ public void Dto_FromSonarCloudBinding_SerializedAsExpected() serializeObject.Should().BeEquivalentTo( """ { - "ServerConnectionId": "org_key_123", + "ServerConnectionId": "https://sonarcloud.io/organizations/org_key_123", "ServerUri": "https://sonarcloud.io", "Organization": { "Key": "org_key_123", diff --git a/src/ConnectedMode.UnitTests/Persistence/ServerConnectionsRepositoryTests.cs b/src/ConnectedMode.UnitTests/Persistence/ServerConnectionsRepositoryTests.cs index 7f838cda6a..44d72f7ce7 100644 --- a/src/ConnectedMode.UnitTests/Persistence/ServerConnectionsRepositoryTests.cs +++ b/src/ConnectedMode.UnitTests/Persistence/ServerConnectionsRepositoryTests.cs @@ -109,8 +109,8 @@ public void TryGet_FileExistsAndConnectionDoesNotExist_ReturnsFalse() [TestMethod] public void TryGet_FileExistsAndConnectionIsSonarCloud_ReturnsSonarCloudConnection() { - var sonarCloudModel = GetSonarCloudJsonModel("myOrg"); - var expectedConnection = new SonarCloud(sonarCloudModel.Id); + var sonarCloudModel = GetSonarCloudJsonModel(); + var expectedConnection = new SonarCloud(sonarCloudModel.OrganizationKey); MockReadingFile(new ServerConnectionsListJsonModel { ServerConnections = [sonarCloudModel] }); serverConnectionModelMapper.GetServerConnection(sonarCloudModel).Returns(expectedConnection); @@ -200,7 +200,7 @@ public void TryGetAll_FileExistsAndIsEmpty_ReturnsEmptyList() [TestMethod] public void TryGetAll_FileExistsAndHasConnection_MapsModel() { - var cloudModel = GetSonarCloudJsonModel("myOrg"); + var cloudModel = GetSonarCloudJsonModel(); MockReadingFile(new ServerConnectionsListJsonModel { ServerConnections = [cloudModel] }); testSubject.TryGetAll(out _); @@ -467,7 +467,7 @@ public void TryUpdateSettingsById_FileExistsAndConnectionExists_UpdatesSettings( jsonFileHandler.TryWriteToFile(Arg.Any(), Arg.Any()).Returns(true); MockFileWithOneSonarCloudConnection(oldSmartNotifications); - var succeeded = testSubject.TryUpdateSettingsById("myOrg", new ServerConnectionSettings(newSmartNotifications)); + var succeeded = testSubject.TryUpdateSettingsById("https://sonarcloud.io/organizations/myOrg", new ServerConnectionSettings(newSmartNotifications)); succeeded.Should().BeTrue(); Received.InOrder(() => @@ -552,8 +552,8 @@ public void TryUpdateCredentialsById_SavingCredentialsThrows_ReturnsFalseAndLogs private SonarCloud MockFileWithOneSonarCloudConnection(bool isSmartNotificationsEnabled = true) { - var sonarCloudModel = GetSonarCloudJsonModel("myOrg", isSmartNotificationsEnabled); - var sonarCloud = new SonarCloud(sonarCloudModel.Id, sonarCloudModel.Settings, Substitute.For()); + var sonarCloudModel = GetSonarCloudJsonModel(isSmartNotificationsEnabled); + var sonarCloud = new SonarCloud(sonarCloudModel.OrganizationKey, sonarCloudModel.Settings, Substitute.For()); MockReadingFile(new ServerConnectionsListJsonModel { ServerConnections = [sonarCloudModel] }); serverConnectionModelMapper.GetServerConnection(sonarCloudModel).Returns(sonarCloud); @@ -575,12 +575,12 @@ private void MockReadingFile(ServerConnectionsListJsonModel modelToReturn) jsonFileHandler.ReadFile(Arg.Any()).Returns(modelToReturn); } - private static ServerConnectionJsonModel GetSonarCloudJsonModel(string id, bool isSmartNotificationsEnabled = false) + private static ServerConnectionJsonModel GetSonarCloudJsonModel(bool isSmartNotificationsEnabled = false) { return new ServerConnectionJsonModel { - Id = id, - OrganizationKey = id, + Id = "https://sonarcloud.io/organizations/myOrg", + OrganizationKey = "myOrg", Settings = new ServerConnectionSettings(isSmartNotificationsEnabled) }; } diff --git a/src/ConnectedMode.UnitTests/ServerConnectionsRepositoryAdapterTests.cs b/src/ConnectedMode.UnitTests/ServerConnectionsRepositoryAdapterTests.cs index 5aa35f79aa..e5c40eb39f 100644 --- a/src/ConnectedMode.UnitTests/ServerConnectionsRepositoryAdapterTests.cs +++ b/src/ConnectedMode.UnitTests/ServerConnectionsRepositoryAdapterTests.cs @@ -47,14 +47,14 @@ public void MefCtor_CheckIsExported() [TestMethod] public void TryGetServerConnectionById_CallServerConnectionsRepository() { - var expectedServerConnection = new SonarCloud("connection-id"); - serverConnectionsRepository.TryGet("connection-id", out _).Returns(callInfo => + var expectedServerConnection = new SonarCloud("myOrg"); + serverConnectionsRepository.TryGet("https://sonarcloud.io/organizations/myOrg", out _).Returns(callInfo => { callInfo[1] = expectedServerConnection; return true; }); - testSubject.TryGetServerConnectionById("connection-id", out var serverConnection); + testSubject.TryGet(new ConnectionInfo("myOrg", ConnectionServerType.SonarCloud), out var serverConnection); serverConnection.Should().Be(expectedServerConnection); } @@ -80,7 +80,7 @@ public void TryGetAllConnections_HasOneSonarCloudConnection_ReturnsOneMappedConn testSubject.TryGetAllConnections(out var connections); - connections.Should().BeEquivalentTo([new Connection(new ConnectionInfo(sonarCloud.Id, ConnectionServerType.SonarCloud), isSmartNotificationsEnabled)]); + connections.Should().BeEquivalentTo([new Connection(new ConnectionInfo(sonarCloud.OrganizationKey, ConnectionServerType.SonarCloud), isSmartNotificationsEnabled)]); } [TestMethod] @@ -128,7 +128,7 @@ public void TryGetAllConnectionsInfo_HasOneSonarCloudConnection_ReturnsOneMapped testSubject.TryGetAllConnectionsInfo(out var connections); - connections.Should().BeEquivalentTo([new ConnectionInfo(sonarCloud.Id, ConnectionServerType.SonarCloud)]); + connections.Should().BeEquivalentTo([new ConnectionInfo(sonarCloud.OrganizationKey, ConnectionServerType.SonarCloud)]); } [TestMethod] @@ -179,7 +179,7 @@ public void TryAddConnection_AddsSonarCloudConnection_CallsSlCoreWithMappedConne serverConnectionsRepository.Received(1) .TryAdd(Arg.Is(sc => - sc.Id == sonarCloud.Info.Id && + sc.Id == $"https://sonarcloud.io/organizations/{sonarCloud.Info.Id}" && sc.OrganizationKey == sonarCloud.Info.Id && sc.Settings.IsSmartNotificationsEnabled == sonarCloud.EnableSmartNotifications)); } @@ -240,10 +240,11 @@ public void TryAddConnection_NullCredentials_TriesAddingAConnectionWithNoCredent [DataRow(false)] public void TryDeleteConnection_ReturnsStatusFromSlCore(bool expectedStatus) { - var connectionInfoId = "http://localhost:9000"; + const string connectionInfoId = "http://localhost:9000/"; + var connectionInfo = new ConnectionInfo(connectionInfoId, ConnectionServerType.SonarQube); serverConnectionsRepository.TryDelete(connectionInfoId).Returns(expectedStatus); - var succeeded = testSubject.TryRemoveConnection(connectionInfoId); + var succeeded = testSubject.TryRemoveConnection(connectionInfo); succeeded.Should().Be(expectedStatus); } @@ -253,11 +254,12 @@ public void TryDeleteConnection_ReturnsStatusFromSlCore(bool expectedStatus) [DataRow(false)] public void TryGet_ReturnsStatusFromSlCore(bool expectedStatus) { - var connectionId = "myOrg"; + const string connectionInfoId = "myOrg"; + var connectionInfo = new ConnectionInfo(connectionInfoId, ConnectionServerType.SonarCloud); var expectedServerConnection = new SonarCloud("myOrg"); - MockTryGet(connectionId, expectedStatus, expectedServerConnection); + MockTryGet("https://sonarcloud.io/organizations/myOrg", expectedStatus, expectedServerConnection); - var succeeded = testSubject.TryGet(connectionId, out var receivedServerConnection); + var succeeded = testSubject.TryGet(connectionInfo, out var receivedServerConnection); succeeded.Should().Be(expectedStatus); receivedServerConnection.Should().Be(expectedServerConnection); diff --git a/src/ConnectedMode.UnitTests/SlCoreConnectionAdapterTests.cs b/src/ConnectedMode.UnitTests/SlCoreConnectionAdapterTests.cs index 7acd63fc7d..b3f6423852 100644 --- a/src/ConnectedMode.UnitTests/SlCoreConnectionAdapterTests.cs +++ b/src/ConnectedMode.UnitTests/SlCoreConnectionAdapterTests.cs @@ -24,6 +24,7 @@ using SonarLint.VisualStudio.ConnectedMode.UI.OrganizationSelection; using SonarLint.VisualStudio.ConnectedMode.UI.ProjectSelection; using SonarLint.VisualStudio.Core; +using SonarLint.VisualStudio.Core.Binding; using SonarLint.VisualStudio.SLCore; using SonarLint.VisualStudio.SLCore.Common.Models; using SonarLint.VisualStudio.SLCore.Core; @@ -37,7 +38,9 @@ namespace SonarLint.VisualStudio.ConnectedMode.UnitTests; [TestClass] public class SlCoreConnectionAdapterTests { - private readonly BasicAuthCredentials validToken = new ("I_AM_JUST_A_TOKEN", new SecureString()); + private static readonly BasicAuthCredentials ValidToken = new ("I_AM_JUST_A_TOKEN", new SecureString()); + private static readonly ServerConnection.SonarQube SonarQubeConnection = new(new Uri("http://localhost:9000/"), new ServerConnectionSettings(true), ValidToken); + private static readonly ServerConnection.SonarCloud SonarCloudConnection = new("myOrg", new ServerConnectionSettings(true), ValidToken); private SlCoreConnectionAdapter testSubject; private ISLCoreServiceProvider slCoreServiceProvider; @@ -145,7 +148,7 @@ public async Task ValidateConnectionAsync_SlCoreValidationThrowsException_Return { var exceptionMessage = "validation failed"; connectionConfigurationSlCoreService.When(x => x.ValidateConnectionAsync(Arg.Any())) - .Do(x => throw new Exception(exceptionMessage)); + .Do(_ => throw new Exception(exceptionMessage)); var response = await testSubject.ValidateConnectionAsync(sonarCloudConnectionInfo, new TokenCredentialsModel("token".CreateSecureString())); @@ -181,7 +184,7 @@ public async Task GetOrganizationsAsync_SlCoreThrowsException_ReturnsFailedRespo { var exceptionMessage = "validation failed"; connectionConfigurationSlCoreService.When(x => x.ListUserOrganizationsAsync(Arg.Any())) - .Do(x => throw new Exception(exceptionMessage)); + .Do(_ => throw new Exception(exceptionMessage)); var response = await testSubject.GetOrganizationsAsync(new TokenCredentialsModel("token".CreateSecureString())); @@ -254,8 +257,8 @@ public async Task GetAllProjectsAsync_SwitchesToBackgroundThread() { var threadHandlingMock = Substitute.For(); var slCoreConnectionAdapter = new SlCoreConnectionAdapter(slCoreServiceProvider, threadHandlingMock, logger); - - await slCoreConnectionAdapter.GetAllProjectsAsync(sonarQubeConnectionInfo, validToken); + + await slCoreConnectionAdapter.GetAllProjectsAsync(SonarQubeConnection); await threadHandlingMock.Received(1).RunOnBackgroundThread(Arg.Any>>>>()); } @@ -265,7 +268,7 @@ public async Task GetAllProjectsAsync_GettingConnectionConfigurationSLCoreServic { slCoreServiceProvider.TryGetTransientService(out IConnectionConfigurationSLCoreService _).Returns(false); - var response = await testSubject.GetAllProjectsAsync(sonarQubeConnectionInfo, validToken); + var response = await testSubject.GetAllProjectsAsync(SonarQubeConnection); logger.Received(1).LogVerbose($"[{nameof(IConnectionConfigurationSLCoreService)}] {SLCoreStrings.ServiceProviderNotInitialized}"); response.Success.Should().BeFalse(); @@ -274,19 +277,20 @@ public async Task GetAllProjectsAsync_GettingConnectionConfigurationSLCoreServic [TestMethod] public async Task GetAllProjectsAsync_ConnectionToSonarQubeWithToken_CallsGetAllProjectsAsyncWithCorrectParams() { - await testSubject.GetAllProjectsAsync(sonarQubeConnectionInfo, validToken); + await testSubject.GetAllProjectsAsync(SonarQubeConnection); await connectionConfigurationSlCoreService.Received(1) - .GetAllProjectsAsync(Arg.Is(x => IsExpectedSonarQubeConnectionParams(x.transientConnection, validToken.UserName))); + .GetAllProjectsAsync(Arg.Is(x => IsExpectedSonarQubeConnectionParams(x.transientConnection, ValidToken.UserName))); } [TestMethod] public async Task GetAllProjectsAsync_ConnectionToSonarQubeWithCredentials_CallsGetAllProjectsAsyncWithCorrectParams() { - var username = "username"; - var password = "password"; - - await testSubject.GetAllProjectsAsync(sonarQubeConnectionInfo, new BasicAuthCredentials(username, password.CreateSecureString())); + const string username = "username"; + const string password = "password"; + SonarQubeConnection.Credentials = new BasicAuthCredentials(username, password.CreateSecureString()); + + await testSubject.GetAllProjectsAsync(SonarQubeConnection); await connectionConfigurationSlCoreService.Received(1) .GetAllProjectsAsync(Arg.Is(x => IsExpectedSonarQubeConnectionParams(x.transientConnection, username, password))); @@ -295,19 +299,20 @@ await connectionConfigurationSlCoreService.Received(1) [TestMethod] public async Task GetAllProjectsAsync_ConnectionToSonarCloudWithToken_CallsGetAllProjectsAsyncWithCorrectParams() { - await testSubject.GetAllProjectsAsync(sonarCloudConnectionInfo, validToken); + await testSubject.GetAllProjectsAsync(SonarCloudConnection); await connectionConfigurationSlCoreService.Received(1) - .GetAllProjectsAsync(Arg.Is(x => IsExpectedSonarCloudConnectionParams(x.transientConnection, validToken.UserName))); + .GetAllProjectsAsync(Arg.Is(x => IsExpectedSonarCloudConnectionParams(x.transientConnection, ValidToken.UserName))); } [TestMethod] public async Task GetAllProjectsAsync_ConnectionToSonarCloudWithCredentials_CallsGetAllProjectsAsyncWithCorrectParams() { - var username = "username"; - var password = "password"; + const string username = "username"; + const string password = "password"; + SonarCloudConnection.Credentials = new BasicAuthCredentials(username, password.CreateSecureString()); - await testSubject.GetAllProjectsAsync(sonarCloudConnectionInfo, new BasicAuthCredentials(username, password.CreateSecureString())); + await testSubject.GetAllProjectsAsync(SonarCloudConnection); await connectionConfigurationSlCoreService.Received(1) .GetAllProjectsAsync(Arg.Is(x => IsExpectedSonarCloudConnectionParams(x.transientConnection, username, password))); @@ -319,7 +324,7 @@ public async Task GetAllProjectsAsync_ReturnsResponseFromSlCore() List expectedServerProjects = [CreateSonarProjectDto("projKey1", "projName1"), CreateSonarProjectDto("projKey2", "projName2")]; connectionConfigurationSlCoreService.GetAllProjectsAsync(Arg.Any()).Returns(new GetAllProjectsResponse(expectedServerProjects)); - var response = await testSubject.GetAllProjectsAsync(sonarCloudConnectionInfo, new BasicAuthCredentials("myToken", null)); + var response = await testSubject.GetAllProjectsAsync(SonarCloudConnection); response.Success.Should().BeTrue(); response.ResponseData.Count.Should().Be(expectedServerProjects.Count); @@ -335,7 +340,7 @@ public async Task GetServerProjectByKeyAsync_SwitchesToBackgroundThread() var threadHandlingMock = Substitute.For(); var slCoreConnectionAdapter = new SlCoreConnectionAdapter(slCoreServiceProvider, threadHandlingMock, logger); - await slCoreConnectionAdapter.GetServerProjectByKeyAsync(validToken, sonarCloudConnectionInfo, "server-project-key"); + await slCoreConnectionAdapter.GetServerProjectByKeyAsync(SonarCloudConnection, "server-project-key"); await threadHandlingMock.Received(1).RunOnBackgroundThread(Arg.Any>>>()); } @@ -345,7 +350,7 @@ public async Task GetServerProjectByKeyAsync_GettingConnectionConfigurationSLCor { slCoreServiceProvider.TryGetTransientService(out IConnectionConfigurationSLCoreService _).Returns(false); - var response = await testSubject.GetServerProjectByKeyAsync(validToken, sonarCloudConnectionInfo, "server-project-key"); + var response = await testSubject.GetServerProjectByKeyAsync(SonarCloudConnection, "server-project-key"); logger.Received(1).LogVerbose($"[{nameof(IConnectionConfigurationSLCoreService)}] {SLCoreStrings.ServiceProviderNotInitialized}"); response.Success.Should().BeFalse(); @@ -357,9 +362,9 @@ public async Task GetServerProjectByKeyAsync_SlCoreThrowsException_ReturnsFailed { const string exceptionMessage = "SLCore error"; connectionConfigurationSlCoreService.When(x => x.GetProjectNamesByKeyAsync(Arg.Any())) - .Do(x => throw new Exception(exceptionMessage)); + .Do(_ => throw new Exception(exceptionMessage)); - var response = await testSubject.GetServerProjectByKeyAsync(validToken, sonarCloudConnectionInfo, "server-project-key"); + var response = await testSubject.GetServerProjectByKeyAsync(SonarCloudConnection, "server-project-key"); logger.Received(1).LogVerbose($"{Resources.GetServerProjectByKey_Fails}: {exceptionMessage}"); response.Success.Should().BeFalse(); @@ -373,7 +378,7 @@ public async Task GetServerProjectByKeyAsync_ProjectNotFound_ReturnsFailedRespon connectionConfigurationSlCoreService.GetProjectNamesByKeyAsync(Arg.Any()) .Returns(new GetProjectNamesByKeyResponse(slCoreResponse)); - var response = await testSubject.GetServerProjectByKeyAsync(validToken, sonarCloudConnectionInfo, "project-key"); + var response = await testSubject.GetServerProjectByKeyAsync(SonarCloudConnection, "project-key"); response.Success.Should().BeFalse(); response.ResponseData.Should().BeNull(); @@ -388,7 +393,7 @@ public async Task GetServerProjectByKeyAsync_ProjectFound_ReturnsSuccessResponse }; connectionConfigurationSlCoreService.GetProjectNamesByKeyAsync(Arg.Any()) .Returns(new GetProjectNamesByKeyResponse(slCoreResponse)); - var response = await testSubject.GetServerProjectByKeyAsync(new BasicAuthCredentials("USERNAME", "SHHHHH".CreateSecureString()), sonarQubeConnectionInfo, "project-key"); + var response = await testSubject.GetServerProjectByKeyAsync(SonarQubeConnection, "project-key"); response.Success.Should().BeTrue(); response.ResponseData.Should().BeEquivalentTo(new ServerProject("project-key", "project-name")); @@ -400,9 +405,9 @@ public async Task GetAllProjectsAsync_SlCoreValidationThrowsException_ReturnsUns { var exceptionMessage = "validation failed"; connectionConfigurationSlCoreService.When(x => x.GetAllProjectsAsync(Arg.Any())) - .Do(x => throw new Exception(exceptionMessage)); + .Do(_ => throw new Exception(exceptionMessage)); - var response = await testSubject.GetAllProjectsAsync(sonarCloudConnectionInfo, new BasicAuthCredentials("token", null)); + var response = await testSubject.GetAllProjectsAsync(SonarCloudConnection); logger.Received(1).LogVerbose($"{Resources.GetAllProjects_Fails}: {exceptionMessage}"); response.Success.Should().BeFalse(); @@ -465,7 +470,7 @@ private bool IsExpectedSonarCloudConnectionParams(Either { x[0] = connectionConfigurationSlCoreService; diff --git a/src/ConnectedMode.UnitTests/UI/ManageBinding/ManageBindingViewModelTests.cs b/src/ConnectedMode.UnitTests/UI/ManageBinding/ManageBindingViewModelTests.cs index 4557d178b9..480104a598 100644 --- a/src/ConnectedMode.UnitTests/UI/ManageBinding/ManageBindingViewModelTests.cs +++ b/src/ConnectedMode.UnitTests/UI/ManageBinding/ManageBindingViewModelTests.cs @@ -671,8 +671,9 @@ await progressReporterViewModel.Received(1) [TestMethod] public async Task BindAsync_WhenConnectionNotFound_Fails() { - testSubject.SelectedConnectionInfo = new ConnectionInfo("organization", ConnectionServerType.SonarCloud); - serverConnectionsRepositoryAdapter.TryGetServerConnectionById("organization", out _).Returns(callInfo => + var connectionInfo = new ConnectionInfo("organization", ConnectionServerType.SonarCloud); + testSubject.SelectedConnectionInfo = connectionInfo; + serverConnectionsRepositoryAdapter.TryGet(connectionInfo, out _).Returns(callInfo => { callInfo[1] = null; return false; @@ -918,7 +919,7 @@ private void SetupBoundProject(ServerConnection serverConnection, ServerProject private void MockTryGetServerConnectionId(ServerConnection serverConnection = null) { - serverConnectionsRepositoryAdapter.TryGetServerConnectionById(serverConnection?.Id ?? Arg.Any(), out _).Returns(callInfo => + serverConnectionsRepositoryAdapter.TryGet(ConnectionInfo.From(serverConnection) ?? Arg.Any(), out _).Returns(callInfo => { callInfo[1] = serverConnection; return true; @@ -947,7 +948,7 @@ private void SetupBoundProjectThatDoesNotExistOnServer(ServerConnection serverCo private void MockGetServerProjectByKey(bool success, ServerProject responseData) { var slCoreConnectionAdapter = Substitute.For(); - slCoreConnectionAdapter.GetServerProjectByKeyAsync(Arg.Any(), Arg.Any(),Arg.Any()) + slCoreConnectionAdapter.GetServerProjectByKeyAsync(Arg.Any(),Arg.Any()) .Returns(Task.FromResult(new AdapterResponseWithData(success, responseData))); connectedModeServices.SlCoreConnectionAdapter.Returns(slCoreConnectionAdapter); } diff --git a/src/ConnectedMode.UnitTests/UI/ManageConnections/ManageConnectionsViewModelTest.cs b/src/ConnectedMode.UnitTests/UI/ManageConnections/ManageConnectionsViewModelTest.cs index 9cba844b50..a7ceb662fa 100644 --- a/src/ConnectedMode.UnitTests/UI/ManageConnections/ManageConnectionsViewModelTest.cs +++ b/src/ConnectedMode.UnitTests/UI/ManageConnections/ManageConnectionsViewModelTest.cs @@ -44,7 +44,7 @@ public void TestInitialize() twoConnections = [ new Connection(new ConnectionInfo("http://localhost:9000", ConnectionServerType.SonarQube), true), - new Connection(new ConnectionInfo("myOrg", ConnectionServerType.SonarCloud), false) + new Connection(new ConnectionInfo("https://sonarcloud.io/organizations/myOrg", ConnectionServerType.SonarCloud), false) ]; progressReporterViewModel = Substitute.For(); connectedModeServices = Substitute.For(); @@ -90,12 +90,12 @@ public void RemoveConnectionViewModel_ReturnsStatusFromSlCore(bool expectedStatu { InitializeTwoConnections(); var connectionToRemove = testSubject.ConnectionViewModels[0]; - serverConnectionsRepositoryAdapter.TryRemoveConnection(connectionToRemove.Connection.Info.Id).Returns(expectedStatus); + serverConnectionsRepositoryAdapter.TryRemoveConnection(connectionToRemove.Connection.Info).Returns(expectedStatus); var succeeded = testSubject.RemoveConnectionViewModel(connectionToRemove); succeeded.Should().Be(expectedStatus); - serverConnectionsRepositoryAdapter.Received(1).TryRemoveConnection(connectionToRemove.Connection.Info.Id); + serverConnectionsRepositoryAdapter.Received(1).TryRemoveConnection(connectionToRemove.Connection.Info); } [TestMethod] @@ -103,7 +103,7 @@ public void RemoveConnection_ConnectionWasRemoved_RemovesProvidedConnectionViewM { InitializeTwoConnections(); var connectionToRemove = testSubject.ConnectionViewModels[0]; - serverConnectionsRepositoryAdapter.TryRemoveConnection(connectionToRemove.Connection.Info.Id).Returns(true); + serverConnectionsRepositoryAdapter.TryRemoveConnection(connectionToRemove.Connection.Info).Returns(true); testSubject.RemoveConnectionViewModel(connectionToRemove); @@ -116,7 +116,7 @@ public void RemoveConnectionViewModel_ConnectionWasNotRemoved_DoesNotRemoveProvi { InitializeTwoConnections(); var connectionToRemove = testSubject.ConnectionViewModels[0]; - serverConnectionsRepositoryAdapter.TryRemoveConnection(connectionToRemove.Connection.Info.Id).Returns(false); + serverConnectionsRepositoryAdapter.TryRemoveConnection(connectionToRemove.Connection.Info).Returns(false); testSubject.RemoveConnectionViewModel(connectionToRemove); @@ -128,7 +128,7 @@ public void RemoveConnectionViewModel_ConnectionWasNotRemoved_DoesNotRemoveProvi public void RemoveConnection_ConnectionWasRemoved_RaisesEvents() { InitializeTwoConnections(); - serverConnectionsRepositoryAdapter.TryRemoveConnection(Arg.Any()).Returns(true); + serverConnectionsRepositoryAdapter.TryRemoveConnection(Arg.Any()).Returns(true); var eventHandler = Substitute.For(); testSubject.PropertyChanged += eventHandler; @@ -141,7 +141,7 @@ public void RemoveConnection_ConnectionWasRemoved_RaisesEvents() public void RemoveConnectionViewModel_ConnectionWasNotRemoved_DoesNotRaiseEvents() { InitializeTwoConnections(); - serverConnectionsRepositoryAdapter.TryRemoveConnection(Arg.Any()).Returns(false); + serverConnectionsRepositoryAdapter.TryRemoveConnection(Arg.Any()).Returns(false); var eventHandler = Substitute.For(); testSubject.PropertyChanged += eventHandler; diff --git a/src/ConnectedMode.UnitTests/UI/ProjectSelection/ProjectSelectionViewModelTests.cs b/src/ConnectedMode.UnitTests/UI/ProjectSelection/ProjectSelectionViewModelTests.cs index b4c92656e3..85e49cb67c 100644 --- a/src/ConnectedMode.UnitTests/UI/ProjectSelection/ProjectSelectionViewModelTests.cs +++ b/src/ConnectedMode.UnitTests/UI/ProjectSelection/ProjectSelectionViewModelTests.cs @@ -185,8 +185,8 @@ public async Task AdapterGetAllProjectsAsync_GettingServerConnectionSucceeded_Ca await testSubject.AdapterGetAllProjectsAsync(); - serverConnectionsRepositoryAdapter.Received(1).TryGet(AConnectionInfo.Id, out Arg.Any()); - await slCoreConnectionAdapter.Received(1).GetAllProjectsAsync(AConnectionInfo, expectedCredentials); + serverConnectionsRepositoryAdapter.Received(1).TryGet(AConnectionInfo, out Arg.Any()); + await slCoreConnectionAdapter.Received(1).GetAllProjectsAsync(Arg.Is(x => x.Credentials == expectedCredentials)); } [TestMethod] @@ -199,7 +199,7 @@ public async Task AdapterGetAllProjectsAsync_GettingServerConnectionFailed_Retur response.Success.Should().BeFalse(); response.ResponseData.Should().BeNull(); logger.Received(1).WriteLine(Arg.Any()); - await slCoreConnectionAdapter.DidNotReceive().GetAllProjectsAsync(Arg.Any(), Arg.Any()); + await slCoreConnectionAdapter.DidNotReceive().GetAllProjectsAsync(Arg.Any()); } [TestMethod] @@ -209,7 +209,7 @@ public async Task AdapterGetAllProjectsAsync_ReturnsResponseFromAdapter(bool exp { MockTrySonarQubeConnection(AConnectionInfo, success: true); var expectedServerProjects = new List{new("proj1", "name1"), new("proj2", "name2") }; - slCoreConnectionAdapter.GetAllProjectsAsync(AConnectionInfo, Arg.Any()) + slCoreConnectionAdapter.GetAllProjectsAsync(Arg.Any()) .Returns(new AdapterResponseWithData>(expectedResponse, expectedServerProjects)); var response = await testSubject.AdapterGetAllProjectsAsync(); @@ -225,7 +225,7 @@ private void MockInitializedProjects(List serverProjects) private void MockTrySonarQubeConnection(ConnectionInfo connectionInfo, bool success = true, ICredentials expectedCredentials = null) { - serverConnectionsRepositoryAdapter.TryGet(connectionInfo.Id, out _).Returns(callInfo => + serverConnectionsRepositoryAdapter.TryGet(connectionInfo, out _).Returns(callInfo => { callInfo[1] = new ServerConnection.SonarQube(new Uri(connectionInfo.Id), credentials: expectedCredentials); return success; diff --git a/src/ConnectedMode/ConnectionInfo.cs b/src/ConnectedMode/ConnectionInfo.cs index d2337c9d46..636f16e1ca 100644 --- a/src/ConnectedMode/ConnectionInfo.cs +++ b/src/ConnectedMode/ConnectionInfo.cs @@ -33,11 +33,12 @@ public record ConnectionInfo(string Id, ConnectionServerType ServerType) { public static ConnectionInfo From(ServerConnection serverConnection) { - return new ConnectionInfo( - serverConnection.Id, - serverConnection is ServerConnection.SonarCloud - ? ConnectionServerType.SonarCloud - : ConnectionServerType.SonarQube); + return serverConnection switch + { + ServerConnection.SonarQube sonarQubeConnection => new ConnectionInfo(sonarQubeConnection.Id, ConnectionServerType.SonarQube), + ServerConnection.SonarCloud sonarCloudConnection => new ConnectionInfo(sonarCloudConnection.OrganizationKey, ConnectionServerType.SonarCloud), + _ => null + }; } } diff --git a/src/ConnectedMode/ServerConnectionsRepositoryAdapter.cs b/src/ConnectedMode/ServerConnectionsRepositoryAdapter.cs index 8730fd7e3b..e3ad9b1131 100644 --- a/src/ConnectedMode/ServerConnectionsRepositoryAdapter.cs +++ b/src/ConnectedMode/ServerConnectionsRepositoryAdapter.cs @@ -29,21 +29,21 @@ namespace SonarLint.VisualStudio.ConnectedMode; public interface IServerConnectionsRepositoryAdapter { - bool TryGetServerConnectionById(string connectionId, out ServerConnection serverConnection); + bool TryGetServerConnectionById(ConnectionInfo connectionInfo, out ServerConnection serverConnection); bool TryGetAllConnections(out List connections); bool TryGetAllConnectionsInfo(out List connectionInfos); - bool TryRemoveConnection(string connectionInfoId); + bool TryRemoveConnection(ConnectionInfo connectionInfo); bool TryAddConnection(Connection connection, ICredentialsModel credentialsModel); - bool TryGet(string connectionId, out ServerConnection serverConnection); + bool TryGet(ConnectionInfo connectionInfo, out ServerConnection serverConnection); } [Export(typeof(IServerConnectionsRepositoryAdapter))] [method: ImportingConstructor] internal class ServerConnectionsRepositoryAdapter(IServerConnectionsRepository serverConnectionsRepository) : IServerConnectionsRepositoryAdapter { - public bool TryGetServerConnectionById(string connectionId, out ServerConnection serverConnection) + public bool TryGetServerConnectionById(ConnectionInfo connectionInfo, out ServerConnection serverConnection) { - return serverConnectionsRepository.TryGet(connectionId, out serverConnection); + return TryGet(connectionInfo, out serverConnection); } public bool TryGetAllConnections(out List connections) @@ -67,14 +67,16 @@ public bool TryAddConnection(Connection connection, ICredentialsModel credential return serverConnectionsRepository.TryAdd(serverConnection); } - public bool TryGet(string connectionId, out ServerConnection serverConnection) + public bool TryGet(ConnectionInfo connectionInfo, out ServerConnection serverConnection) { + var connectionId = GetServerIdFromConnectionInfo(connectionInfo); return serverConnectionsRepository.TryGet(connectionId, out serverConnection); } - public bool TryRemoveConnection(string connectionInfoId) + public bool TryRemoveConnection(ConnectionInfo connectionInfo) { - return serverConnectionsRepository.TryDelete(connectionInfoId); + var connectionId = GetServerIdFromConnectionInfo(connectionInfo); + return serverConnectionsRepository.TryDelete(connectionId); } private static Connection MapServerConnectionModel(ServerConnection serverConnection) @@ -107,4 +109,13 @@ private static ICredentials MapCredentials(ICredentialsModel credentialsModel) return null; } } + + private static string GetServerIdFromConnectionInfo(ConnectionInfo connectionInfo) + { + ServerConnection partialServerConnection = connectionInfo.ServerType == ConnectionServerType.SonarCloud + ? new ServerConnection.SonarCloud(connectionInfo.Id) + : new ServerConnection.SonarQube(new Uri(connectionInfo.Id)); + + return partialServerConnection.Id; + } } diff --git a/src/ConnectedMode/SlCoreConnectionAdapter.cs b/src/ConnectedMode/SlCoreConnectionAdapter.cs index 23b6c6228d..90a84b5557 100644 --- a/src/ConnectedMode/SlCoreConnectionAdapter.cs +++ b/src/ConnectedMode/SlCoreConnectionAdapter.cs @@ -93,7 +93,7 @@ public Task>> GetOrganizations try { - var credentials = MapCredentials(credentialsModel.ToICredentials()); + var credentials = MapCredentials(credentialsModel?.ToICredentials()); var response = await connectionConfigurationSlCoreService.ListUserOrganizationsAsync(new ListUserOrganizationsParams(credentials)); var organizationDisplays = response.userOrganizations.Select(o => new OrganizationDisplay(o.key, o.name)).ToList(); diff --git a/src/ConnectedMode/UI/ManageBinding/ManageBindingViewModel.cs b/src/ConnectedMode/UI/ManageBinding/ManageBindingViewModel.cs index 68d356b594..754c0b63f6 100644 --- a/src/ConnectedMode/UI/ManageBinding/ManageBindingViewModel.cs +++ b/src/ConnectedMode/UI/ManageBinding/ManageBindingViewModel.cs @@ -261,7 +261,7 @@ internal bool LoadConnections() internal /* for testing */ async Task BindAsync() { - if (!connectedModeServices.ServerConnectionsRepositoryAdapter.TryGetServerConnectionById(SelectedConnectionInfo?.Id, out var serverConnection)) + if (!connectedModeServices.ServerConnectionsRepositoryAdapter.TryGet(SelectedConnectionInfo, out var serverConnection)) { return new AdapterResponse(false); } diff --git a/src/ConnectedMode/UI/ManageConnections/ManageConnectionsViewModel.cs b/src/ConnectedMode/UI/ManageConnections/ManageConnectionsViewModel.cs index 8dfb8b5308..31ee57b31c 100644 --- a/src/ConnectedMode/UI/ManageConnections/ManageConnectionsViewModel.cs +++ b/src/ConnectedMode/UI/ManageConnections/ManageConnectionsViewModel.cs @@ -84,7 +84,7 @@ internal bool InitializeConnectionViewModels() internal bool RemoveConnectionViewModel(ConnectionViewModel connectionViewModel) { - var succeeded = connectedModeServices.ServerConnectionsRepositoryAdapter.TryRemoveConnection(connectionViewModel.Connection.Info.Id); + var succeeded = connectedModeServices.ServerConnectionsRepositoryAdapter.TryRemoveConnection(connectionViewModel.Connection.Info); if (succeeded) { ConnectionViewModels.Remove(connectionViewModel); diff --git a/src/ConnectedMode/UI/ProjectSelection/ProjectSelectionViewModel.cs b/src/ConnectedMode/UI/ProjectSelection/ProjectSelectionViewModel.cs index 2c51a5bdda..d87be8882c 100644 --- a/src/ConnectedMode/UI/ProjectSelection/ProjectSelectionViewModel.cs +++ b/src/ConnectedMode/UI/ProjectSelection/ProjectSelectionViewModel.cs @@ -30,7 +30,6 @@ public class ProjectSelectionViewModel( IProgressReporterViewModel progressReporterViewModel) : ViewModelBase { - public ObservableCollection ProjectResults { get; } = []; public ConnectionInfo ConnectionInfo { get; } = connectionInfo; @@ -79,7 +78,7 @@ public async Task InitializeProjectWithProgressAsync() internal async Task>> AdapterGetAllProjectsAsync() { - var serverConnectionCredentials = connectedModeServices.ServerConnectionsRepositoryAdapter.TryGet(ConnectionInfo.Id, out var serverConnection); + var serverConnectionCredentials = connectedModeServices.ServerConnectionsRepositoryAdapter.TryGet(ConnectionInfo, out var serverConnection); if (!serverConnectionCredentials) { connectedModeServices.Logger.WriteLine(UiResources.LoadingProjectsFailedTextForNotFoundServerConnection); @@ -96,6 +95,7 @@ internal void InitProjects(AdapterResponseWithData> response { ProjectResults.Add(serverProject); } + RaisePropertyChanged(nameof(NoProjectExists)); } @@ -105,7 +105,7 @@ private void SearchForProject() { return; } - + ProjectResults.Clear(); ProjectResults.Add(new ServerProject(Key: $"{ProjectSearchTerm.ToLower().Replace(" ", "_")}_1", diff --git a/src/SLCore.UnitTests/Common/Helpers/ConnectionIdHelperTests.cs b/src/SLCore.UnitTests/Common/Helpers/ConnectionIdHelperTests.cs index 04e3a80ffe..59e4377235 100644 --- a/src/SLCore.UnitTests/Common/Helpers/ConnectionIdHelperTests.cs +++ b/src/SLCore.UnitTests/Common/Helpers/ConnectionIdHelperTests.cs @@ -63,7 +63,7 @@ public void GetUriFromConnectionId_ReturnsAsExpected(string connectionId, string [TestMethod] [DataRow("http://someuri.com", null, "sq|http://someuri.com/")] - [DataRow("https://sonarcloud.io", "something", "sc|something")] + [DataRow("https://sonarcloud.io", "something", "sc|https://sonarcloud.io/organizations/something")] public void GetConnectionIdFromServerConnection_PassUri_ReturnsAsExpected(string uriString, string organisation, string expectedConnectionId) { var uri = new Uri(uriString); diff --git a/src/SLCore.UnitTests/State/ServerConnectionsProviderTests.cs b/src/SLCore.UnitTests/State/ServerConnectionsProviderTests.cs index a051512a15..3eefe1a1f9 100644 --- a/src/SLCore.UnitTests/State/ServerConnectionsProviderTests.cs +++ b/src/SLCore.UnitTests/State/ServerConnectionsProviderTests.cs @@ -63,13 +63,13 @@ public void GetServerConnections_CorrectlyReturnsSonarQubeConnection() [TestMethod] public void GetServerConnections_CorrectlyReturnsSonarCloudConnection() { - const string connectionId = "connectionId"; + const string connectionId = "https://sonarcloud.io/organizations/org"; var serverUri = new Uri("https://sonarcloud.io/"); const string organizationKey = "org"; var binding = new BoundServerProject("solution", "project", new ServerConnection.SonarCloud(organizationKey)); var solutionBindingRepository = SetUpBindingRepository(binding); var connectionIdHelper = Substitute.For(); - connectionIdHelper.GetConnectionIdFromServerConnection(Arg.Is(s => s.ServerUri.Equals(serverUri) && s.Id == organizationKey)).Returns(connectionId); + connectionIdHelper.GetConnectionIdFromServerConnection(Arg.Is(s => s.ServerUri.Equals(serverUri) && s.Id == connectionId)).Returns(connectionId); var testSubject = CreateTestSubject(solutionBindingRepository, connectionIdHelper); var serverConnections = testSubject.GetServerConnections(); @@ -91,7 +91,7 @@ public void GetServerConnections_CorrectlyHandlesMultipleConnections() var serverConnections = testSubject.GetServerConnections(); serverConnections.Should().HaveCount(3); - serverConnections["sc|myorg"].Should().BeOfType(); + serverConnections["sc|https://sonarcloud.io/organizations/myorg"].Should().BeOfType(); serverConnections["sq|http://localhost/"].Should().BeOfType(); serverConnections["sq|https://next.sonarqube.org/sonarqube/"].Should().BeOfType(); } From db71335e3c9e6b47b42ad83871b466a597904a8f Mon Sep 17 00:00:00 2001 From: Vasileios Naskos Date: Mon, 23 Sep 2024 13:04:33 +0200 Subject: [PATCH 04/12] Replace hardcoded url with Resource --- src/Core/Binding/ServerConnection.cs | 2 +- src/Core/CoreStrings.Designer.cs | 10 +++++++++- src/Core/CoreStrings.resx | 3 +++ 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/Core/Binding/ServerConnection.cs b/src/Core/Binding/ServerConnection.cs index ab8f1328c9..f4d2f5c2ee 100644 --- a/src/Core/Binding/ServerConnection.cs +++ b/src/Core/Binding/ServerConnection.cs @@ -50,7 +50,7 @@ private ServerConnection(string id, ServerConnectionSettings settings = null, IC public sealed class SonarCloud : ServerConnection { - private const string SonarCloudUrl = "https://sonarcloud.io"; + private static readonly string SonarCloudUrl = CoreStrings.SonarCloudUrl; public SonarCloud(string organizationKey, ServerConnectionSettings settings = null, ICredentials credentials = null) : base(OrganizationKeyToId(organizationKey), settings, credentials) diff --git a/src/Core/CoreStrings.Designer.cs b/src/Core/CoreStrings.Designer.cs index a7ba60a36f..ee252a3361 100644 --- a/src/Core/CoreStrings.Designer.cs +++ b/src/Core/CoreStrings.Designer.cs @@ -1,7 +1,6 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.42000 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. @@ -188,6 +187,15 @@ public static string Settings_SavedSettingsFile { } } + /// + /// Looks up a localized string similar to https://sonarcloud.io. + /// + public static string SonarCloudUrl { + get { + return ResourceManager.GetString("SonarCloudUrl", resourceCulture); + } + } + /// /// Looks up a localized string similar to SonarQube request failed: {0} {1}. /// diff --git a/src/Core/CoreStrings.resx b/src/Core/CoreStrings.resx index e670da190b..05482c564f 100644 --- a/src/Core/CoreStrings.resx +++ b/src/Core/CoreStrings.resx @@ -190,4 +190,7 @@ Solution/folder is not in a git repository + + https://sonarcloud.io + \ No newline at end of file From e0b7b61d59fa6594b67bd2daac4db8e67c26ae09 Mon Sep 17 00:00:00 2001 From: Vasileios Naskos Date: Tue, 24 Sep 2024 16:30:08 +0200 Subject: [PATCH 05/12] Fix rebase issues --- .../UI/ManageBinding/ManageBindingViewModelTests.cs | 4 ++-- .../ServerConnectionsRepositoryAdapter.cs | 6 ------ .../UI/ManageBinding/ManageBindingViewModel.cs | 13 +++++-------- 3 files changed, 7 insertions(+), 16 deletions(-) diff --git a/src/ConnectedMode.UnitTests/UI/ManageBinding/ManageBindingViewModelTests.cs b/src/ConnectedMode.UnitTests/UI/ManageBinding/ManageBindingViewModelTests.cs index 480104a598..33e4222194 100644 --- a/src/ConnectedMode.UnitTests/UI/ManageBinding/ManageBindingViewModelTests.cs +++ b/src/ConnectedMode.UnitTests/UI/ManageBinding/ManageBindingViewModelTests.cs @@ -799,7 +799,7 @@ public async Task UseSharedBindingAsync_SharedBindingForExistSonarQubeConnection var response = await testSubject.UseSharedBindingAsync(); response.Success.Should().BeTrue(); - serverConnectionsRepositoryAdapter.Received(1).TryGetServerConnectionById(testSubject.SharedBindingConfigModel.Uri.ToString(), out _); + serverConnectionsRepositoryAdapter.Received(1).TryGet(new ConnectionInfo(testSubject.SharedBindingConfigModel.Uri.ToString(), ConnectionServerType.SonarQube), out _); await bindingController.Received(1) .BindAsync(Arg.Is(proj => proj.ServerConnection == expectedServerConnection), Arg.Any()); } @@ -815,7 +815,7 @@ public async Task UseSharedBindingAsync_SharedBindingForExistingSonarCloudConnec var response = await testSubject.UseSharedBindingAsync(); response.Success.Should().BeTrue(); - serverConnectionsRepositoryAdapter.Received(1).TryGetServerConnectionById(testSubject.SharedBindingConfigModel.Organization, out _); + serverConnectionsRepositoryAdapter.Received(1).TryGet(new ConnectionInfo(testSubject.SharedBindingConfigModel.Organization, ConnectionServerType.SonarCloud), out _); await bindingController.Received(1) .BindAsync(Arg.Is(proj => proj.ServerConnection == expectedServerConnection), Arg.Any()); } diff --git a/src/ConnectedMode/ServerConnectionsRepositoryAdapter.cs b/src/ConnectedMode/ServerConnectionsRepositoryAdapter.cs index e3ad9b1131..3935514fd1 100644 --- a/src/ConnectedMode/ServerConnectionsRepositoryAdapter.cs +++ b/src/ConnectedMode/ServerConnectionsRepositoryAdapter.cs @@ -29,7 +29,6 @@ namespace SonarLint.VisualStudio.ConnectedMode; public interface IServerConnectionsRepositoryAdapter { - bool TryGetServerConnectionById(ConnectionInfo connectionInfo, out ServerConnection serverConnection); bool TryGetAllConnections(out List connections); bool TryGetAllConnectionsInfo(out List connectionInfos); bool TryRemoveConnection(ConnectionInfo connectionInfo); @@ -41,11 +40,6 @@ public interface IServerConnectionsRepositoryAdapter [method: ImportingConstructor] internal class ServerConnectionsRepositoryAdapter(IServerConnectionsRepository serverConnectionsRepository) : IServerConnectionsRepositoryAdapter { - public bool TryGetServerConnectionById(ConnectionInfo connectionInfo, out ServerConnection serverConnection) - { - return TryGet(connectionInfo, out serverConnection); - } - public bool TryGetAllConnections(out List connections) { var succeeded = serverConnectionsRepository.TryGetAll(out var serverConnections); diff --git a/src/ConnectedMode/UI/ManageBinding/ManageBindingViewModel.cs b/src/ConnectedMode/UI/ManageBinding/ManageBindingViewModel.cs index 754c0b63f6..712e9b2907 100644 --- a/src/ConnectedMode/UI/ManageBinding/ManageBindingViewModel.cs +++ b/src/ConnectedMode/UI/ManageBinding/ManageBindingViewModel.cs @@ -176,10 +176,12 @@ public async Task ExportBindingConfigurationAsync() internal async Task UseSharedBindingAsync() { - var connectionId = GetConnectionIdFromSharedBindingConfig(); - if (!connectedModeServices.ServerConnectionsRepositoryAdapter.TryGetServerConnectionById(connectionId, out var serverConnection)) + var connection = SharedBindingConfigModel.IsSonarCloud() + ? new ConnectionInfo(SharedBindingConfigModel.Organization, ConnectionServerType.SonarCloud) + : new ConnectionInfo(SharedBindingConfigModel.Uri.ToString(), ConnectionServerType.SonarQube); + if (!connectedModeServices.ServerConnectionsRepositoryAdapter.TryGet(connection, out var serverConnection)) { - connectedModeServices.Logger.WriteLine(ConnectedMode.Resources.UseSharedBinding_ConnectionNotFound, connectionId); + connectedModeServices.Logger.WriteLine(ConnectedMode.Resources.UseSharedBinding_ConnectionNotFound, connection.Id); connectedModeServices.MessageBox.Show(UiResources.NotFoundConnectionForSharedBindingMessageBoxText, UiResources.NotFoundConnectionForSharedBindingMessageBoxCaption, MessageBoxButton.OK, MessageBoxImage.Warning); return new AdapterResponse(false); } @@ -268,11 +270,6 @@ internal bool LoadConnections() return await BindAsync(serverConnection, SelectedProject?.Key); } - private string GetConnectionIdFromSharedBindingConfig() - { - return SharedBindingConfigModel.IsSonarCloud() ? new ServerConnection.SonarCloud(SharedBindingConfigModel.Organization).Id : new ServerConnection.SonarQube(SharedBindingConfigModel.Uri).Id; - } - private async Task BindAsync(ServerConnection serverConnection, string serverProjectKey) { try From db52a0cad25d414583a3974bf863d7c50dac449d Mon Sep 17 00:00:00 2001 From: Vasileios Naskos Date: Tue, 24 Sep 2024 17:06:00 +0200 Subject: [PATCH 06/12] Validation of new SonarCloud connection does not have an organization --- src/ConnectedMode/SlCoreConnectionAdapter.cs | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/ConnectedMode/SlCoreConnectionAdapter.cs b/src/ConnectedMode/SlCoreConnectionAdapter.cs index 90a84b5557..fb973642bf 100644 --- a/src/ConnectedMode/SlCoreConnectionAdapter.cs +++ b/src/ConnectedMode/SlCoreConnectionAdapter.cs @@ -75,10 +75,8 @@ public SlCoreConnectionAdapter(ISLCoreServiceProvider serviceProvider, IThreadHa public async Task ValidateConnectionAsync(ConnectionInfo connectionInfo, ICredentialsModel credentialsModel) { var credentials = credentialsModel.ToICredentials(); - ServerConnection serverConnection = connectionInfo.ServerType == ConnectionServerType.SonarCloud - ? new ServerConnection.SonarCloud(connectionInfo.Id, credentials: credentials) - : new ServerConnection.SonarQube(new Uri(connectionInfo.Id), credentials: credentials); - var validateConnectionParams = new ValidateConnectionParams(GetTransientConnectionDto(serverConnection)); + + var validateConnectionParams = new ValidateConnectionParams(GetTransientConnectionDto(connectionInfo, credentials)); return await ValidateConnectionAsync(validateConnectionParams); } @@ -203,6 +201,20 @@ private bool TryGetConnectionConfigurationSlCoreService(out IConnectionConfigura logger.LogVerbose($"[{nameof(IConnectionConfigurationSLCoreService)}] {SLCoreStrings.ServiceProviderNotInitialized}"); return false; } + + private static Either GetTransientConnectionDto(ConnectionInfo connectionInfo, ICredentials credentials) + { + var credentialsDto = MapCredentials(credentials); + + return connectionInfo.ServerType switch + { + ConnectionServerType.SonarQube => Either.CreateLeft( + new TransientSonarQubeConnectionDto(connectionInfo.Id, credentialsDto)), + ConnectionServerType.SonarCloud => Either.CreateRight( + new TransientSonarCloudConnectionDto(connectionInfo.Id, credentialsDto)), + _ => null + }; + } private static Either GetTransientConnectionDto(ServerConnection serverConnection) { From f4b868e5fc6e6ff94f23d879a68bd5c99de8d068 Mon Sep 17 00:00:00 2001 From: Vasileios Naskos Date: Tue, 24 Sep 2024 17:14:48 +0200 Subject: [PATCH 07/12] Throw exception if credentialsModel is null --- src/ConnectedMode/SlCoreConnectionAdapter.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/ConnectedMode/SlCoreConnectionAdapter.cs b/src/ConnectedMode/SlCoreConnectionAdapter.cs index fb973642bf..59e8980c04 100644 --- a/src/ConnectedMode/SlCoreConnectionAdapter.cs +++ b/src/ConnectedMode/SlCoreConnectionAdapter.cs @@ -91,7 +91,11 @@ public Task>> GetOrganizations try { - var credentials = MapCredentials(credentialsModel?.ToICredentials()); + if (credentialsModel == null) + { + throw new ArgumentException($"Unexpected {nameof(ICredentialsModel)} argument"); + } + var credentials = MapCredentials(credentialsModel.ToICredentials()); var response = await connectionConfigurationSlCoreService.ListUserOrganizationsAsync(new ListUserOrganizationsParams(credentials)); var organizationDisplays = response.userOrganizations.Select(o => new OrganizationDisplay(o.key, o.name)).ToList(); From 955edfeeedc5c852b92e46d2bfc6b2b8a2237521 Mon Sep 17 00:00:00 2001 From: Vasileios Naskos Date: Tue, 24 Sep 2024 17:22:57 +0200 Subject: [PATCH 08/12] Throw exception in case of unknown connection type --- src/ConnectedMode/ConnectionInfo.cs | 2 +- src/ConnectedMode/Resources.Designer.cs | 10 +++++++++- src/ConnectedMode/Resources.resx | 3 +++ src/ConnectedMode/SlCoreConnectionAdapter.cs | 4 ++-- 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/ConnectedMode/ConnectionInfo.cs b/src/ConnectedMode/ConnectionInfo.cs index 636f16e1ca..c4cad0d6ac 100644 --- a/src/ConnectedMode/ConnectionInfo.cs +++ b/src/ConnectedMode/ConnectionInfo.cs @@ -37,7 +37,7 @@ public static ConnectionInfo From(ServerConnection serverConnection) { ServerConnection.SonarQube sonarQubeConnection => new ConnectionInfo(sonarQubeConnection.Id, ConnectionServerType.SonarQube), ServerConnection.SonarCloud sonarCloudConnection => new ConnectionInfo(sonarCloudConnection.OrganizationKey, ConnectionServerType.SonarCloud), - _ => null + _ => throw new ArgumentException(Resources.UnexpectedConnectionType) }; } } diff --git a/src/ConnectedMode/Resources.Designer.cs b/src/ConnectedMode/Resources.Designer.cs index 4704aa2ab2..6c345152a3 100644 --- a/src/ConnectedMode/Resources.Designer.cs +++ b/src/ConnectedMode/Resources.Designer.cs @@ -1,7 +1,6 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.42000 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. @@ -636,6 +635,15 @@ internal static string TimedUpdateTriggered { } } + /// + /// Looks up a localized string similar to Unexpected server connection type. + /// + internal static string UnexpectedConnectionType { + get { + return ResourceManager.GetString("UnexpectedConnectionType", resourceCulture); + } + } + /// /// Looks up a localized string similar to [ConnectedMode/UseSharedBinding] Connection to server {0} could not be found. /// diff --git a/src/ConnectedMode/Resources.resx b/src/ConnectedMode/Resources.resx index 2a87e5d083..cddfd4dd1c 100644 --- a/src/ConnectedMode/Resources.resx +++ b/src/ConnectedMode/Resources.resx @@ -322,4 +322,7 @@ [ConnectedMode/UseSharedBinding] Connection to server {0} could not be found + + Unexpected server connection type + \ No newline at end of file diff --git a/src/ConnectedMode/SlCoreConnectionAdapter.cs b/src/ConnectedMode/SlCoreConnectionAdapter.cs index 59e8980c04..3a5b27fd7b 100644 --- a/src/ConnectedMode/SlCoreConnectionAdapter.cs +++ b/src/ConnectedMode/SlCoreConnectionAdapter.cs @@ -216,7 +216,7 @@ private static Either Either.CreateRight( new TransientSonarCloudConnectionDto(connectionInfo.Id, credentialsDto)), - _ => null + _ => throw new ArgumentException(Resources.UnexpectedConnectionType) }; } @@ -230,7 +230,7 @@ private static Either Either.CreateRight( new TransientSonarCloudConnectionDto(sonarCloudConnection.OrganizationKey, credentials)), - _ => null + _ => throw new ArgumentException(Resources.UnexpectedConnectionType) }; } From 290d104e658b893fa9a52afdcb1c778a936a59a3 Mon Sep 17 00:00:00 2001 From: Vasileios Naskos Date: Wed, 25 Sep 2024 08:14:33 +0200 Subject: [PATCH 09/12] Reuse CoreStrings.SonarCloudUrl --- src/ConnectedMode.UnitTests/ConnectionInfoTests.cs | 3 ++- src/ConnectedMode/ConnectionInfo.cs | 4 ++-- src/ConnectedMode/UI/Resources/UiResources.Designer.cs | 10 ---------- src/ConnectedMode/UI/Resources/UiResources.resx | 3 --- 4 files changed, 4 insertions(+), 16 deletions(-) diff --git a/src/ConnectedMode.UnitTests/ConnectionInfoTests.cs b/src/ConnectedMode.UnitTests/ConnectionInfoTests.cs index 3510613c67..7b59fa77ec 100644 --- a/src/ConnectedMode.UnitTests/ConnectionInfoTests.cs +++ b/src/ConnectedMode.UnitTests/ConnectionInfoTests.cs @@ -19,6 +19,7 @@ */ using SonarLint.VisualStudio.ConnectedMode.UI.Resources; +using SonarLint.VisualStudio.Core; using SonarLint.VisualStudio.Core.Binding; namespace SonarLint.VisualStudio.ConnectedMode.UnitTests; @@ -61,7 +62,7 @@ public void GetIdForTransientConnection_SonarCloudWithNullId_ReturnsSonarCloudUr { var connectionInfo = new ConnectionInfo(null, ConnectionServerType.SonarCloud); - connectionInfo.GetIdForTransientConnection().Should().Be(UiResources.SonarCloudUrl); + connectionInfo.GetIdForTransientConnection().Should().Be(CoreStrings.SonarCloudUrl); } [TestMethod] diff --git a/src/ConnectedMode/ConnectionInfo.cs b/src/ConnectedMode/ConnectionInfo.cs index c4cad0d6ac..a6df96a980 100644 --- a/src/ConnectedMode/ConnectionInfo.cs +++ b/src/ConnectedMode/ConnectionInfo.cs @@ -18,7 +18,7 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -using SonarLint.VisualStudio.ConnectedMode.UI.Resources; +using SonarLint.VisualStudio.Core; using SonarLint.VisualStudio.Core.Binding; namespace SonarLint.VisualStudio.ConnectedMode; @@ -54,7 +54,7 @@ public static string GetIdForTransientConnection(this ConnectionInfo connection) { if (connection.Id == null && connection.ServerType == ConnectionServerType.SonarCloud) { - return UiResources.SonarCloudUrl; + return CoreStrings.SonarCloudUrl; } return connection.Id; } diff --git a/src/ConnectedMode/UI/Resources/UiResources.Designer.cs b/src/ConnectedMode/UI/Resources/UiResources.Designer.cs index e7eb4e4201..ea19607b44 100644 --- a/src/ConnectedMode/UI/Resources/UiResources.Designer.cs +++ b/src/ConnectedMode/UI/Resources/UiResources.Designer.cs @@ -1,7 +1,6 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.42000 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. @@ -799,15 +798,6 @@ public static string SonarCloudDescription { } } - /// - /// Looks up a localized string similar to https://sonarcloud.io/. - /// - public static string SonarCloudUrl { - get { - return ResourceManager.GetString("SonarCloudUrl", resourceCulture); - } - } - /// /// Looks up a localized string similar to account/security. /// diff --git a/src/ConnectedMode/UI/Resources/UiResources.resx b/src/ConnectedMode/UI/Resources/UiResources.resx index 6498679539..723aa0424d 100644 --- a/src/ConnectedMode/UI/Resources/UiResources.resx +++ b/src/ConnectedMode/UI/Resources/UiResources.resx @@ -306,9 +306,6 @@ Export - - https://sonarcloud.io/ - No connection exists From 734aa31a1a2a26872d73cb5c67bc25f2e439f1ab Mon Sep 17 00:00:00 2001 From: Vasileios Naskos Date: Wed, 25 Sep 2024 08:32:50 +0200 Subject: [PATCH 10/12] Add documentation --- src/ConnectedMode/ConnectionInfo.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/ConnectedMode/ConnectionInfo.cs b/src/ConnectedMode/ConnectionInfo.cs index a6df96a980..2e4bca6628 100644 --- a/src/ConnectedMode/ConnectionInfo.cs +++ b/src/ConnectedMode/ConnectionInfo.cs @@ -29,6 +29,11 @@ public enum ConnectionServerType SonarCloud } +/// +/// Model containing connection information, intended to be used by UI components +/// +/// The organization key for SonarCloud or the server uri for SonarQube +/// The type of server (SonarCloud, SonarQube) public record ConnectionInfo(string Id, ConnectionServerType ServerType) { public static ConnectionInfo From(ServerConnection serverConnection) From 56b85a711434b394c769de5f28179e0b3571a90b Mon Sep 17 00:00:00 2001 From: Vasileios Naskos Date: Wed, 25 Sep 2024 08:49:16 +0200 Subject: [PATCH 11/12] Fix test --- .../UI/ManageBinding/ManageBindingViewModelTests.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/ConnectedMode.UnitTests/UI/ManageBinding/ManageBindingViewModelTests.cs b/src/ConnectedMode.UnitTests/UI/ManageBinding/ManageBindingViewModelTests.cs index 33e4222194..2447a2a7ce 100644 --- a/src/ConnectedMode.UnitTests/UI/ManageBinding/ManageBindingViewModelTests.cs +++ b/src/ConnectedMode.UnitTests/UI/ManageBinding/ManageBindingViewModelTests.cs @@ -855,7 +855,8 @@ await bindingController.DidNotReceive() [TestMethod] public async Task UseSharedBindingAsync_BindingFails_ReturnsFalse() { - MockTryGetServerConnectionId(); + var sonarCloudConnection = new ServerConnection.SonarCloud("organization", credentials: validCredentials); + MockTryGetServerConnection(sonarCloudConnection); bindingController.When(x => x.BindAsync(Arg.Any(), Arg.Any())) .Do(_ => throw new Exception()); testSubject.SharedBindingConfigModel = sonarCloudSharedBindingConfigModel; @@ -911,17 +912,17 @@ private void SetupBoundProject(ServerConnection serverConnection, ServerProject var configurationProvider = Substitute.For(); configurationProvider.GetConfiguration().Returns(new BindingConfiguration(boundServerProject, SonarLintMode.Connected, "binding-dir")); connectedModeServices.ConfigurationProvider.Returns(configurationProvider); - MockTryGetServerConnectionId(serverConnection); + MockTryGetServerConnection(serverConnection); solutionInfoProvider.GetSolutionNameAsync().Returns(ALocalProjectKey); MockGetServerProjectByKey(true, expectedServerProject); } - private void MockTryGetServerConnectionId(ServerConnection serverConnection = null) + private void MockTryGetServerConnection(ServerConnection expectedServerConnection = null) { - serverConnectionsRepositoryAdapter.TryGet(ConnectionInfo.From(serverConnection) ?? Arg.Any(), out _).Returns(callInfo => + serverConnectionsRepositoryAdapter.TryGet(Arg.Any(), out _).Returns(callInfo => { - callInfo[1] = serverConnection; + callInfo[1] = expectedServerConnection; return true; }); } From 6f9ed5094908811ea6f8383a7c4bb1e521818a0b Mon Sep 17 00:00:00 2001 From: Vasileios Naskos Date: Wed, 25 Sep 2024 10:58:55 +0200 Subject: [PATCH 12/12] Replace redundant check with null conditional operator --- src/ConnectedMode/SlCoreConnectionAdapter.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/ConnectedMode/SlCoreConnectionAdapter.cs b/src/ConnectedMode/SlCoreConnectionAdapter.cs index 3a5b27fd7b..1ed36eaea2 100644 --- a/src/ConnectedMode/SlCoreConnectionAdapter.cs +++ b/src/ConnectedMode/SlCoreConnectionAdapter.cs @@ -91,11 +91,7 @@ public Task>> GetOrganizations try { - if (credentialsModel == null) - { - throw new ArgumentException($"Unexpected {nameof(ICredentialsModel)} argument"); - } - var credentials = MapCredentials(credentialsModel.ToICredentials()); + var credentials = MapCredentials(credentialsModel?.ToICredentials()); var response = await connectionConfigurationSlCoreService.ListUserOrganizationsAsync(new ListUserOrganizationsParams(credentials)); var organizationDisplays = response.userOrganizations.Select(o => new OrganizationDisplay(o.key, o.name)).ToList();