Skip to content

Commit

Permalink
SLCORE-818 Provide connection parameters for SC while opening an issu…
Browse files Browse the repository at this point in the history
…e in IDE
  • Loading branch information
serhat-yenican-sonarsource committed May 27, 2024
1 parent 1b2f8a1 commit e0ab5b0
Show file tree
Hide file tree
Showing 6 changed files with 78 additions and 33 deletions.
5 changes: 4 additions & 1 deletion API_CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@

* Add `getRawIssues` method to `org.sonarsource.sonarlint.core.rpc.protocol.backend.analysis.AnalyzeFilesResponse`
* It allows clients to get raised issues in the analysis response.
* This method is temporarily added and will be removed when the deprecated APIs have been dropped.
* This method is temporarily added and will be removed when the deprecated APIs have been drop

* Add `getOrganizationKey` method to `org.sonarsource.sonarlint.core.rpc.protocol.client.connection.AssistCreatingConnectionParams`
* It allows clients to get organizationKey parameter required for creating a Sonar Cloud connection

## Deprecation

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,21 +74,22 @@ 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(String serverUrl, @Nullable String tokenName, @Nullable String tokenValue,
@Nullable String organizationKey, String projectKey, Callback callback) {
var cancelMonitor = new SonarLintCancelMonitor();
cancelMonitor.watchForShutdown(executorService);
executorService.submit(() -> assistConnectionAndBindingIfNeeded(serverUrl, tokenName, tokenValue, projectKey, callback, cancelMonitor));
executorService.submit(() -> assistConnectionAndBindingIfNeeded(serverUrl, tokenName, tokenValue, organizationKey, projectKey, callback, cancelMonitor));
}

