From 8f2f04e1273a60415bf9c126a90030054d3ab1a2 Mon Sep 17 00:00:00 2001 From: Serhat Yenican Date: Fri, 24 May 2024 11:27:58 +0200 Subject: [PATCH] SLCORE-818 Provide connection parameters for SC while opening an issue in IDE --- API_CHANGES.md | 18 +++ .../RequestHandlerBindingAssistant.java | 45 +++++--- .../server/ShowHotspotRequestHandler.java | 7 +- .../server/ShowIssueRequestHandler.java | 57 ++++++++-- .../server/ShowIssueRequestHandlerTests.java | 103 ++++++++++++++---- .../issues/OpenIssueInIdeMediumTests.java | 90 +++++++++++++-- ...arCloudConnectionParamsAdapterFactory.java | 37 +++++++ .../AssistCreatingConnectionParams.java | 47 +++++--- .../SonarCloudConnectionParams.java | 49 +++++++++ .../connection/SonarQubeConnectionParams.java | 49 +++++++++ 10 files changed, 426 insertions(+), 76 deletions(-) create mode 100644 rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/adapter/EitherSonarQubeSonarCloudConnectionParamsAdapterFactory.java create mode 100644 rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/client/connection/SonarCloudConnectionParams.java create mode 100644 rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/client/connection/SonarQubeConnectionParams.java diff --git a/API_CHANGES.md b/API_CHANGES.md index 74ffadfbb6..5314b27ca9 100644 --- a/API_CHANGES.md +++ b/API_CHANGES.md @@ -1,3 +1,21 @@ +# 10.3 + +## Breaking changes + +## New features + +### Open Issue in IDE + +* Add the `getConnectionParams` method to `org.sonarsource.sonarlint.core.rpc.protocol.client.connection.AssistCreatingConnectionParams` + * It allows clients to get parameters to create either SonarQube or SonarCloud connection + * This field type is `Either` + * Common methods of both connection types are added to the `AssistCreatingConnectionParams` class to provide users simplicity + +## Deprecation + +* `org.sonarsource.sonarlint.core.rpc.protocol.client.connection.AssistCreatingConnectionParams.getServerUrl` is only meaningful for SQ + connections. Use `getConnection().getLeft().getServerUrl()` instead to get the `serverUrl` of a SQ connection + # 10.2 ## Breaking changes diff --git a/backend/core/src/main/java/org/sonarsource/sonarlint/core/embedded/server/RequestHandlerBindingAssistant.java b/backend/core/src/main/java/org/sonarsource/sonarlint/core/embedded/server/RequestHandlerBindingAssistant.java index dc726f3dda..cb10ea4055 100644 --- a/backend/core/src/main/java/org/sonarsource/sonarlint/core/embedded/server/RequestHandlerBindingAssistant.java +++ b/backend/core/src/main/java/org/sonarsource/sonarlint/core/embedded/server/RequestHandlerBindingAssistant.java @@ -31,6 +31,7 @@ import javax.inject.Singleton; import org.sonarsource.sonarlint.core.BindingCandidatesFinder; import org.sonarsource.sonarlint.core.BindingSuggestionProvider; +import org.sonarsource.sonarlint.core.SonarCloudActiveEnvironment; import org.sonarsource.sonarlint.core.commons.log.SonarLintLogger; import org.sonarsource.sonarlint.core.commons.progress.ExecutorServiceShutdownWatchable; import org.sonarsource.sonarlint.core.commons.progress.SonarLintCancelMonitor; @@ -56,10 +57,11 @@ public class RequestHandlerBindingAssistant { private final ConfigurationRepository configurationRepository; private final UserTokenService userTokenService; private final ExecutorServiceShutdownWatchable executorService; + private final String sonarCloudUrl; - public RequestHandlerBindingAssistant(BindingSuggestionProvider bindingSuggestionProvider, BindingCandidatesFinder bindingCandidatesFinder, SonarLintRpcClient client, - ConnectionConfigurationRepository connectionConfigurationRepository, - ConfigurationRepository configurationRepository, UserTokenService userTokenService) { + public RequestHandlerBindingAssistant(BindingSuggestionProvider bindingSuggestionProvider, BindingCandidatesFinder bindingCandidatesFinder, + SonarLintRpcClient client, ConnectionConfigurationRepository connectionConfigurationRepository, ConfigurationRepository configurationRepository, + UserTokenService userTokenService, SonarCloudActiveEnvironment sonarCloudActiveEnvironment) { this.bindingSuggestionProvider = bindingSuggestionProvider; this.bindingCandidatesFinder = bindingCandidatesFinder; this.client = client; @@ -68,27 +70,29 @@ public RequestHandlerBindingAssistant(BindingSuggestionProvider bindingSuggestio this.userTokenService = userTokenService; this.executorService = new ExecutorServiceShutdownWatchable<>(new ThreadPoolExecutor(0, 1, 10L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(), r -> new Thread(r, "Show Issue or Hotspot Request Handler"))); + this.sonarCloudUrl = sonarCloudActiveEnvironment.getUri().toString(); } interface Callback { void andThen(String connectionId, @Nullable String configurationScopeId, SonarLintCancelMonitor cancelMonitor); } - void assistConnectionAndBindingIfNeededAsync(String serverUrl, @Nullable String tokenName, @Nullable String tokenValue, String projectKey, Callback callback) { + void assistConnectionAndBindingIfNeededAsync(AssistCreatingConnectionParams connectionParams, String projectKey, Callback callback) { var cancelMonitor = new SonarLintCancelMonitor(); cancelMonitor.watchForShutdown(executorService); - executorService.submit(() -> assistConnectionAndBindingIfNeeded(serverUrl, tokenName, tokenValue, projectKey, callback, cancelMonitor)); + executorService.submit(() -> assistConnectionAndBindingIfNeeded(connectionParams, projectKey, callback, cancelMonitor)); } - private void assistConnectionAndBindingIfNeeded(String serverUrl, @Nullable String tokenName, @Nullable String tokenValue, String projectKey, + private void assistConnectionAndBindingIfNeeded(AssistCreatingConnectionParams connectionParams, String projectKey, Callback callback, SonarLintCancelMonitor cancelMonitor) { + String serverUrl = getServerUrl(connectionParams); LOG.debug("Assist connection and binding if needed for project {} and server {}", projectKey, serverUrl); try { var connectionsMatchingOrigin = connectionConfigurationRepository.findByUrl(serverUrl); if (connectionsMatchingOrigin.isEmpty()) { startFullBindingProcess(); try { - var assistNewConnectionResult = assistCreatingConnectionAndWaitForRepositoryUpdate(serverUrl, tokenName, tokenValue, cancelMonitor); + var assistNewConnectionResult = assistCreatingConnectionAndWaitForRepositoryUpdate(connectionParams, cancelMonitor); var assistNewBindingResult = assistBindingAndWaitForRepositoryUpdate(assistNewConnectionResult.getNewConnectionId(), projectKey, cancelMonitor); callback.andThen(assistNewConnectionResult.getNewConnectionId(), assistNewBindingResult.getConfigurationScopeId(), cancelMonitor); @@ -105,9 +109,13 @@ private void assistConnectionAndBindingIfNeeded(String serverUrl, @Nullable Stri } } - private AssistCreatingConnectionResponse assistCreatingConnectionAndWaitForRepositoryUpdate(String serverUrl, @Nullable String tokenName, @Nullable String tokenValue, - SonarLintCancelMonitor cancelMonitor) { - var assistNewConnectionResult = assistCreatingConnection(serverUrl, tokenName, tokenValue, cancelMonitor); + private String getServerUrl(AssistCreatingConnectionParams connectionParams) { + return connectionParams.getConnectionParams().isLeft() ? connectionParams.getConnectionParams().getLeft().getServerUrl() : sonarCloudUrl; + } + + private AssistCreatingConnectionResponse assistCreatingConnectionAndWaitForRepositoryUpdate( + AssistCreatingConnectionParams connectionParams, SonarLintCancelMonitor cancelMonitor) { + var assistNewConnectionResult = assistCreatingConnection(connectionParams, cancelMonitor); // Wait 5s for the connection to be created in the repository. This is happening asynchronously by the // ConnectionService::didUpdateConnections event @@ -179,19 +187,26 @@ void endFullBindingProcess() { bindingSuggestionProvider.enable(); } - AssistCreatingConnectionResponse assistCreatingConnection(String serverUrl, @Nullable String tokenName, @Nullable String tokenValue, SonarLintCancelMonitor cancelMonitor) { + AssistCreatingConnectionResponse assistCreatingConnection(AssistCreatingConnectionParams connectionParams, SonarLintCancelMonitor cancelMonitor) { try { - var future = client.assistCreatingConnection(new AssistCreatingConnectionParams(serverUrl, tokenName, tokenValue)); + var future = client.assistCreatingConnection(connectionParams); cancelMonitor.onCancel(() -> future.cancel(true)); return future.join(); } catch (Exception e) { - if (tokenName != null && tokenValue != null) { - userTokenService.revokeToken(new RevokeTokenParams(serverUrl, tokenName, tokenValue), cancelMonitor); - } + revokeToken(connectionParams, cancelMonitor); throw e; } } + private void revokeToken(AssistCreatingConnectionParams connectionParams, SonarLintCancelMonitor cancelMonitor) { + String tokenName = connectionParams.getTokenName(); + String tokenValue = connectionParams.getTokenValue(); + if (tokenName != null && tokenValue != null) { + var revokeTokenParams = new RevokeTokenParams(getServerUrl(connectionParams), tokenName, tokenValue); + userTokenService.revokeToken(revokeTokenParams, cancelMonitor); + } + } + NewBinding assistBinding(String connectionId, String projectKey, SonarLintCancelMonitor cancelMonitor) { var configScopeCandidates = bindingCandidatesFinder.findConfigScopesToBind(connectionId, projectKey, cancelMonitor); // For now, we decided to only support automatic binding if there is only one clear candidate diff --git a/backend/core/src/main/java/org/sonarsource/sonarlint/core/embedded/server/ShowHotspotRequestHandler.java b/backend/core/src/main/java/org/sonarsource/sonarlint/core/embedded/server/ShowHotspotRequestHandler.java index 574bcdf0f1..c9e937ab43 100644 --- a/backend/core/src/main/java/org/sonarsource/sonarlint/core/embedded/server/ShowHotspotRequestHandler.java +++ b/backend/core/src/main/java/org/sonarsource/sonarlint/core/embedded/server/ShowHotspotRequestHandler.java @@ -41,6 +41,8 @@ import org.sonarsource.sonarlint.core.file.FilePathTranslation; import org.sonarsource.sonarlint.core.file.PathTranslationService; import org.sonarsource.sonarlint.core.rpc.protocol.SonarLintRpcClient; +import org.sonarsource.sonarlint.core.rpc.protocol.client.connection.AssistCreatingConnectionParams; +import org.sonarsource.sonarlint.core.rpc.protocol.client.connection.SonarQubeConnectionParams; import org.sonarsource.sonarlint.core.rpc.protocol.client.hotspot.HotspotDetailsDto; import org.sonarsource.sonarlint.core.rpc.protocol.client.hotspot.ShowHotspotParams; import org.sonarsource.sonarlint.core.rpc.protocol.client.message.MessageType; @@ -77,8 +79,9 @@ public void handle(ClassicHttpRequest request, ClassicHttpResponse response, Htt return; } telemetryService.showHotspotRequestReceived(); - - requestHandlerBindingAssistant.assistConnectionAndBindingIfNeededAsync(showHotspotQuery.serverUrl, null, null, showHotspotQuery.projectKey, + var sonarQubeConnectionParams = new SonarQubeConnectionParams(showHotspotQuery.serverUrl, null, null); + var connectionParams = new AssistCreatingConnectionParams(sonarQubeConnectionParams); + requestHandlerBindingAssistant.assistConnectionAndBindingIfNeededAsync(connectionParams, showHotspotQuery.projectKey, (connectionId, configScopeId, cancelMonitor) -> { if (configScopeId != null) { showHotspotForScope(connectionId, configScopeId, showHotspotQuery.hotspotKey, cancelMonitor); diff --git a/backend/core/src/main/java/org/sonarsource/sonarlint/core/embedded/server/ShowIssueRequestHandler.java b/backend/core/src/main/java/org/sonarsource/sonarlint/core/embedded/server/ShowIssueRequestHandler.java index aede0bed39..ba0e4d12dd 100644 --- a/backend/core/src/main/java/org/sonarsource/sonarlint/core/embedded/server/ShowIssueRequestHandler.java +++ b/backend/core/src/main/java/org/sonarsource/sonarlint/core/embedded/server/ShowIssueRequestHandler.java @@ -35,15 +35,21 @@ import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.Method; +import org.apache.hc.core5.http.NameValuePair; +import org.apache.hc.core5.http.ProtocolException; import org.apache.hc.core5.http.io.HttpRequestHandler; import org.apache.hc.core5.http.io.entity.StringEntity; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.net.URIBuilder; import org.sonarsource.sonarlint.core.ServerApiProvider; +import org.sonarsource.sonarlint.core.SonarCloudActiveEnvironment; import org.sonarsource.sonarlint.core.commons.progress.SonarLintCancelMonitor; import org.sonarsource.sonarlint.core.file.FilePathTranslation; import org.sonarsource.sonarlint.core.file.PathTranslationService; import org.sonarsource.sonarlint.core.rpc.protocol.SonarLintRpcClient; +import org.sonarsource.sonarlint.core.rpc.protocol.client.connection.AssistCreatingConnectionParams; +import org.sonarsource.sonarlint.core.rpc.protocol.client.connection.SonarCloudConnectionParams; +import org.sonarsource.sonarlint.core.rpc.protocol.client.connection.SonarQubeConnectionParams; import org.sonarsource.sonarlint.core.rpc.protocol.client.issue.IssueDetailsDto; import org.sonarsource.sonarlint.core.rpc.protocol.client.issue.ShowIssueParams; import org.sonarsource.sonarlint.core.rpc.protocol.client.message.MessageType; @@ -69,14 +75,16 @@ public class ShowIssueRequestHandler implements HttpRequestHandler { private final TelemetryService telemetryService; private final RequestHandlerBindingAssistant requestHandlerBindingAssistant; private final PathTranslationService pathTranslationService; + private final String sonarCloudUrl; public ShowIssueRequestHandler(SonarLintRpcClient client, ServerApiProvider serverApiProvider, TelemetryService telemetryService, - RequestHandlerBindingAssistant requestHandlerBindingAssistant, PathTranslationService pathTranslationService) { + RequestHandlerBindingAssistant requestHandlerBindingAssistant, PathTranslationService pathTranslationService, SonarCloudActiveEnvironment sonarCloudActiveEnvironment) { this.client = client; this.serverApiProvider = serverApiProvider; this.telemetryService = telemetryService; this.requestHandlerBindingAssistant = requestHandlerBindingAssistant; this.pathTranslationService = pathTranslationService; + this.sonarCloudUrl = sonarCloudActiveEnvironment.getUri().toString(); } @Override @@ -88,7 +96,11 @@ public void handle(ClassicHttpRequest request, ClassicHttpResponse response, Htt } telemetryService.showIssueRequestReceived(); - requestHandlerBindingAssistant.assistConnectionAndBindingIfNeededAsync(showIssueQuery.serverUrl, showIssueQuery.tokenName, showIssueQuery.tokenValue, showIssueQuery.projectKey, + AssistCreatingConnectionParams serverConnectionParams = createAssistServerConnectionParams(showIssueQuery); + + requestHandlerBindingAssistant.assistConnectionAndBindingIfNeededAsync( + serverConnectionParams, + showIssueQuery.projectKey, (connectionId, configScopeId, cancelMonitor) -> { if (configScopeId != null) { showIssueForScope(connectionId, configScopeId, showIssueQuery.issueKey, showIssueQuery.projectKey, showIssueQuery.branch, @@ -100,6 +112,21 @@ public void handle(ClassicHttpRequest request, ClassicHttpResponse response, Htt response.setEntity(new StringEntity("OK")); } + private static AssistCreatingConnectionParams createAssistServerConnectionParams(ShowIssueQuery query) { + String tokenName = query.getTokenName(); + String tokenValue = query.getTokenValue(); + return query.isSonarCloud ? + new AssistCreatingConnectionParams(new SonarCloudConnectionParams(query.getOrganizationKey(), tokenName, tokenValue)) + : new AssistCreatingConnectionParams(new SonarQubeConnectionParams(query.getServerUrl(), tokenName, tokenValue)); + } + + private boolean isSonarCloud(ClassicHttpRequest request) throws ProtocolException { + return Optional.ofNullable(request.getHeader("Origin")) + .map(NameValuePair::getValue) + .map(sonarCloudUrl::equals) + .orElse(false); + } + private void showIssueForScope(String connectionId, String configScopeId, String issueKey, String projectKey, String branch, @Nullable String pullRequest, SonarLintCancelMonitor cancelMonitor) { var issueDetailsOpt = tryFetchIssue(connectionId, issueKey, projectKey, branch, pullRequest, cancelMonitor); @@ -159,7 +186,7 @@ private Optional tryFetchCodeSnippet(String connectionId, String fileKey } @VisibleForTesting - static ShowIssueQuery extractQuery(ClassicHttpRequest request) { + ShowIssueQuery extractQuery(ClassicHttpRequest request) throws ProtocolException { var params = new HashMap(); try { new URIBuilder(request.getUri(), StandardCharsets.UTF_8) @@ -168,8 +195,10 @@ static ShowIssueQuery extractQuery(ClassicHttpRequest request) { } catch (URISyntaxException e) { // Ignored } - return new ShowIssueQuery(params.get("server"), params.get("project"), params.get("issue"), params.get("branch"), - params.get("pullRequest"), params.get("tokenName"), params.get("tokenValue")); + boolean isSonarCloud = isSonarCloud(request); + String serverUrl = isSonarCloud ? sonarCloudUrl : params.get("server"); + return new ShowIssueQuery(serverUrl, params.get("project"), params.get("issue"), params.get("branch"), + params.get("pullRequest"), params.get("tokenName"), params.get("tokenValue"), params.get("organizationKey"), isSonarCloud); } @VisibleForTesting @@ -184,9 +213,12 @@ public static class ShowIssueQuery { private final String tokenName; @Nullable private final String tokenValue; + @Nullable + private final String organizationKey; + private final boolean isSonarCloud; - public ShowIssueQuery(String serverUrl, String projectKey, String issueKey, String branch, - @Nullable String pullRequest, @Nullable String tokenName, @Nullable String tokenValue) { + public ShowIssueQuery(String serverUrl, String projectKey, String issueKey, String branch, @Nullable String pullRequest, + @Nullable String tokenName, @Nullable String tokenValue, @Nullable String organizationKey, boolean isSonarCloud) { this.serverUrl = serverUrl; this.projectKey = projectKey; this.issueKey = issueKey; @@ -194,10 +226,14 @@ public ShowIssueQuery(String serverUrl, String projectKey, String issueKey, Stri this.pullRequest = pullRequest; this.tokenName = tokenName; this.tokenValue = tokenValue; + this.organizationKey = organizationKey; + this.isSonarCloud = isSonarCloud; } public boolean isValid() { - return isNotBlank(serverUrl) && isNotBlank(projectKey) && isNotBlank(issueKey) && isNotBlank(branch) + return isNotBlank(projectKey) && isNotBlank(issueKey) && isNotBlank(branch) + && (isSonarCloud || isNotBlank(serverUrl)) + && (!isSonarCloud || isNotBlank(organizationKey)) && isPullRequestParamValid() && isTokenValid(); } @@ -227,6 +263,11 @@ public String getProjectKey() { return projectKey; } + @Nullable + public String getOrganizationKey() { + return organizationKey; + } + public String getIssueKey() { return issueKey; } diff --git a/backend/core/src/test/java/org/sonarsource/sonarlint/core/embedded/server/ShowIssueRequestHandlerTests.java b/backend/core/src/test/java/org/sonarsource/sonarlint/core/embedded/server/ShowIssueRequestHandlerTests.java index 3e0ca3dc4f..537d840bd6 100644 --- a/backend/core/src/test/java/org/sonarsource/sonarlint/core/embedded/server/ShowIssueRequestHandlerTests.java +++ b/backend/core/src/test/java/org/sonarsource/sonarlint/core/embedded/server/ShowIssueRequestHandlerTests.java @@ -30,12 +30,14 @@ import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.Method; +import org.apache.hc.core5.http.ProtocolException; import org.apache.hc.core5.http.message.BasicClassicHttpRequest; import org.apache.hc.core5.http.protocol.HttpContext; import org.junit.jupiter.api.Test; import org.sonarsource.sonarlint.core.BindingCandidatesFinder; import org.sonarsource.sonarlint.core.BindingSuggestionProvider; import org.sonarsource.sonarlint.core.ServerApiProvider; +import org.sonarsource.sonarlint.core.SonarCloudActiveEnvironment; import org.sonarsource.sonarlint.core.commons.progress.SonarLintCancelMonitor; import org.sonarsource.sonarlint.core.file.FilePathTranslation; import org.sonarsource.sonarlint.core.file.PathTranslationService; @@ -148,44 +150,98 @@ void should_trigger_telemetry() throws HttpException, IOException, URISyntaxExce } @Test - void should_extract_query_from_request_without_token() { - var issueQuery = ShowIssueRequestHandler.extractQuery(new BasicClassicHttpRequest("GET", "/sonarlint/api/issues/show?server=" + - "https%3A%2F%2Fnext.sonarqube.com%2Fsonarqube&project=org.sonarsource.sonarlint" + - ".core%3Asonarlint-core-parent&issue=AX2VL6pgAvx3iwyNtLyr")); + void should_extract_query_from_sq_request_without_token() throws ProtocolException { + SonarCloudActiveEnvironment sonarCloudActiveEnvironment = SonarCloudActiveEnvironment.prod(); + ShowIssueRequestHandler showIssueRequestHandler = new ShowIssueRequestHandler(null, null, null, null, null, sonarCloudActiveEnvironment); + var issueQuery = showIssueRequestHandler.extractQuery(new BasicClassicHttpRequest("GET", "/sonarlint/api/issues/show" + + "?server=https%3A%2F%2Fnext.sonarqube.com%2Fsonarqube" + + "&project=org.sonarsource.sonarlint.core%3Asonarlint-core-parent" + + "&issue=AX2VL6pgAvx3iwyNtLyr" + + "&organizationKey=sample-organization")); assertThat(issueQuery.getServerUrl()).isEqualTo("https://next.sonarqube.com/sonarqube"); assertThat(issueQuery.getProjectKey()).isEqualTo("org.sonarsource.sonarlint.core:sonarlint-core-parent"); assertThat(issueQuery.getIssueKey()).isEqualTo("AX2VL6pgAvx3iwyNtLyr"); + assertThat(issueQuery.getOrganizationKey()).isEqualTo("sample-organization"); assertThat(issueQuery.getTokenName()).isNull(); assertThat(issueQuery.getTokenValue()).isNull(); } @Test - void should_extract_query_from_request_with_token() { - var issueQuery = ShowIssueRequestHandler.extractQuery(new BasicClassicHttpRequest("GET", "/sonarlint/api/issues/show?server=" + - "https%3A%2F%2Fnext.sonarqube.com%2Fsonarqube&project=org.sonarsource.sonarlint" + - ".core%3Asonarlint-core-parent&issue=AX2VL6pgAvx3iwyNtLyr&tokenName=abc&tokenValue=123")); + void should_extract_query_from_sq_request_with_token() throws ProtocolException { + SonarCloudActiveEnvironment sonarCloudActiveEnvironment = SonarCloudActiveEnvironment.prod(); + ShowIssueRequestHandler showIssueRequestHandler = new ShowIssueRequestHandler(null, null, null, null, null, sonarCloudActiveEnvironment); + var issueQuery = showIssueRequestHandler.extractQuery(new BasicClassicHttpRequest("GET", "/sonarlint/api/issues/show" + + "?server=https%3A%2F%2Fnext.sonarqube.com%2Fsonarqube" + + "&project=org.sonarsource.sonarlint.core%3Asonarlint-core-parent" + + "&issue=AX2VL6pgAvx3iwyNtLyr&tokenName=abc" + + "&organizationKey=sample-organization" + + "&tokenValue=123")); assertThat(issueQuery.getServerUrl()).isEqualTo("https://next.sonarqube.com/sonarqube"); assertThat(issueQuery.getProjectKey()).isEqualTo("org.sonarsource.sonarlint.core:sonarlint-core-parent"); assertThat(issueQuery.getIssueKey()).isEqualTo("AX2VL6pgAvx3iwyNtLyr"); assertThat(issueQuery.getTokenName()).isEqualTo("abc"); + assertThat(issueQuery.getOrganizationKey()).isEqualTo("sample-organization"); assertThat(issueQuery.getTokenValue()).isEqualTo("123"); } @Test - void should_validate_issue_query() { - assertThat(new ShowIssueRequestHandler.ShowIssueQuery("serverUrl", "project", "issue", "branch", "pullRequest", null, null).isValid()).isTrue(); - - assertThat(new ShowIssueRequestHandler.ShowIssueQuery("", "project", "issue", "branch", "pullRequest", null, null).isValid()).isFalse(); - assertThat(new ShowIssueRequestHandler.ShowIssueQuery("serverUrl", "", "issue", "branch", "pullRequest", null, null).isValid()).isFalse(); - assertThat(new ShowIssueRequestHandler.ShowIssueQuery("serverUrl", "project", "", "branch", "pullRequest", null, null).isValid()).isFalse(); - assertThat(new ShowIssueRequestHandler.ShowIssueQuery("serverUrl", "project", "issue", "", "", null, null).isValid()).isFalse(); - assertThat(new ShowIssueRequestHandler.ShowIssueQuery("serverUrl", "project", "issue", "branch", null, null, null).isValid()).isTrue(); - - assertThat(new ShowIssueRequestHandler.ShowIssueQuery("serverUrl", "project", "issue", "branch", "pullRequest", "name", null).isValid()).isFalse(); - assertThat(new ShowIssueRequestHandler.ShowIssueQuery("serverUrl", "project", "issue", "branch", "pullRequest", null, "value").isValid()).isFalse(); - assertThat(new ShowIssueRequestHandler.ShowIssueQuery("serverUrl", "project", "issue", "branch", "pullRequest", "name", "").isValid()).isFalse(); - assertThat(new ShowIssueRequestHandler.ShowIssueQuery("serverUrl", "project", "issue", "branch", "pullRequest", "", "value").isValid()).isFalse(); - assertThat(new ShowIssueRequestHandler.ShowIssueQuery("serverUrl", "project", "issue", "branch", "pullRequest", "name", "value").isValid()).isTrue(); + void should_extract_query_from_sc_request_without_token() throws ProtocolException { + SonarCloudActiveEnvironment sonarCloudActiveEnvironment = SonarCloudActiveEnvironment.prod(); + ShowIssueRequestHandler showIssueRequestHandler = new ShowIssueRequestHandler(null, null, null, null, null, sonarCloudActiveEnvironment); + BasicClassicHttpRequest request = new BasicClassicHttpRequest("GET", "/sonarlint/api/issues/show" + + "?project=org.sonarsource.sonarlint.core%3Asonarlint-core-parent" + + "&issue=AX2VL6pgAvx3iwyNtLyr" + + "&organizationKey=sample-organization"); + request.addHeader("Origin", SonarCloudActiveEnvironment.PRODUCTION_URI); + var issueQuery = showIssueRequestHandler.extractQuery(request); + assertThat(issueQuery.getServerUrl()).isEqualTo("https://sonarcloud.io"); + assertThat(issueQuery.getProjectKey()).isEqualTo("org.sonarsource.sonarlint.core:sonarlint-core-parent"); + assertThat(issueQuery.getIssueKey()).isEqualTo("AX2VL6pgAvx3iwyNtLyr"); + assertThat(issueQuery.getOrganizationKey()).isEqualTo("sample-organization"); + assertThat(issueQuery.getTokenName()).isNull(); + assertThat(issueQuery.getTokenValue()).isNull(); + } + + @Test + void should_extract_query_from_sc_request_with_token() throws ProtocolException { + SonarCloudActiveEnvironment sonarCloudActiveEnvironment = SonarCloudActiveEnvironment.prod(); + ShowIssueRequestHandler showIssueRequestHandler = new ShowIssueRequestHandler(null, null, null, null, null, sonarCloudActiveEnvironment); + BasicClassicHttpRequest request = new BasicClassicHttpRequest("GET", "/sonarlint/api/issues/show" + + "?project=org.sonarsource.sonarlint.core%3Asonarlint-core-parent" + + "&issue=AX2VL6pgAvx3iwyNtLyr&tokenName=abc" + + "&organizationKey=sample-organization" + + "&tokenValue=123"); + request.addHeader("Origin", SonarCloudActiveEnvironment.PRODUCTION_URI); + var issueQuery = showIssueRequestHandler.extractQuery(request); + assertThat(issueQuery.getServerUrl()).isEqualTo("https://sonarcloud.io"); + assertThat(issueQuery.getProjectKey()).isEqualTo("org.sonarsource.sonarlint.core:sonarlint-core-parent"); + assertThat(issueQuery.getIssueKey()).isEqualTo("AX2VL6pgAvx3iwyNtLyr"); + assertThat(issueQuery.getTokenName()).isEqualTo("abc"); + assertThat(issueQuery.getOrganizationKey()).isEqualTo("sample-organization"); + assertThat(issueQuery.getTokenValue()).isEqualTo("123"); + } + + @Test + void should_validate_issue_query_for_sq() { + assertThat(new ShowIssueRequestHandler.ShowIssueQuery("serverUrl", "project", "issue", "branch", "pullRequest", null, null, null, false).isValid()).isTrue(); + + assertThat(new ShowIssueRequestHandler.ShowIssueQuery("", "project", "issue", "branch", "pullRequest", null, null, null, false).isValid()).isFalse(); + assertThat(new ShowIssueRequestHandler.ShowIssueQuery("serverUrl", "", "issue", "branch", "pullRequest", null, null, null, false).isValid()).isFalse(); + assertThat(new ShowIssueRequestHandler.ShowIssueQuery("serverUrl", "project", "", "branch", "pullRequest", null, null, null, false).isValid()).isFalse(); + assertThat(new ShowIssueRequestHandler.ShowIssueQuery("serverUrl", "project", "issue", "", "", null, null, null, false).isValid()).isFalse(); + assertThat(new ShowIssueRequestHandler.ShowIssueQuery("serverUrl", "project", "issue", "branch", null, null, null, null, false).isValid()).isTrue(); + + assertThat(new ShowIssueRequestHandler.ShowIssueQuery("serverUrl", "project", "issue", "branch", "pullRequest", "name", null, null, false).isValid()).isFalse(); + assertThat(new ShowIssueRequestHandler.ShowIssueQuery("serverUrl", "project", "issue", "branch", "pullRequest", null, "value", null, false).isValid()).isFalse(); + assertThat(new ShowIssueRequestHandler.ShowIssueQuery("serverUrl", "project", "issue", "branch", "pullRequest", "name", "", null, false).isValid()).isFalse(); + assertThat(new ShowIssueRequestHandler.ShowIssueQuery("serverUrl", "project", "issue", "branch", "pullRequest", "", "value", null, false).isValid()).isFalse(); + assertThat(new ShowIssueRequestHandler.ShowIssueQuery("serverUrl", "project", "issue", "branch", "pullRequest", "name", "value", null, false).isValid()).isTrue(); + } + + @Test + void should_validate_issue_query_for_sc() { + assertThat(new ShowIssueRequestHandler.ShowIssueQuery(null, "project", "issue", "branch", "pullRequest", "name", "value", "organizationKey", true).isValid()).isTrue(); + assertThat(new ShowIssueRequestHandler.ShowIssueQuery(null, "project", "issue", "branch", "pullRequest", "name", "value", null, true).isValid()).isFalse(); } @Test @@ -214,7 +270,8 @@ private static ShowIssueRequestHandler getShowIssueRequestHandler(TelemetryServi var sonarLintClient = mock(SonarLintRpcClient.class); var pathTranslationService = mock(PathTranslationService.class); var userTokenService = mock(UserTokenService.class); + SonarCloudActiveEnvironment sonarCloudActiveEnvironment = SonarCloudActiveEnvironment.prod(); return new ShowIssueRequestHandler(sonarLintClient, serverApiProvider, telemetryService, - new RequestHandlerBindingAssistant(bindingSuggestionProvider, bindingCandidatesFinder, sonarLintClient, repository, configurationRepository, userTokenService), pathTranslationService); + new RequestHandlerBindingAssistant(bindingSuggestionProvider, bindingCandidatesFinder, sonarLintClient, repository, configurationRepository, userTokenService, sonarCloudActiveEnvironment), pathTranslationService, sonarCloudActiveEnvironment); } } diff --git a/medium-tests/src/test/java/mediumtest/issues/OpenIssueInIdeMediumTests.java b/medium-tests/src/test/java/mediumtest/issues/OpenIssueInIdeMediumTests.java index c6a6571c2d..1713843f31 100644 --- a/medium-tests/src/test/java/mediumtest/issues/OpenIssueInIdeMediumTests.java +++ b/medium-tests/src/test/java/mediumtest/issues/OpenIssueInIdeMediumTests.java @@ -45,18 +45,23 @@ import org.sonarsource.sonarlint.core.rpc.protocol.backend.connection.config.DidUpdateConnectionsParams; import org.sonarsource.sonarlint.core.rpc.protocol.backend.connection.config.SonarQubeConnectionConfigurationDto; import org.sonarsource.sonarlint.core.rpc.protocol.client.binding.AssistBindingResponse; +import org.sonarsource.sonarlint.core.rpc.protocol.client.connection.AssistCreatingConnectionParams; import org.sonarsource.sonarlint.core.rpc.protocol.client.connection.AssistCreatingConnectionResponse; import org.sonarsource.sonarlint.core.rpc.protocol.client.issue.IssueDetailsDto; +import org.sonarsource.sonarlint.core.rpc.protocol.client.log.LogParams; import org.sonarsource.sonarlint.core.rpc.protocol.common.TextRangeDto; import static mediumtest.fixtures.ServerFixture.newSonarQubeServer; import static mediumtest.fixtures.SonarLintBackendFixture.newBackend; import static mediumtest.fixtures.SonarLintBackendFixture.newFakeClient; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.tuple; import static org.awaitility.Awaitility.await; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.after; import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.never; import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.verify; @@ -308,6 +313,63 @@ void it_should_assist_creating_the_connection_when_server_url_unknown() throws E verify(fakeClient, timeout(2000)).showIssue(eq(CONFIG_SCOPE_ID), any()); verify(fakeClient, never()).showMessage(any(), any()); + + ArgumentCaptor captor = ArgumentCaptor.captor(); + verify(fakeClient, timeout(1000)).assistCreatingConnection(captor.capture(), any()); + assertThat(captor.getAllValues()) + .extracting(connectionParams -> connectionParams.getConnectionParams().getLeft().getServerUrl(), + connectionParams -> connectionParams.getConnectionParams().getLeft() != null, + AssistCreatingConnectionParams::getTokenName, + AssistCreatingConnectionParams::getTokenValue) + .containsExactly(tuple(serverWithIssues.baseUrl(), true, null, null)); + } + + @Test + void it_should_assist_creating_the_connection_when_no_sc_connection() throws Exception { + var fakeClient = newFakeClient().build(); + mockAssistCreatingConnection(fakeClient, CONNECTION_ID); + mockAssistBinding(fakeClient, CONFIG_SCOPE_ID, CONNECTION_ID, PROJECT_KEY); + + backend = newBackend() + .withSonarCloudUrl("https://sonar.my") + .withUnboundConfigScope(CONFIG_SCOPE_ID, SONAR_PROJECT_NAME) + .withEmbeddedServer() + .build(fakeClient); + + var statusCode = executeOpenSCIssueRequest(ISSUE_KEY, PROJECT_KEY, BRANCH_NAME, "orgKey"); + assertThat(statusCode).isEqualTo(200); + + verify(fakeClient, timeout(2000)).showIssue(eq(CONFIG_SCOPE_ID), any()); + verify(fakeClient, never()).showMessage(any(), any()); + + ArgumentCaptor captor = ArgumentCaptor.captor(); + verify(fakeClient, timeout(1000)).assistCreatingConnection(captor.capture(), any()); + assertThat(captor.getAllValues()) + .extracting(connectionParams -> connectionParams.getConnectionParams().getRight().getOrganizationKey(), + AssistCreatingConnectionParams::getTokenName, + AssistCreatingConnectionParams::getTokenValue) + .containsExactly(tuple("orgKey", null, null)); + } + + @Test + void it_should_revoke_token_when_exception_thrown_while_assist_creating_the_connection() throws Exception { + var fakeClient = newFakeClient().build(); + doThrow(RuntimeException.class).when(fakeClient).assistCreatingConnection(any(), any()); + + backend = newBackend() + .withSonarCloudUrl("https://sonar.my") + .withUnboundConfigScope(CONFIG_SCOPE_ID, SONAR_PROJECT_NAME) + .withEmbeddedServer() + .build(fakeClient); + + var statusCode = executeOpenSCIssueRequest(ISSUE_KEY, PROJECT_KEY, BRANCH_NAME, "orgKey", "token-name", "token-value"); + assertThat(statusCode).isEqualTo(200); + + ArgumentCaptor captor = ArgumentCaptor.captor(); + verify(fakeClient, after(500).atLeastOnce()).log(captor.capture()); + assertThat(captor.getAllValues()) + .extracting(LogParams::getMessage) + .containsAnyOf("Revoking token 'token-name'"); } @Test @@ -333,29 +395,33 @@ void it_should_fail_request_when_project_parameter_missing() throws Exception { } private int executeOpenIssueRequest(String issueKey, String projectKey, String branch) throws IOException, InterruptedException { - HttpRequest request = openIssueWithProjectAndKeyRequest("&issue=" + issueKey, "&project=" + projectKey, "&branch=" + branch); + HttpRequest request = openIssueRequest("&issue=" + issueKey, "&project=" + projectKey, "&branch=" + branch); var response = java.net.http.HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString()); return response.statusCode(); } - private int executeOpenIssueRequest(String issueKey, String projectKey, String branch, String pullRequest) throws IOException, InterruptedException { - HttpRequest request = openIssueWithBranchAndPRRequest("&issue=" + issueKey, "&project=" + projectKey, "&branch=" + branch, "&pullRequest=" + pullRequest); + private int executeOpenSCIssueRequest(String issueKey, String projectKey, String branch, String organizationKey) throws IOException, InterruptedException { + HttpRequest request = this.openIssueRequest("&issue=" + issueKey, "&project=" + projectKey, "&branch=" + branch, "&organizationKey=" + organizationKey); var response = java.net.http.HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString()); return response.statusCode(); } - private HttpRequest openIssueWithProjectAndKeyRequest(String issueParam, String projectParam, String branchParam) { - return HttpRequest.newBuilder() - .uri(URI.create( - "http://localhost:" + backend.getEmbeddedServerPort() + "/sonarlint/api/issues/show?server=" + serverWithIssues.baseUrl() + projectParam + issueParam + branchParam)) - .header("Origin", "https://sonar.my") - .GET().build(); + private Object executeOpenSCIssueRequest(String issueKey, String projectKey, String branchName, String orgKey, String tokenName, String tokenValue) throws IOException, InterruptedException { + HttpRequest request = this.openIssueRequest("&issue=" + issueKey, "&project=" + projectKey, "&branch=" + branchName, "&organizationKey=" + orgKey, "&tokenName=" + tokenName, "&tokenValue=" + tokenValue); + var response = java.net.http.HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString()); + return response.statusCode(); + } + + private int executeOpenIssueRequest(String issueKey, String projectKey, String branch, String pullRequest) throws IOException, InterruptedException { + HttpRequest request = openIssueRequest("&issue=" + issueKey, "&project=" + projectKey, "&branch=" + branch, "&pullRequest=" + pullRequest); + var response = java.net.http.HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString()); + return response.statusCode(); } - private HttpRequest openIssueWithBranchAndPRRequest(String issueParam, String projectParam, String branchParam, String pullRequestParam) { + private HttpRequest openIssueRequest(String... params) { return HttpRequest.newBuilder() - .uri(URI.create("http://localhost:" + backend.getEmbeddedServerPort() + "/sonarlint/api/issues/show?server=" + serverWithIssues.baseUrl() + projectParam + issueParam - + branchParam + pullRequestParam)) + .uri(URI.create( + "http://localhost:" + backend.getEmbeddedServerPort() + "/sonarlint/api/issues/show?server=" + serverWithIssues.baseUrl() + String.join("", params))) .header("Origin", "https://sonar.my") .GET().build(); } diff --git a/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/adapter/EitherSonarQubeSonarCloudConnectionParamsAdapterFactory.java b/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/adapter/EitherSonarQubeSonarCloudConnectionParamsAdapterFactory.java new file mode 100644 index 0000000000..64f0ec6e0e --- /dev/null +++ b/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/adapter/EitherSonarQubeSonarCloudConnectionParamsAdapterFactory.java @@ -0,0 +1,37 @@ +/* + * SonarLint Core - RPC Protocol + * Copyright (C) 2016-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.sonarlint.core.rpc.protocol.adapter; + +import com.google.gson.reflect.TypeToken; +import org.eclipse.lsp4j.jsonrpc.json.adapters.EitherTypeAdapter; +import org.sonarsource.sonarlint.core.rpc.protocol.client.connection.SonarCloudConnectionParams; +import org.sonarsource.sonarlint.core.rpc.protocol.client.connection.SonarQubeConnectionParams; +import org.sonarsource.sonarlint.core.rpc.protocol.common.Either; + +public class EitherSonarQubeSonarCloudConnectionParamsAdapterFactory extends CustomEitherAdapterFactory { + + private static final TypeToken> ELEMENT_TYPE = new TypeToken<>() { + }; + + public EitherSonarQubeSonarCloudConnectionParamsAdapterFactory() { + super(ELEMENT_TYPE, SonarQubeConnectionParams.class, SonarCloudConnectionParams.class, new EitherTypeAdapter.PropertyChecker("serverUrl")); + } + +} diff --git a/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/client/connection/AssistCreatingConnectionParams.java b/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/client/connection/AssistCreatingConnectionParams.java index 70f436cd21..63419b6b1b 100644 --- a/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/client/connection/AssistCreatingConnectionParams.java +++ b/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/client/connection/AssistCreatingConnectionParams.java @@ -19,32 +19,47 @@ */ package org.sonarsource.sonarlint.core.rpc.protocol.client.connection; -import javax.annotation.Nullable; +import com.google.gson.annotations.JsonAdapter; +import org.sonarsource.sonarlint.core.rpc.protocol.adapter.EitherSonarQubeSonarCloudConnectionParamsAdapterFactory; +import org.sonarsource.sonarlint.core.rpc.protocol.common.Either; public class AssistCreatingConnectionParams { - private final String serverUrl; - @Nullable - private final String tokenName; - @Nullable - private final String tokenValue; - - public AssistCreatingConnectionParams(String serverUrl, @Nullable String tokenName, @Nullable String tokenValue) { - this.serverUrl = serverUrl; - this.tokenName = tokenName; - this.tokenValue = tokenValue; + @JsonAdapter(EitherSonarQubeSonarCloudConnectionParamsAdapterFactory.class) + private final Either connectionParams; + + public AssistCreatingConnectionParams(Either connectionParams) { + this.connectionParams = connectionParams; + } + + public AssistCreatingConnectionParams(SonarQubeConnectionParams sonarQubeConnection) { + this(Either.forLeft(sonarQubeConnection)); + } + + public AssistCreatingConnectionParams(SonarCloudConnectionParams sonarCloudConnection) { + this(Either.forRight(sonarCloudConnection)); + } + + public Either getConnectionParams() { + return connectionParams; } + /** + * @deprecated Use {@link #getConnectionParams()}.getLeft().getServerUrl() instead. + */ + @Deprecated(since = "10.3", forRemoval = true) public String getServerUrl() { - return serverUrl; + return connectionParams.isLeft() ? connectionParams.getLeft().getServerUrl() : null; } - @Nullable public String getTokenName() { - return tokenName; + return connectionParams.isLeft() ? + connectionParams.getLeft().getTokenName() + : connectionParams.getRight().getTokenName(); } - @Nullable public String getTokenValue() { - return tokenValue; + return connectionParams.isLeft() ? + connectionParams.getLeft().getTokenValue() + : connectionParams.getRight().getTokenValue(); } } diff --git a/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/client/connection/SonarCloudConnectionParams.java b/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/client/connection/SonarCloudConnectionParams.java new file mode 100644 index 0000000000..9d1af62cda --- /dev/null +++ b/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/client/connection/SonarCloudConnectionParams.java @@ -0,0 +1,49 @@ +/* + * SonarLint Core - RPC Protocol + * Copyright (C) 2016-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.sonarlint.core.rpc.protocol.client.connection; + +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; + +public class SonarCloudConnectionParams { + private final String organizationKey; + private final String tokenName; + private final String tokenValue; + + public SonarCloudConnectionParams(String organizationKey, @Nullable String tokenName, @Nullable String tokenValue) { + this.organizationKey = organizationKey; + this.tokenName = tokenName; + this.tokenValue = tokenValue; + } + + public String getOrganizationKey() { + return organizationKey; + } + + @CheckForNull + public String getTokenName() { + return tokenName; + } + + @CheckForNull + public String getTokenValue() { + return tokenValue; + } +} diff --git a/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/client/connection/SonarQubeConnectionParams.java b/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/client/connection/SonarQubeConnectionParams.java new file mode 100644 index 0000000000..7aab3404a8 --- /dev/null +++ b/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/client/connection/SonarQubeConnectionParams.java @@ -0,0 +1,49 @@ +/* + * SonarLint Core - RPC Protocol + * Copyright (C) 2016-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.sonarlint.core.rpc.protocol.client.connection; + +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; + +public class SonarQubeConnectionParams { + private final String serverUrl; + private final String tokenName; + private final String tokenValue; + + public SonarQubeConnectionParams(String serverUrl, @Nullable String tokenName, @Nullable String tokenValue) { + this.serverUrl = serverUrl; + this.tokenName = tokenName; + this.tokenValue = tokenValue; + } + + public String getServerUrl() { + return serverUrl; + } + + @CheckForNull + public String getTokenName() { + return tokenName; + } + + @CheckForNull + public String getTokenValue() { + return tokenValue; + } +}