diff --git a/API_CHANGES.md b/API_CHANGES.md index 74ffadfbb6..fec2c7f101 100644 --- a/API_CHANGES.md +++ b/API_CHANGES.md @@ -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 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..cc9b69c58a 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 @@ -74,13 +74,14 @@ 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 { @@ -88,7 +89,7 @@ private void assistConnectionAndBindingIfNeeded(String serverUrl, @Nullable Stri 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); @@ -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 @@ -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) { 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..3551b72141 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 @@ -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); 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..e4232ccbe2 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 @@ -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 @@ -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, @@ -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 @@ -184,9 +190,11 @@ 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; @@ -194,13 +202,19 @@ public ShowIssueQuery(String serverUrl, String projectKey, String issueKey, Stri 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); @@ -227,6 +241,10 @@ public String getProjectKey() { return projectKey; } + 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..65035819d2 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 @@ -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 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..2632e322bd 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 @@ -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() { @@ -47,4 +50,9 @@ public String getTokenName() { public String getTokenValue() { return tokenValue; } + + @Nullable + public String getOrganizationKey() { + return organizationKey; + } }