private void assistConnectionAndBindingIfNeeded(String serverUrl, @Nullable String tokenName, @Nullable String tokenValue, String projectKey,
private void assistConnectionAndBindingIfNeeded(String serverUrl, @Nullable String tokenName, @Nullable String tokenValue, @Nullable String organizationKey, String projectKey,
Callback callback, SonarLintCancelMonitor cancelMonitor) {
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(serverUrl, tokenName, tokenValue, organizationKey, cancelMonitor);
var assistNewBindingResult = assistBindingAndWaitForRepositoryUpdate(assistNewConnectionResult.getNewConnectionId(),
projectKey, cancelMonitor);
callback.andThen(assistNewConnectionResult.getNewConnectionId(), assistNewBindingResult.getConfigurationScopeId(), cancelMonitor);
Expand All @@ -106,8 +107,8 @@ 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);
@Nullable String organizationKey, SonarLintCancelMonitor cancelMonitor) {
var assistNewConnectionResult = assistCreatingConnection(serverUrl, tokenName, tokenValue, organizationKey, 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 @@ -179,9 +180,10 @@ void endFullBindingProcess() {
bindingSuggestionProvider.enable();
}

AssistCreatingConnectionResponse assistCreatingConnection(String serverUrl, @Nullable String tokenName, @Nullable String tokenValue, SonarLintCancelMonitor cancelMonitor) {
AssistCreatingConnectionResponse assistCreatingConnection(String serverUrl, @Nullable String tokenName, @Nullable String tokenValue,
@Nullable String organizationKey, SonarLintCancelMonitor cancelMonitor) {
try {
var future = client.assistCreatingConnection(new AssistCreatingConnectionParams(serverUrl, tokenName, tokenValue));
var future = client.assistCreatingConnection(new AssistCreatingConnectionParams(serverUrl, tokenName, tokenValue, organizationKey));
cancelMonitor.onCancel(() -> future.cancel(true));
return future.join();
} catch (Exception e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ public void handle(ClassicHttpRequest request, ClassicHttpResponse response, Htt
}
telemetryService.showHotspotRequestReceived();

requestHandlerBindingAssistant.assistConnectionAndBindingIfNeededAsync(showHotspotQuery.serverUrl, null, null, showHotspotQuery.projectKey,
requestHandlerBindingAssistant.assistConnectionAndBindingIfNeededAsync(showHotspotQuery.serverUrl, null, null, null, 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 @@ -59,6 +59,7 @@

import static org.apache.commons.lang3.StringUtils.isNotBlank;
import static org.apache.commons.lang3.StringUtils.isNotEmpty;
import static org.sonarsource.sonarlint.core.SonarCloudActiveEnvironment.PRODUCTION_URI;

@Named
@Singleton
Expand Down Expand Up @@ -88,7 +89,12 @@ public void handle(ClassicHttpRequest request, ClassicHttpResponse response, Htt
}
telemetryService.showIssueRequestReceived();

requestHandlerBindingAssistant.assistConnectionAndBindingIfNeededAsync(showIssueQuery.serverUrl, showIssueQuery.tokenName, showIssueQuery.tokenValue, showIssueQuery.projectKey,
requestHandlerBindingAssistant.assistConnectionAndBindingIfNeededAsync(
showIssueQuery.serverUrl,
showIssueQuery.tokenName,
showIssueQuery.tokenValue,
showIssueQuery.organizationKey,
showIssueQuery.projectKey,
(connectionId, configScopeId, cancelMonitor) -> {
if (configScopeId != null) {
showIssueForScope(connectionId, configScopeId, showIssueQuery.issueKey, showIssueQuery.projectKey, showIssueQuery.branch,
Expand Down Expand Up @@ -169,7 +175,7 @@ static ShowIssueQuery extractQuery(ClassicHttpRequest request) {
// 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"));
params.get("pullRequest"), params.get("tokenName"), params.get("tokenValue"), params.get("organizationKey"));
}

@VisibleForTesting
Expand All @@ -184,23 +190,31 @@ public static class ShowIssueQuery {
private final String tokenName;
@Nullable
private final String tokenValue;
@Nullable
private final String organizationKey;

public ShowIssueQuery(String serverUrl, String projectKey, String issueKey, String branch,
@Nullable String pullRequest, @Nullable String tokenName, @Nullable String tokenValue) {
@Nullable String pullRequest, @Nullable String tokenName, @Nullable String tokenValue, @Nullable String organizationKey) {
this.serverUrl = serverUrl;
this.projectKey = projectKey;
this.issueKey = issueKey;
this.branch = branch;
this.pullRequest = pullRequest;
this.tokenName = tokenName;
this.tokenValue = tokenValue;
this.organizationKey = organizationKey;
}

public boolean isValid() {
return isNotBlank(serverUrl) && isNotBlank(projectKey) && isNotBlank(issueKey) && isNotBlank(branch)
&& (!isSonarCloud(serverUrl) || isNotBlank(organizationKey))
&& isPullRequestParamValid() && isTokenValid();
}

private static boolean isSonarCloud(String serverUrl) {
return PRODUCTION_URI.toString().equals(serverUrl);
}

public boolean isPullRequestParamValid() {
if (pullRequest != null) {
return isNotEmpty(pullRequest);
Expand All @@ -227,6 +241,10 @@ public String getProjectKey() {
return projectKey;
}

public String getOrganizationKey() {
return organizationKey;
}

public String getIssueKey() {
return issueKey;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,43 +149,57 @@ 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"));
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"));
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();
assertThat(new ShowIssueRequestHandler.ShowIssueQuery("serverUrl", "project", "issue", "branch", "pullRequest", null, null, null).isValid()).isTrue();

assertThat(new ShowIssueRequestHandler.ShowIssueQuery("", "project", "issue", "branch", "pullRequest", null, null, null).isValid()).isFalse();
assertThat(new ShowIssueRequestHandler.ShowIssueQuery("serverUrl", "", "issue", "branch", "pullRequest", null, null, null).isValid()).isFalse();
assertThat(new ShowIssueRequestHandler.ShowIssueQuery("serverUrl", "project", "", "branch", "pullRequest", null, null, null).isValid()).isFalse();
assertThat(new ShowIssueRequestHandler.ShowIssueQuery("serverUrl", "project", "issue", "", "", null, null, null).isValid()).isFalse();
assertThat(new ShowIssueRequestHandler.ShowIssueQuery("serverUrl", "project", "issue", "branch", null, null, null, null).isValid()).isTrue();

assertThat(new ShowIssueRequestHandler.ShowIssueQuery("serverUrl", "project", "issue", "branch", "pullRequest", "name", null, null).isValid()).isFalse();
assertThat(new ShowIssueRequestHandler.ShowIssueQuery("serverUrl", "project", "issue", "branch", "pullRequest", null, "value", null).isValid()).isFalse();
assertThat(new ShowIssueRequestHandler.ShowIssueQuery("serverUrl", "project", "issue", "branch", "pullRequest", "name", "", null).isValid()).isFalse();
assertThat(new ShowIssueRequestHandler.ShowIssueQuery("serverUrl", "project", "issue", "branch", "pullRequest", "", "value", null).isValid()).isFalse();
assertThat(new ShowIssueRequestHandler.ShowIssueQuery("serverUrl", "project", "issue", "branch", "pullRequest", "name", "value", null).isValid()).isTrue();

assertThat(new ShowIssueRequestHandler.ShowIssueQuery("https://sonarcloud.io", "project", "issue", "branch", "pullRequest", "name", "value", "organizationKey").isValid()).isTrue();
}

@Test
void should_invalid_when_no_organization_key_for_SC() {
assertThat(new ShowIssueRequestHandler.ShowIssueQuery("https://sonarcloud.io", "project", "issue", "branch", "pullRequest", "name", "value", null).isValid()).isFalse();
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,14 @@ public class AssistCreatingConnectionParams {
private final String tokenName;
@Nullable
private final String tokenValue;
@Nullable
private final String organizationKey;

public AssistCreatingConnectionParams(String serverUrl, @Nullable String tokenName, @Nullable String tokenValue) {
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;
}

public String getServerUrl() {
Expand All @@ -47,4 +50,9 @@ public String getTokenName() {
public String getTokenValue() {
return tokenValue;
}

@Nullable
public String getOrganizationKey() {
return organizationKey;
}
}

0 comments on commit e0ab5b0

Please sign in to comment.