From a8cba9151c8a4634369eec3380bda49250d4a1dd Mon Sep 17 00:00:00 2001 From: Sophio Japharidze Date: Fri, 21 Jun 2024 15:25:03 +0200 Subject: [PATCH] SLVSCODE-747 get user organizations --- .../ls/SonarLintExtendedLanguageServer.java | 4 +++ .../sonarlint/ls/SonarLintLanguageServer.java | 8 +++++ .../sonarlint/ls/backend/BackendService.java | 8 +++++ .../ls/backend/BackendServiceTests.java | 17 +++++++++++ .../LanguageServerWithFoldersMediumTests.java | 30 +++++++++++++++++++ .../testutils/MockWebServerExtension.java | 14 ++++++++- 6 files changed, 80 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/sonarsource/sonarlint/ls/SonarLintExtendedLanguageServer.java b/src/main/java/org/sonarsource/sonarlint/ls/SonarLintExtendedLanguageServer.java index 16b55706f..0e6aaf5f5 100644 --- a/src/main/java/org/sonarsource/sonarlint/ls/SonarLintExtendedLanguageServer.java +++ b/src/main/java/org/sonarsource/sonarlint/ls/SonarLintExtendedLanguageServer.java @@ -36,6 +36,7 @@ import org.sonarsource.sonarlint.core.rpc.protocol.backend.binding.GetSharedConnectedModeConfigFileParams; import org.sonarsource.sonarlint.core.rpc.protocol.backend.binding.GetSharedConnectedModeConfigFileResponse; import org.sonarsource.sonarlint.core.rpc.protocol.backend.connection.auth.HelpGenerateUserTokenResponse; +import org.sonarsource.sonarlint.core.rpc.protocol.backend.connection.org.OrganizationDto; import org.sonarsource.sonarlint.core.rpc.protocol.client.binding.GetBindingSuggestionsResponse; public interface SonarLintExtendedLanguageServer extends LanguageServer { @@ -705,4 +706,7 @@ enum BindingCreationMode { @JsonNotification("sonarlint/didCreateBinding") void didCreateBinding(BindingCreationMode creationMode); + @JsonRequest("sonarlint/listUserOrganizations") + CompletableFuture> listUserOrganizations(String token); + } diff --git a/src/main/java/org/sonarsource/sonarlint/ls/SonarLintLanguageServer.java b/src/main/java/org/sonarsource/sonarlint/ls/SonarLintLanguageServer.java index 07cd7e68d..fcb898732 100644 --- a/src/main/java/org/sonarsource/sonarlint/ls/SonarLintLanguageServer.java +++ b/src/main/java/org/sonarsource/sonarlint/ls/SonarLintLanguageServer.java @@ -95,6 +95,8 @@ import org.sonarsource.sonarlint.core.rpc.protocol.backend.binding.GetSharedConnectedModeConfigFileParams; import org.sonarsource.sonarlint.core.rpc.protocol.backend.binding.GetSharedConnectedModeConfigFileResponse; import org.sonarsource.sonarlint.core.rpc.protocol.backend.connection.auth.HelpGenerateUserTokenResponse; +import org.sonarsource.sonarlint.core.rpc.protocol.backend.connection.org.ListUserOrganizationsResponse; +import org.sonarsource.sonarlint.core.rpc.protocol.backend.connection.org.OrganizationDto; import org.sonarsource.sonarlint.core.rpc.protocol.backend.connection.validate.ValidateConnectionParams; import org.sonarsource.sonarlint.core.rpc.protocol.backend.hotspot.CheckStatusChangePermittedParams; import org.sonarsource.sonarlint.core.rpc.protocol.backend.hotspot.HotspotStatus; @@ -837,6 +839,12 @@ public void didCreateBinding(BindingCreationMode creationMode) { } } + @Override + public CompletableFuture> listUserOrganizations(String token) { + return backendServiceFacade.getBackendService().listUserOrganizations(token) + .thenApply(ListUserOrganizationsResponse::getUserOrganizations); + } + @Override public CompletableFuture checkIssueStatusChangePermitted(CheckIssueStatusChangePermittedParams params) { var bindingWrapperOpt = bindingManager.getBinding(create(params.getFolderUri())); diff --git a/src/main/java/org/sonarsource/sonarlint/ls/backend/BackendService.java b/src/main/java/org/sonarsource/sonarlint/ls/backend/BackendService.java index 57cbaa447..b133f92be 100644 --- a/src/main/java/org/sonarsource/sonarlint/ls/backend/BackendService.java +++ b/src/main/java/org/sonarsource/sonarlint/ls/backend/BackendService.java @@ -57,6 +57,8 @@ import org.sonarsource.sonarlint.core.rpc.protocol.backend.connection.config.DidUpdateConnectionsParams; import org.sonarsource.sonarlint.core.rpc.protocol.backend.connection.config.SonarCloudConnectionConfigurationDto; import org.sonarsource.sonarlint.core.rpc.protocol.backend.connection.config.SonarQubeConnectionConfigurationDto; +import org.sonarsource.sonarlint.core.rpc.protocol.backend.connection.org.ListUserOrganizationsParams; +import org.sonarsource.sonarlint.core.rpc.protocol.backend.connection.org.ListUserOrganizationsResponse; import org.sonarsource.sonarlint.core.rpc.protocol.backend.connection.projects.GetAllProjectsParams; import org.sonarsource.sonarlint.core.rpc.protocol.backend.connection.projects.GetAllProjectsResponse; import org.sonarsource.sonarlint.core.rpc.protocol.backend.connection.projects.GetProjectNamesByKeyParams; @@ -91,6 +93,7 @@ import org.sonarsource.sonarlint.core.rpc.protocol.client.binding.GetBindingSuggestionsResponse; import org.sonarsource.sonarlint.core.rpc.protocol.common.ClientFileDto; import org.sonarsource.sonarlint.core.rpc.protocol.common.Either; +import org.sonarsource.sonarlint.core.rpc.protocol.common.TokenDto; import org.sonarsource.sonarlint.ls.SonarLintExtendedLanguageClient; import org.sonarsource.sonarlint.ls.SonarLintExtendedLanguageServer; import org.sonarsource.sonarlint.ls.connected.ProjectBinding; @@ -362,4 +365,9 @@ public CompletableFuture analyzeFilesAndTrack(String confi shouldFetchServerIssues, System.currentTimeMillis()); return backend.getAnalysisService().analyzeFilesAndTrack(params); } + + public CompletableFuture listUserOrganizations(String token) { + var params = new ListUserOrganizationsParams(Either.forLeft(new TokenDto(token))); + return initializedBackend().getConnectionService().listUserOrganizations(params); + } } diff --git a/src/test/java/org/sonarsource/sonarlint/ls/backend/BackendServiceTests.java b/src/test/java/org/sonarsource/sonarlint/ls/backend/BackendServiceTests.java index 233c575d7..22387913a 100644 --- a/src/test/java/org/sonarsource/sonarlint/ls/backend/BackendServiceTests.java +++ b/src/test/java/org/sonarsource/sonarlint/ls/backend/BackendServiceTests.java @@ -25,11 +25,14 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; import org.sonarsource.sonarlint.core.rpc.protocol.SonarLintRpcServer; import org.sonarsource.sonarlint.core.rpc.protocol.backend.binding.BindingRpcService; import org.sonarsource.sonarlint.core.rpc.protocol.backend.binding.GetSharedConnectedModeConfigFileParams; import org.sonarsource.sonarlint.core.rpc.protocol.backend.config.ConfigurationRpcService; import org.sonarsource.sonarlint.core.rpc.protocol.backend.config.binding.DidUpdateBindingParams; +import org.sonarsource.sonarlint.core.rpc.protocol.backend.connection.ConnectionRpcService; +import org.sonarsource.sonarlint.core.rpc.protocol.backend.connection.org.ListUserOrganizationsParams; import org.sonarsource.sonarlint.core.rpc.protocol.backend.hotspot.HotspotRpcService; import org.sonarsource.sonarlint.core.rpc.protocol.backend.hotspot.OpenHotspotInBrowserParams; import org.sonarsource.sonarlint.ls.SonarLintExtendedLanguageClient; @@ -48,6 +51,7 @@ class BackendServiceTests { HotspotRpcService hotspotService = mock(HotspotRpcService.class); ConfigurationRpcService configurationService = mock(ConfigurationRpcService.class); BindingRpcService bindingRpcService = mock(BindingRpcService.class); + ConnectionRpcService connectionRpcService = mock(ConnectionRpcService.class); static LanguageClientLogger lsLogOutput = mock(LanguageClientLogger.class); static SonarLintExtendedLanguageClient client = mock(SonarLintExtendedLanguageClient.class); static BackendService underTest = new BackendService(backend, lsLogOutput, client); @@ -63,6 +67,7 @@ void init() { when(backend.getHotspotService()).thenReturn(hotspotService); when(backend.getConfigurationService()).thenReturn(configurationService); when(backend.getBindingService()).thenReturn(bindingRpcService); + when(backend.getConnectionService()).thenReturn(connectionRpcService); } @Test @@ -114,4 +119,16 @@ void getSharedConnectedModeFileContents() { verify(bindingRpcService).getSharedConnectedModeConfigFileContents(params); } + @Test + void shouldListUserOrganizations() { + var argumentCaptor = ArgumentCaptor.forClass(ListUserOrganizationsParams.class); + var token = "token"; + underTest.listUserOrganizations(token); + + verify(connectionRpcService).listUserOrganizations(argumentCaptor.capture()); + + assertThat(argumentCaptor.getValue().getCredentials().isLeft()).isTrue(); + assertThat(argumentCaptor.getValue().getCredentials().getLeft().getToken()).isEqualTo(token); + } + } diff --git a/src/test/java/org/sonarsource/sonarlint/ls/mediumtests/LanguageServerWithFoldersMediumTests.java b/src/test/java/org/sonarsource/sonarlint/ls/mediumtests/LanguageServerWithFoldersMediumTests.java index fb92f9473..f7efd0c5c 100644 --- a/src/test/java/org/sonarsource/sonarlint/ls/mediumtests/LanguageServerWithFoldersMediumTests.java +++ b/src/test/java/org/sonarsource/sonarlint/ls/mediumtests/LanguageServerWithFoldersMediumTests.java @@ -35,6 +35,10 @@ import org.eclipse.lsp4j.WorkspaceFoldersChangeEvent; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.sonarsource.sonarlint.core.serverapi.proto.sonarcloud.ws.Organizations; +import org.sonarsource.sonarlint.core.serverapi.proto.sonarqube.ws.Common; +import testutils.MockWebServerExtension; import static java.util.concurrent.TimeUnit.SECONDS; import static org.assertj.core.api.Assertions.assertThat; @@ -49,9 +53,13 @@ class LanguageServerWithFoldersMediumTests extends AbstractLanguageServerMediumT private static Path folder1BaseDir; private static Path folder2BaseDir; + @RegisterExtension + private static final MockWebServerExtension sonarCloudWebServer = new MockWebServerExtension(40000); @BeforeAll public static void initialize() throws Exception { + System.setProperty("sonarlint.internal.sonarcloud.url", "http://localhost:40000"); + System.setProperty("sonarlint.internal.sonarcloud.websocket.url", "http://localhost:40000"); folder1BaseDir = makeStaticTempDir(); folder2BaseDir = makeStaticTempDir(); initialize(Map.of( @@ -205,4 +213,26 @@ void shouldOpenRuleDescFromCodeAction() throws Exception { assertThat(client.ruleDesc.getSeverity()).isEqualTo("MINOR"); } + @Test + void list_user_organizations() { + var paging = Common.Paging.newBuilder() + .setPageSize(500) + .setTotal(1) + .setPageIndex(1) + .build(); + var organization = Organizations.Organization.newBuilder() + .setKey("key") + .setName("name") + .build(); + sonarCloudWebServer.addProtobufResponse("/api/organizations/search.protobuf?member=true&ps=500&p=1", + Organizations.SearchWsResponse.newBuilder() + .addAllOrganizations(List.of(organization)) + .setPaging(paging) + .build()); + var token = "123456"; + var result = lsProxy.listUserOrganizations(token).join(); + assertThat(result).hasSize(1); + assertThat(result.get(0).getKey()).isEqualTo("key"); + } + } diff --git a/src/test/java/testutils/MockWebServerExtension.java b/src/test/java/testutils/MockWebServerExtension.java index 39f9e966d..b50a58288 100644 --- a/src/test/java/testutils/MockWebServerExtension.java +++ b/src/test/java/testutils/MockWebServerExtension.java @@ -40,12 +40,20 @@ public class MockWebServerExtension implements BeforeEachCallback, AfterEachCallback { + private final Integer port; private MockWebServer server; protected final Map responsesByPath = new HashMap<>(); public MockWebServerExtension() { this.server = new MockWebServer(); + this.port = null; } + + public MockWebServerExtension(int port) { + this.server = new MockWebServer(); + this.port = port; + } + @Override public void beforeEach(ExtensionContext context) throws Exception { server = new MockWebServer(); @@ -60,7 +68,11 @@ public MockResponse dispatch(RecordedRequest request) { } }; server.setDispatcher(dispatcher); - server.start(); + if (this.port != null) { + server.start(this.port); + } else { + server.start(); + } } @Override