From b3af88c6fadb2a01516b83acafd7839a97af127d Mon Sep 17 00:00:00 2001 From: Serhat Yenican Date: Wed, 29 May 2024 08:28:49 +0200 Subject: [PATCH] SLCORE-818 split assist connection parameters for SQ and SC --- .../RequestHandlerBindingAssistant.java | 37 +++++++------ .../server/ShowHotspotRequestHandler.java | 7 ++- .../server/ShowIssueRequestHandler.java | 31 +++++++---- ...ubeSonarCloudConnectionAdapterFactory.java | 37 +++++++++++++ .../AssistCreatingConnectionParams.java | 53 ++++++++++-------- .../AssistSonarCloudConnection.java | 55 +++++++++++++++++++ .../connection/AssistSonarQubeConnection.java | 49 +++++++++++++++++ 7 files changed, 217 insertions(+), 52 deletions(-) create mode 100644 rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/adapter/EitherAssistSonarQubeSonarCloudConnectionAdapterFactory.java create mode 100644 rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/client/connection/AssistSonarCloudConnection.java create mode 100644 rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/client/connection/AssistSonarQubeConnection.java 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 cc9b69c58a..5d230eb4ec 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 @@ -58,8 +58,7 @@ public class RequestHandlerBindingAssistant { private final ExecutorServiceShutdownWatchable executorService; public RequestHandlerBindingAssistant(BindingSuggestionProvider bindingSuggestionProvider, BindingCandidatesFinder bindingCandidatesFinder, SonarLintRpcClient client, - ConnectionConfigurationRepository connectionConfigurationRepository, - ConfigurationRepository configurationRepository, UserTokenService userTokenService) { + ConnectionConfigurationRepository connectionConfigurationRepository, ConfigurationRepository configurationRepository, UserTokenService userTokenService) { this.bindingSuggestionProvider = bindingSuggestionProvider; this.bindingCandidatesFinder = bindingCandidatesFinder; this.client = client; @@ -74,22 +73,22 @@ interface Callback { void andThen(String connectionId, @Nullable String configurationScopeId, SonarLintCancelMonitor cancelMonitor); } - void assistConnectionAndBindingIfNeededAsync(String serverUrl, @Nullable String tokenName, @Nullable String tokenValue, - @Nullable String organizationKey, 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, organizationKey, projectKey, callback, cancelMonitor)); + executorService.submit(() -> assistConnectionAndBindingIfNeeded(connectionParams, projectKey, callback, cancelMonitor)); } - private void assistConnectionAndBindingIfNeeded(String serverUrl, @Nullable String tokenName, @Nullable String tokenValue, @Nullable String organizationKey, String projectKey, + private void assistConnectionAndBindingIfNeeded(AssistCreatingConnectionParams connectionParams, String projectKey, Callback callback, SonarLintCancelMonitor cancelMonitor) { + String serverUrl = connectionParams.getServerUrl(); 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, organizationKey, cancelMonitor); + var assistNewConnectionResult = assistCreatingConnectionAndWaitForRepositoryUpdate(connectionParams, cancelMonitor); var assistNewBindingResult = assistBindingAndWaitForRepositoryUpdate(assistNewConnectionResult.getNewConnectionId(), projectKey, cancelMonitor); callback.andThen(assistNewConnectionResult.getNewConnectionId(), assistNewBindingResult.getConfigurationScopeId(), cancelMonitor); @@ -106,9 +105,9 @@ private void assistConnectionAndBindingIfNeeded(String serverUrl, @Nullable Stri } } - private AssistCreatingConnectionResponse assistCreatingConnectionAndWaitForRepositoryUpdate(String serverUrl, @Nullable String tokenName, @Nullable String tokenValue, - @Nullable String organizationKey, SonarLintCancelMonitor cancelMonitor) { - var assistNewConnectionResult = assistCreatingConnection(serverUrl, tokenName, tokenValue, organizationKey, cancelMonitor); + 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 @@ -180,20 +179,26 @@ void endFullBindingProcess() { bindingSuggestionProvider.enable(); } - AssistCreatingConnectionResponse assistCreatingConnection(String serverUrl, @Nullable String tokenName, @Nullable String tokenValue, - @Nullable String organizationKey, SonarLintCancelMonitor cancelMonitor) { + AssistCreatingConnectionResponse assistCreatingConnection(AssistCreatingConnectionParams connectionParams, SonarLintCancelMonitor cancelMonitor) { try { - var future = client.assistCreatingConnection(new AssistCreatingConnectionParams(serverUrl, tokenName, tokenValue, organizationKey)); + 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(connectionParams.getServerUrl(), 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 3551b72141..625d73d92f 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.AssistSonarQubeConnection; 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, null, showHotspotQuery.projectKey, + var sonarQubeConnection = new AssistSonarQubeConnection(showHotspotQuery.serverUrl, null, null); + var connectionParams = new AssistCreatingConnectionParams(sonarQubeConnection); + 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 3ad675ee39..dc1c0d3f89 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 @@ -47,6 +47,9 @@ 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.AssistSonarCloudConnection; +import org.sonarsource.sonarlint.core.rpc.protocol.client.connection.AssistSonarQubeConnection; 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; @@ -72,7 +75,7 @@ public class ShowIssueRequestHandler implements HttpRequestHandler { private final TelemetryService telemetryService; private final RequestHandlerBindingAssistant requestHandlerBindingAssistant; private final PathTranslationService pathTranslationService; - private final SonarCloudActiveEnvironment sonarCloudActiveEnvironment; + private final String sonarCloudUrl; public ShowIssueRequestHandler(SonarLintRpcClient client, ServerApiProvider serverApiProvider, TelemetryService telemetryService, RequestHandlerBindingAssistant requestHandlerBindingAssistant, PathTranslationService pathTranslationService, SonarCloudActiveEnvironment sonarCloudActiveEnvironment) { @@ -81,7 +84,7 @@ public ShowIssueRequestHandler(SonarLintRpcClient client, ServerApiProvider serv this.telemetryService = telemetryService; this.requestHandlerBindingAssistant = requestHandlerBindingAssistant; this.pathTranslationService = pathTranslationService; - this.sonarCloudActiveEnvironment = sonarCloudActiveEnvironment; + this.sonarCloudUrl = sonarCloudActiveEnvironment.getUri().toString(); } @Override @@ -93,11 +96,10 @@ public void handle(ClassicHttpRequest request, ClassicHttpResponse response, Htt } telemetryService.showIssueRequestReceived(); + AssistCreatingConnectionParams serverConnectionParams = createAssistServerConnectionParams(showIssueQuery); + requestHandlerBindingAssistant.assistConnectionAndBindingIfNeededAsync( - showIssueQuery.serverUrl, - showIssueQuery.tokenName, - showIssueQuery.tokenValue, - showIssueQuery.organizationKey, + serverConnectionParams, showIssueQuery.projectKey, (connectionId, configScopeId, cancelMonitor) -> { if (configScopeId != null) { @@ -110,10 +112,18 @@ public void handle(ClassicHttpRequest request, ClassicHttpResponse response, Htt response.setEntity(new StringEntity("OK")); } + private AssistCreatingConnectionParams createAssistServerConnectionParams(ShowIssueQuery query) { + String tokenName = query.getTokenName(); + String tokenValue = query.getTokenValue(); + return query.isSonarCloud ? + new AssistCreatingConnectionParams(new AssistSonarCloudConnection(sonarCloudUrl, query.getOrganizationKey(), tokenName, tokenValue)) + : new AssistCreatingConnectionParams(new AssistSonarQubeConnection(query.getServerUrl(), tokenName, tokenValue)); + } + private boolean isSonarCloud(ClassicHttpRequest request) throws ProtocolException { return Optional.ofNullable(request.getHeader("Origin")) .map(NameValuePair::getValue) - .map(serverUrl -> sonarCloudActiveEnvironment.getUri().toString().equals(serverUrl)) + .map(sonarCloudUrl::equals) .orElse(false); } @@ -186,7 +196,7 @@ ShowIssueQuery extractQuery(ClassicHttpRequest request) throws ProtocolException // Ignored } boolean isSonarCloud = isSonarCloud(request); - String serverUrl = isSonarCloud ? sonarCloudActiveEnvironment.getUri().toString() : params.get("server"); + 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); } @@ -207,8 +217,8 @@ public static class ShowIssueQuery { 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, @Nullable String organizationKey, boolean isSonarCloud) { + 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; @@ -253,6 +263,7 @@ public String getProjectKey() { return projectKey; } + @Nullable public String getOrganizationKey() { return organizationKey; } diff --git a/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/adapter/EitherAssistSonarQubeSonarCloudConnectionAdapterFactory.java b/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/adapter/EitherAssistSonarQubeSonarCloudConnectionAdapterFactory.java new file mode 100644 index 0000000000..216b41189f --- /dev/null +++ b/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/adapter/EitherAssistSonarQubeSonarCloudConnectionAdapterFactory.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.AssistSonarCloudConnection; +import org.sonarsource.sonarlint.core.rpc.protocol.client.connection.AssistSonarQubeConnection; +import org.sonarsource.sonarlint.core.rpc.protocol.common.Either; + +public class EitherAssistSonarQubeSonarCloudConnectionAdapterFactory extends CustomEitherAdapterFactory { + + private static final TypeToken> ELEMENT_TYPE = new TypeToken<>() { + }; + + public EitherAssistSonarQubeSonarCloudConnectionAdapterFactory() { + super(ELEMENT_TYPE, AssistSonarQubeConnection.class, AssistSonarCloudConnection.class, new EitherTypeAdapter.PropertyChecker("organizationKey")); + } + +} 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 2632e322bd..7f8ad48dea 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,40 +19,45 @@ */ 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.EitherAssistSonarQubeSonarCloudConnectionAdapterFactory; +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; - @Nullable - private final String organizationKey; - - public AssistCreatingConnectionParams(String serverUrl, @Nullable String tokenName, @Nullable String tokenValue, @Nullable String organizationKey) { - this.serverUrl = serverUrl; - this.tokenName = tokenName; - this.tokenValue = tokenValue; - this.organizationKey = organizationKey; + @JsonAdapter(EitherAssistSonarQubeSonarCloudConnectionAdapterFactory.class) + private final Either connection; + + public AssistCreatingConnectionParams(Either connection) { + this.connection = connection; + } + + public AssistCreatingConnectionParams(AssistSonarQubeConnection sonarQubeConnection) { + this(Either.forLeft(sonarQubeConnection)); + } + + public AssistCreatingConnectionParams(AssistSonarCloudConnection sonarCloudConnection) { + this(Either.forRight(sonarCloudConnection)); + } + + public Either getConnection() { + return connection; } public String getServerUrl() { - return serverUrl; + return connection.isLeft() ? + connection.getLeft().getServerUrl() + : connection.getRight().getServerUrl(); } - @Nullable public String getTokenName() { - return tokenName; + return connection.isLeft() ? + connection.getLeft().getTokenName() + : connection.getRight().getTokenName(); } - @Nullable public String getTokenValue() { - return tokenValue; - } - - @Nullable - public String getOrganizationKey() { - return organizationKey; + return connection.isLeft() ? + connection.getLeft().getTokenValue() + : connection.getRight().getTokenValue(); } } diff --git a/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/client/connection/AssistSonarCloudConnection.java b/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/client/connection/AssistSonarCloudConnection.java new file mode 100644 index 0000000000..b9228c6350 --- /dev/null +++ b/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/client/connection/AssistSonarCloudConnection.java @@ -0,0 +1,55 @@ +/* + * 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 AssistSonarCloudConnection { + private final String serverUrl; + private final String organizationKey; + private final String tokenName; + private final String tokenValue; + + public AssistSonarCloudConnection(String serverUrl, String organizationKey, @Nullable String tokenName, @Nullable String tokenValue) { + this.serverUrl = serverUrl; + this.organizationKey = organizationKey; + this.tokenName = tokenName; + this.tokenValue = tokenValue; + } + + public String getServerUrl() { + return serverUrl; + } + + 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/AssistSonarQubeConnection.java b/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/client/connection/AssistSonarQubeConnection.java new file mode 100644 index 0000000000..4060992d95 --- /dev/null +++ b/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/client/connection/AssistSonarQubeConnection.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 AssistSonarQubeConnection { + private final String serverUrl; + private final String tokenName; + private final String tokenValue; + + public AssistSonarQubeConnection(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; + } +}