Skip to content

Commit

Permalink
SLCORE-818 split assist connection parameters for SQ and SC
Browse files Browse the repository at this point in the history
  • Loading branch information
serhat-yenican-sonarsource committed May 29, 2024
1 parent 55b6d06 commit b3af88c
Show file tree
Hide file tree
Showing 7 changed files with 217 additions and 52 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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) {
Expand All @@ -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
Expand All @@ -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) {
Expand All @@ -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);
}

Expand Down Expand Up @@ -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);
}
Expand All @@ -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;
Expand Down Expand Up @@ -253,6 +263,7 @@ public String getProjectKey() {
return projectKey;
}

@Nullable
public String getOrganizationKey() {
return organizationKey;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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<AssistSonarQubeConnection, AssistSonarCloudConnection> {

private static final TypeToken<Either<AssistSonarQubeConnection, AssistSonarCloudConnection>> ELEMENT_TYPE = new TypeToken<>() {
};

public EitherAssistSonarQubeSonarCloudConnectionAdapterFactory() {
super(ELEMENT_TYPE, AssistSonarQubeConnection.class, AssistSonarCloudConnection.class, new EitherTypeAdapter.PropertyChecker("organizationKey"));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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<AssistSonarQubeConnection, AssistSonarCloudConnection> connection;

public AssistCreatingConnectionParams(Either<AssistSonarQubeConnection, AssistSonarCloudConnection> connection) {
this.connection = connection;
}

public AssistCreatingConnectionParams(AssistSonarQubeConnection sonarQubeConnection) {
this(Either.forLeft(sonarQubeConnection));
}

public AssistCreatingConnectionParams(AssistSonarCloudConnection sonarCloudConnection) {
this(Either.forRight(sonarCloudConnection));
}

public Either<AssistSonarQubeConnection, AssistSonarCloudConnection> 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();
}
}
Original file line number Diff line number Diff line change
@@ -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;
}
}
Loading

0 comments on commit b3af88c

Please sign in to comment.