From 40150d070d7959907789568acf543af2fb88405f Mon Sep 17 00:00:00 2001 From: Kirill Knize Date: Tue, 17 Sep 2024 11:18:47 +0200 Subject: [PATCH] SLCORE-800 Clean up deprecated classes and methods --- API_CHANGES.md | 4 + .../core/analysis/AnalysisService.java | 2 - .../core/hotspot/HotspotService.java | 102 ++++++- .../core/spring/SonarLintSpringAppConfig.java | 4 - .../core/tracking/IssueMatchingService.java | 207 ------------- .../SecurityHotspotMatchingService.java | 251 --------------- ...TrackedFindingMatchingAttributeMapper.java | 60 ---- .../rpc/impl/AnalysisRpcServiceDelegate.java | 12 - .../impl/IssueTrackingRpcServiceDelegate.java | 38 --- ...rityHotspotMatchingRpcServiceDelegate.java | 41 --- .../core/rpc/impl/SonarLintRpcServerImpl.java | 12 - .../client/SonarLintRpcClientDelegate.java | 10 - .../rpc/client/SonarLintRpcClientImpl.java | 6 - .../test/java/its/AbstractConnectedTests.java | 10 + .../its/MockSonarLintRpcClientDelegate.java | 40 ++- .../src/test/java/its/SonarCloudTests.java | 77 ++--- .../its/SonarQubeCommunityEditionTests.java | 103 +------ .../its/SonarQubeDeveloperEditionTests.java | 180 +++++------ .../its/SonarQubeEnterpriseEditionTests.java | 21 +- .../src/test/java/its/StandaloneTests.java | 23 +- .../ConnectedStorageProblemsMediumTests.java | 1 - .../analysis/AnalysisMediumTests.java | 197 ++++++------ .../MatchWithServerHotspotsMediumTests.java | 187 ------------ ...utionStatusChangePermittedMediumTests.java | 289 +++++++++++++----- .../issues/IssuesStatusChangeMediumTests.java | 245 +++++++++------ .../TrackWithServerIssuesMediumTests.java | 215 ------------- ...esInConnectedModePromotionMediumTests.java | 17 +- .../src/test/java/utils/AnalysisUtils.java | 8 + pom.xml | 1 + .../protocol/SonarLintLauncherBuilder.java | 4 - .../core/rpc/protocol/SonarLintRpcClient.java | 13 - .../core/rpc/protocol/SonarLintRpcServer.java | 8 - ...herServerOrLocalHotspotAdapterFactory.java | 37 --- ...itherServerOrLocalIssueAdapterFactory.java | 37 --- .../backend/analysis/AnalysisRpcService.java | 8 - .../backend/hotspot/HotspotStatus.java | 3 +- .../tracking/ClientTrackedFindingDto.java | 82 ----- .../tracking/IssueTrackingRpcService.java | 45 --- .../backend/tracking/LocalOnlyIssueDto.java | 50 --- .../tracking/LocalOnlySecurityHotspotDto.java | 40 --- ...MatchWithServerSecurityHotspotsParams.java | 55 ---- ...tchWithServerSecurityHotspotsResponse.java | 45 --- .../SecurityHotspotMatchingRpcService.java | 46 --- .../tracking/ServerMatchedIssueDto.java | 83 ----- .../ServerMatchedSecurityHotspotDto.java | 68 ----- .../tracking/TrackWithServerIssuesParams.java | 55 ---- .../TrackWithServerIssuesResponse.java | 45 --- .../client/analysis/DidRaiseIssueParams.java | 49 --- .../AssistCreatingConnectionParams.java | 8 - .../test/utils/SonarLintTestRpcServer.java | 12 - 50 files changed, 779 insertions(+), 2377 deletions(-) delete mode 100644 backend/core/src/main/java/org/sonarsource/sonarlint/core/tracking/IssueMatchingService.java delete mode 100644 backend/core/src/main/java/org/sonarsource/sonarlint/core/tracking/SecurityHotspotMatchingService.java delete mode 100644 backend/core/src/main/java/org/sonarsource/sonarlint/core/tracking/matching/ClientTrackedFindingMatchingAttributeMapper.java delete mode 100644 backend/rpc-impl/src/main/java/org/sonarsource/sonarlint/core/rpc/impl/IssueTrackingRpcServiceDelegate.java delete mode 100644 backend/rpc-impl/src/main/java/org/sonarsource/sonarlint/core/rpc/impl/SecurityHotspotMatchingRpcServiceDelegate.java delete mode 100644 medium-tests/src/test/java/mediumtest/hotspots/MatchWithServerHotspotsMediumTests.java delete mode 100644 medium-tests/src/test/java/mediumtest/issues/TrackWithServerIssuesMediumTests.java delete mode 100644 rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/adapter/EitherServerOrLocalHotspotAdapterFactory.java delete mode 100644 rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/adapter/EitherServerOrLocalIssueAdapterFactory.java delete mode 100644 rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/backend/tracking/ClientTrackedFindingDto.java delete mode 100644 rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/backend/tracking/IssueTrackingRpcService.java delete mode 100644 rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/backend/tracking/LocalOnlyIssueDto.java delete mode 100644 rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/backend/tracking/LocalOnlySecurityHotspotDto.java delete mode 100644 rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/backend/tracking/MatchWithServerSecurityHotspotsParams.java delete mode 100644 rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/backend/tracking/MatchWithServerSecurityHotspotsResponse.java delete mode 100644 rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/backend/tracking/SecurityHotspotMatchingRpcService.java delete mode 100644 rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/backend/tracking/ServerMatchedIssueDto.java delete mode 100644 rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/backend/tracking/ServerMatchedSecurityHotspotDto.java delete mode 100644 rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/backend/tracking/TrackWithServerIssuesParams.java delete mode 100644 rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/backend/tracking/TrackWithServerIssuesResponse.java delete mode 100644 rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/client/analysis/DidRaiseIssueParams.java diff --git a/API_CHANGES.md b/API_CHANGES.md index fc186c06ca..d5b1633387 100644 --- a/API_CHANGES.md +++ b/API_CHANGES.md @@ -3,6 +3,10 @@ ## Breaking changes * New feature flag `enableMonitoring` in `org.sonarsource.sonarlint.core.rpc.protocol.backend.initialize.FeatureFlagsDto` allows clients to opt into monitoring with Sentry +* Removed `org.sonarsource.sonarlint.core.rpc.protocol.SonarLintRpcClient#didRaiseIssue` and associated types. See `raiseIssues` and `raiseHotspots` instead. +* Removed `org.sonarsource.sonarlint.core.rpc.protocol.SonarLintRpcServer#getIssueTrackingService` and associated types. Tracking is managed by the backend. +* Removed `org.sonarsource.sonarlint.core.rpc.protocol.SonarLintRpcServer#getSecurityHotspotMatchingService` and associated types. Tracking is managed by the backend. +* Removed `org.sonarsource.sonarlint.core.rpc.protocol.client.connection.AssistCreatingConnectionParams#getServerUrl()`. Use `getConnectionParams` instead. ## New features diff --git a/backend/core/src/main/java/org/sonarsource/sonarlint/core/analysis/AnalysisService.java b/backend/core/src/main/java/org/sonarsource/sonarlint/core/analysis/AnalysisService.java index fad3ba052c..af2799fe5c 100644 --- a/backend/core/src/main/java/org/sonarsource/sonarlint/core/analysis/AnalysisService.java +++ b/backend/core/src/main/java/org/sonarsource/sonarlint/core/analysis/AnalysisService.java @@ -93,7 +93,6 @@ import org.sonarsource.sonarlint.core.rpc.protocol.backend.initialize.InitializeParams; import org.sonarsource.sonarlint.core.rpc.protocol.client.analysis.DidChangeAnalysisReadinessParams; import org.sonarsource.sonarlint.core.rpc.protocol.client.analysis.DidDetectSecretParams; -import org.sonarsource.sonarlint.core.rpc.protocol.client.analysis.DidRaiseIssueParams; import org.sonarsource.sonarlint.core.rpc.protocol.client.analysis.FileEditDto; import org.sonarsource.sonarlint.core.rpc.protocol.client.analysis.GetInferredAnalysisPropertiesParams; import org.sonarsource.sonarlint.core.rpc.protocol.client.analysis.QuickFixDto; @@ -698,7 +697,6 @@ private void streamIssue(String configScopeId, UUID analysisId, Issue issue, Con if (activeRule != null) { var rawIssue = new RawIssue(issue, activeRule); rawIssues.add(rawIssue); - client.didRaiseIssue(new DidRaiseIssueParams(configScopeId, analysisId, toDto(issue, activeRule))); if (ruleKey.contains("secrets")) { client.didDetectSecret(new DidDetectSecretParams(configScopeId)); } diff --git a/backend/core/src/main/java/org/sonarsource/sonarlint/core/hotspot/HotspotService.java b/backend/core/src/main/java/org/sonarsource/sonarlint/core/hotspot/HotspotService.java index a3cab9cb0d..25884e41b6 100644 --- a/backend/core/src/main/java/org/sonarsource/sonarlint/core/hotspot/HotspotService.java +++ b/backend/core/src/main/java/org/sonarsource/sonarlint/core/hotspot/HotspotService.java @@ -31,6 +31,8 @@ import org.sonarsource.sonarlint.core.commons.HotspotReviewStatus; import org.sonarsource.sonarlint.core.commons.log.SonarLintLogger; import org.sonarsource.sonarlint.core.commons.progress.SonarLintCancelMonitor; +import org.sonarsource.sonarlint.core.event.SonarServerEventReceivedEvent; +import org.sonarsource.sonarlint.core.reporting.FindingReportingService; import org.sonarsource.sonarlint.core.repository.config.ConfigurationRepository; import org.sonarsource.sonarlint.core.repository.connection.ConnectionConfigurationRepository; import org.sonarsource.sonarlint.core.rpc.protocol.SonarLintRpcClient; @@ -39,11 +41,18 @@ import org.sonarsource.sonarlint.core.rpc.protocol.backend.hotspot.CheckStatusChangePermittedResponse; import org.sonarsource.sonarlint.core.rpc.protocol.backend.hotspot.HotspotStatus; import org.sonarsource.sonarlint.core.rpc.protocol.client.OpenUrlInBrowserParams; +import org.sonarsource.sonarlint.core.rpc.protocol.client.hotspot.RaisedHotspotDto; import org.sonarsource.sonarlint.core.serverapi.EndpointParams; import org.sonarsource.sonarlint.core.serverapi.ServerApiHelper; import org.sonarsource.sonarlint.core.serverapi.UrlUtils; +import org.sonarsource.sonarlint.core.serverapi.hotspot.ServerHotspot; +import org.sonarsource.sonarlint.core.serverapi.push.SecurityHotspotChangedEvent; +import org.sonarsource.sonarlint.core.serverapi.push.SecurityHotspotClosedEvent; +import org.sonarsource.sonarlint.core.serverapi.push.SecurityHotspotRaisedEvent; import org.sonarsource.sonarlint.core.storage.StorageService; import org.sonarsource.sonarlint.core.telemetry.TelemetryService; +import org.sonarsource.sonarlint.core.tracking.TaintVulnerabilityTrackingService; +import org.springframework.context.event.EventListener; @Named @Singleton @@ -60,11 +69,12 @@ public class HotspotService { private final ServerApiProvider serverApiProvider; private final TelemetryService telemetryService; private final SonarProjectBranchTrackingService branchTrackingService; + private final FindingReportingService findingReportingService; private final StorageService storageService; public HotspotService(SonarLintRpcClient client, StorageService storageService, ConfigurationRepository configurationRepository, ConnectionConfigurationRepository connectionRepository, ServerApiProvider serverApiProvider, TelemetryService telemetryService, - SonarProjectBranchTrackingService branchTrackingService) { + SonarProjectBranchTrackingService branchTrackingService, FindingReportingService findingReportingService) { this.client = client; this.storageService = storageService; this.configurationRepository = configurationRepository; @@ -72,6 +82,7 @@ public HotspotService(SonarLintRpcClient client, StorageService storageService, this.serverApiProvider = serverApiProvider; this.telemetryService = telemetryService; this.branchTrackingService = branchTrackingService; + this.findingReportingService = findingReportingService; } public void openHotspotInBrowser(String configScopeId, String hotspotKey) { @@ -156,10 +167,6 @@ private void saveStatusInStorage(Binding binding, String hotspotKey, HotspotRevi .changeHotspotStatus(hotspotKey, newStatus); } - private boolean isLocalDetectionSupported(boolean isSonarCloud, String connectionId) { - return isSonarCloud || storageService.connection(connectionId).serverInfo().read().isPresent(); - } - static String buildHotspotUrl(String projectKey, String branch, String hotspotKey, EndpointParams endpointParams) { var relativePath = (endpointParams.isSonarCloud() ? "/project/security_hotspots?id=" : "/security_hotspots?id=") + UrlUtils.urlEncode(projectKey) @@ -170,4 +177,89 @@ static String buildHotspotUrl(String projectKey, String branch, String hotspotKe return ServerApiHelper.concat(endpointParams.getBaseUrl(), relativePath); } + + @EventListener + public void onServerEventReceived(SonarServerEventReceivedEvent event) { + var connectionId = event.getConnectionId(); + var serverEvent = event.getEvent(); + if (serverEvent instanceof SecurityHotspotChangedEvent) { + var hotspotChangedEvent = (SecurityHotspotChangedEvent) serverEvent; + updateStorage(connectionId, hotspotChangedEvent); + republishPreviouslyRaisedHotspots(connectionId, hotspotChangedEvent); + } else if (serverEvent instanceof SecurityHotspotClosedEvent) { + var hotspotClosedEvent = (SecurityHotspotClosedEvent) serverEvent; + updateStorage(connectionId, hotspotClosedEvent); + republishPreviouslyRaisedHotspots(connectionId, hotspotClosedEvent); + } else if (serverEvent instanceof SecurityHotspotRaisedEvent) { + var hotspotRaisedEvent = (SecurityHotspotRaisedEvent) serverEvent; + // We could try to match with an existing hotspot. But we don't do it because we don't invest in hotspots right now. + updateStorage(connectionId, hotspotRaisedEvent); + } + } + + private void updateStorage(String connectionId, SecurityHotspotRaisedEvent event) { + var hotspot = new ServerHotspot( + event.getHotspotKey(), + event.getRuleKey(), + event.getMainLocation().getMessage(), + event.getMainLocation().getFilePath(), + TaintVulnerabilityTrackingService.adapt(event.getMainLocation().getTextRange()), + event.getCreationDate(), + event.getStatus(), + event.getVulnerabilityProbability(), + null); + var projectKey = event.getProjectKey(); + storageService.connection(connectionId).project(projectKey).findings().insert(event.getBranch(), hotspot); + } + + private void updateStorage(String connectionId, SecurityHotspotClosedEvent event) { + var projectKey = event.getProjectKey(); + storageService.connection(connectionId).project(projectKey).findings().deleteHotspot(event.getHotspotKey()); + } + + private void updateStorage(String connectionId, SecurityHotspotChangedEvent event) { + var projectKey = event.getProjectKey(); + storageService.connection(connectionId).project(projectKey).findings().updateHotspot(event.getHotspotKey(), hotspot -> { + var status = event.getStatus(); + if (status != null) { + hotspot.setStatus(status); + } + var assignee = event.getAssignee(); + if (assignee != null) { + hotspot.setAssignee(assignee); + } + }); + } + + private void republishPreviouslyRaisedHotspots(String connectionId, SecurityHotspotChangedEvent event) { + var boundScopes = configurationRepository.getBoundScopesToConnectionAndSonarProject(connectionId, event.getProjectKey()); + boundScopes.forEach(scope -> { + var scopeId = scope.getConfigScopeId(); + findingReportingService.updateAndReportHotspots(scopeId, + raisedHotspotDto -> changedHotspotUpdater(raisedHotspotDto, event)); + }); + } + + private static RaisedHotspotDto changedHotspotUpdater(RaisedHotspotDto raisedHotspotDto, SecurityHotspotChangedEvent event) { + if (event.getHotspotKey().equals(raisedHotspotDto.getServerKey())) { + return raisedHotspotDto.builder().withHotspotStatus(HotspotStatus.valueOf(event.getStatus().name())).buildHotspot(); + } + return raisedHotspotDto; + } + + private void republishPreviouslyRaisedHotspots(String connectionId, SecurityHotspotClosedEvent event) { + var boundScopes = configurationRepository.getBoundScopesToConnectionAndSonarProject(connectionId, event.getProjectKey()); + boundScopes.forEach(scope -> { + var scopeId = scope.getConfigScopeId(); + findingReportingService.updateAndReportHotspots(scopeId, + raisedHotspotDto -> closedHotspotUpdater(raisedHotspotDto, event)); + }); + } + + private static RaisedHotspotDto closedHotspotUpdater(RaisedHotspotDto raisedHotspotDto, SecurityHotspotClosedEvent event) { + if (event.getHotspotKey().equals(raisedHotspotDto.getServerKey())) { + return null; + } + return raisedHotspotDto; + } } diff --git a/backend/core/src/main/java/org/sonarsource/sonarlint/core/spring/SonarLintSpringAppConfig.java b/backend/core/src/main/java/org/sonarsource/sonarlint/core/spring/SonarLintSpringAppConfig.java index faa7b2311f..c0c9c54aef 100644 --- a/backend/core/src/main/java/org/sonarsource/sonarlint/core/spring/SonarLintSpringAppConfig.java +++ b/backend/core/src/main/java/org/sonarsource/sonarlint/core/spring/SonarLintSpringAppConfig.java @@ -104,10 +104,8 @@ import org.sonarsource.sonarlint.core.sync.SonarProjectBranchesSynchronizationService; import org.sonarsource.sonarlint.core.sync.SynchronizationService; import org.sonarsource.sonarlint.core.sync.TaintSynchronizationService; -import org.sonarsource.sonarlint.core.tracking.IssueMatchingService; import org.sonarsource.sonarlint.core.tracking.KnownFindingsStorageService; import org.sonarsource.sonarlint.core.tracking.LocalOnlyIssueRepository; -import org.sonarsource.sonarlint.core.tracking.SecurityHotspotMatchingService; import org.sonarsource.sonarlint.core.tracking.TaintVulnerabilityTrackingService; import org.sonarsource.sonarlint.core.tracking.TrackingService; import org.sonarsource.sonarlint.core.usertoken.UserTokenService; @@ -161,7 +159,6 @@ IssueService.class, AnalysisService.class, SmartNotifications.class, - IssueMatchingService.class, LocalOnlyIssueRepository.class, WebSocketService.class, ServerEventsService.class, @@ -170,7 +167,6 @@ StorageService.class, SeverityModeService.class, NewCodeService.class, - SecurityHotspotMatchingService.class, UserTokenService.class, RequestHandlerBindingAssistant.class, TaintVulnerabilityTrackingService.class, diff --git a/backend/core/src/main/java/org/sonarsource/sonarlint/core/tracking/IssueMatchingService.java b/backend/core/src/main/java/org/sonarsource/sonarlint/core/tracking/IssueMatchingService.java deleted file mode 100644 index 6a59a4c83b..0000000000 --- a/backend/core/src/main/java/org/sonarsource/sonarlint/core/tracking/IssueMatchingService.java +++ /dev/null @@ -1,207 +0,0 @@ -/* - * SonarLint Core - Implementation - * Copyright (C) 2016-2025 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.tracking; - -import com.google.common.util.concurrent.MoreExecutors; -import java.nio.file.Path; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.UUID; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; -import javax.annotation.CheckForNull; -import javax.annotation.Nullable; -import javax.annotation.PreDestroy; -import javax.inject.Named; -import javax.inject.Singleton; -import org.jetbrains.annotations.NotNull; -import org.sonarsource.sonarlint.core.branch.SonarProjectBranchTrackingService; -import org.sonarsource.sonarlint.core.commons.Binding; -import org.sonarsource.sonarlint.core.commons.LineWithHash; -import org.sonarsource.sonarlint.core.commons.LocalOnlyIssue; -import org.sonarsource.sonarlint.core.commons.NewCodeDefinition; -import org.sonarsource.sonarlint.core.commons.api.TextRangeWithHash; -import org.sonarsource.sonarlint.core.commons.log.SonarLintLogger; -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.local.only.LocalOnlyIssueStorageService; -import org.sonarsource.sonarlint.core.newcode.NewCodeService; -import org.sonarsource.sonarlint.core.repository.config.ConfigurationRepository; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.issue.ResolutionStatus; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.tracking.ClientTrackedFindingDto; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.tracking.LineWithHashDto; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.tracking.LocalOnlyIssueDto; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.tracking.ServerMatchedIssueDto; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.tracking.TextRangeWithHashDto; -import org.sonarsource.sonarlint.core.rpc.protocol.common.Either; -import org.sonarsource.sonarlint.core.rules.RuleDetailsAdapter; -import org.sonarsource.sonarlint.core.serverconnection.issues.ServerIssue; -import org.sonarsource.sonarlint.core.storage.StorageService; -import org.sonarsource.sonarlint.core.sync.IssueSynchronizationService; -import org.sonarsource.sonarlint.core.tracking.matching.ClientTrackedFindingMatchingAttributeMapper; -import org.sonarsource.sonarlint.core.tracking.matching.IssueMatcher; -import org.sonarsource.sonarlint.core.tracking.matching.LocalOnlyIssueMatchingAttributesMapper; -import org.sonarsource.sonarlint.core.tracking.matching.ServerIssueMatchingAttributesMapper; - -@Named -@Singleton -public class IssueMatchingService { - private static final int FETCH_ALL_ISSUES_THRESHOLD = 10; - private static final SonarLintLogger LOG = SonarLintLogger.get(); - private final ConfigurationRepository configurationRepository; - private final StorageService storageService; - private final SonarProjectBranchTrackingService branchTrackingService; - private final IssueSynchronizationService issueSynchronizationService; - private final LocalOnlyIssueRepository localOnlyIssueRepository; - private final LocalOnlyIssueStorageService localOnlyIssueStorageService; - private final NewCodeService newCodeService; - private final PathTranslationService pathTranslationService; - private final ExecutorService executorService; - - public IssueMatchingService(ConfigurationRepository configurationRepository, StorageService storageService, SonarProjectBranchTrackingService branchTrackingService, - IssueSynchronizationService issueSynchronizationService, LocalOnlyIssueStorageService localOnlyIssueStorageService, LocalOnlyIssueRepository localOnlyIssueRepository, - NewCodeService newCodeService, PathTranslationService pathTranslationService) { - this.configurationRepository = configurationRepository; - this.storageService = storageService; - this.branchTrackingService = branchTrackingService; - this.issueSynchronizationService = issueSynchronizationService; - this.localOnlyIssueStorageService = localOnlyIssueStorageService; - this.localOnlyIssueRepository = localOnlyIssueRepository; - this.newCodeService = newCodeService; - this.pathTranslationService = pathTranslationService; - this.executorService = Executors.newSingleThreadExecutor(r -> new Thread(r, "sonarlint-server-tracking-issue-updater")); - } - - public Map>> trackWithServerIssues(String configurationScopeId, - Map> clientTrackedIssuesByIdeRelativePath, - boolean shouldFetchIssuesFromServer, SonarLintCancelMonitor cancelMonitor) { - var effectiveBindingOpt = configurationRepository.getEffectiveBinding(configurationScopeId); - var activeBranchOpt = branchTrackingService.awaitEffectiveSonarProjectBranch(configurationScopeId); - var translationOpt = pathTranslationService.getOrComputePathTranslation(configurationScopeId); - if (effectiveBindingOpt.isEmpty() || activeBranchOpt.isEmpty() || translationOpt.isEmpty()) { - return clientTrackedIssuesByIdeRelativePath.entrySet().stream() - .map(e -> Map.entry(e.getKey(), e.getValue().stream() - .map(issue -> Either.forRight( - new LocalOnlyIssueDto(UUID.randomUUID(), null))) - .collect(Collectors.toList()))) - .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); - } - var binding = effectiveBindingOpt.get(); - var activeBranch = activeBranchOpt.get(); - var translation = translationOpt.get(); - if (shouldFetchIssuesFromServer) { - refreshServerIssues(cancelMonitor, binding, activeBranch, clientTrackedIssuesByIdeRelativePath, translation); - } - var newCodeDefinition = newCodeService.getFullNewCodeDefinition(configurationScopeId) - .orElse(NewCodeDefinition.withAlwaysNew()); - return clientTrackedIssuesByIdeRelativePath.entrySet().stream().map(e -> { - var ideRelativePath = e.getKey(); - var serverRelativePath = translation.ideToServerPath(ideRelativePath); - var serverIssues = storageService.binding(binding).findings().load(activeBranch, serverRelativePath); - var localOnlyIssues = localOnlyIssueStorageService.get().loadForFile(configurationScopeId, serverRelativePath); - var matches = matchIssues(serverRelativePath, serverIssues, localOnlyIssues, e.getValue()) - .stream().map(result -> { - if (result.isLeft()) { - var serverIssue = result.getLeft(); - var creationDate = serverIssue.getCreationDate().toEpochMilli(); - var isOnNewCode = newCodeDefinition.isOnNewCode(creationDate); - var userSeverity = serverIssue.getUserSeverity(); - return Either.forLeft( - new ServerMatchedIssueDto(UUID.randomUUID(), serverIssue.getKey(), creationDate, serverIssue.isResolved(), - userSeverity != null ? RuleDetailsAdapter.adapt(userSeverity) : null, RuleDetailsAdapter.adapt(serverIssue.getType()), isOnNewCode)); - } else { - var localOnlyIssue = result.getRight(); - var resolution = localOnlyIssue.getResolution(); - return Either.forRight( - new LocalOnlyIssueDto(localOnlyIssue.getId(), resolution == null ? null : ResolutionStatus.valueOf(resolution.getStatus().name()))); - } - }).collect(Collectors.toList()); - return Map.entry(ideRelativePath, matches); - }).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); - } - - private void refreshServerIssues(SonarLintCancelMonitor cancelMonitor, Binding binding, String activeBranch, - Map> clientTrackedIssuesByIdeRelativePath, FilePathTranslation translation) { - var serverFileRelativePaths = clientTrackedIssuesByIdeRelativePath.keySet() - .stream().map(translation::ideToServerPath).collect(Collectors.toSet()); - var downloadAllIssuesAtOnce = serverFileRelativePaths.size() > FETCH_ALL_ISSUES_THRESHOLD; - var fetchTasks = new LinkedList>(); - if (downloadAllIssuesAtOnce) { - fetchTasks.add(CompletableFuture.runAsync(() -> issueSynchronizationService.fetchProjectIssues(binding, activeBranch, cancelMonitor), executorService)); - } else { - fetchTasks.addAll(serverFileRelativePaths.stream() - .map(serverFileRelativePath -> CompletableFuture.runAsync(() -> issueSynchronizationService - .fetchFileIssues(binding, serverFileRelativePath, activeBranch, cancelMonitor), executorService)) - .collect(Collectors.toList())); - } - CompletableFuture.allOf(fetchTasks.toArray(new CompletableFuture[0])).join(); - } - - private List, LocalOnlyIssue>> matchIssues(Path serverRelativePath, List> serverIssues, - List localOnlyIssues, List clientTrackedIssues) { - var serverIssueMatcher = new IssueMatcher<>(new ClientTrackedFindingMatchingAttributeMapper(), new ServerIssueMatchingAttributesMapper()); - var serverMatchingResult = serverIssueMatcher.match(clientTrackedIssues, serverIssues); - var localIssueMatcher = new IssueMatcher<>(new ClientTrackedFindingMatchingAttributeMapper(), new LocalOnlyIssueMatchingAttributesMapper()); - var localMatchingResult = localIssueMatcher.match(clientTrackedIssues, localOnlyIssues); - var matches = clientTrackedIssues.stream()., LocalOnlyIssue>>map(clientTrackedIssue -> { - var matchToServer = serverMatchingResult.getMatch(clientTrackedIssue); - if (matchToServer != null) { - return Either.forLeft(matchToServer); - } else { - var matchToLocal = localMatchingResult.getMatch(clientTrackedIssue); - return Either.forRight(Objects.requireNonNullElseGet(matchToLocal, () -> newLocalOnlyIssue(serverRelativePath, clientTrackedIssue))); - } - }).collect(Collectors.toList()); - var localOnlyIssuesMatched = matches.stream().filter(Either::isRight).map(Either::getRight).collect(Collectors.toList()); - localOnlyIssueRepository.save(serverRelativePath, localOnlyIssuesMatched); - return matches; - } - - @NotNull - private static LocalOnlyIssue newLocalOnlyIssue(Path serverRelativePath, ClientTrackedFindingDto clientTrackedIssue) { - return new LocalOnlyIssue(UUID.randomUUID(), serverRelativePath, adapt(clientTrackedIssue.getTextRangeWithHash()), adapt(clientTrackedIssue.getLineWithHash()), - clientTrackedIssue.getRuleKey(), clientTrackedIssue.getMessage(), null); - } - - @CheckForNull - private static TextRangeWithHash adapt(@Nullable TextRangeWithHashDto textRange) { - return textRange == null ? null - : new TextRangeWithHash(textRange.getStartLine(), textRange.getStartLineOffset(), textRange.getEndLine(), textRange.getEndLineOffset(), textRange.getHash()); - } - - @CheckForNull - private static LineWithHash adapt(@Nullable LineWithHashDto line) { - return line == null ? null : new LineWithHash(line.getNumber(), line.getHash()); - } - - @PreDestroy - public void shutdown() { - if (!MoreExecutors.shutdownAndAwaitTermination(executorService, 1, TimeUnit.SECONDS)) { - LOG.warn("Unable to stop issue updater executor service in a timely manner"); - } - } - -} diff --git a/backend/core/src/main/java/org/sonarsource/sonarlint/core/tracking/SecurityHotspotMatchingService.java b/backend/core/src/main/java/org/sonarsource/sonarlint/core/tracking/SecurityHotspotMatchingService.java deleted file mode 100644 index aac86b8b2c..0000000000 --- a/backend/core/src/main/java/org/sonarsource/sonarlint/core/tracking/SecurityHotspotMatchingService.java +++ /dev/null @@ -1,251 +0,0 @@ -/* - * SonarLint Core - Implementation - * Copyright (C) 2016-2025 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.tracking; - -import com.google.common.util.concurrent.MoreExecutors; -import java.nio.file.Path; -import java.util.Collection; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.UUID; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; -import javax.annotation.PreDestroy; -import javax.inject.Named; -import javax.inject.Singleton; -import org.sonarsource.sonarlint.core.branch.SonarProjectBranchTrackingService; -import org.sonarsource.sonarlint.core.commons.Binding; -import org.sonarsource.sonarlint.core.commons.log.SonarLintLogger; -import org.sonarsource.sonarlint.core.commons.progress.SonarLintCancelMonitor; -import org.sonarsource.sonarlint.core.event.SonarServerEventReceivedEvent; -import org.sonarsource.sonarlint.core.file.FilePathTranslation; -import org.sonarsource.sonarlint.core.file.PathTranslationService; -import org.sonarsource.sonarlint.core.reporting.FindingReportingService; -import org.sonarsource.sonarlint.core.repository.config.ConfigurationRepository; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.hotspot.HotspotStatus; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.tracking.ClientTrackedFindingDto; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.tracking.LocalOnlySecurityHotspotDto; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.tracking.ServerMatchedSecurityHotspotDto; -import org.sonarsource.sonarlint.core.rpc.protocol.client.hotspot.RaisedHotspotDto; -import org.sonarsource.sonarlint.core.rpc.protocol.common.Either; -import org.sonarsource.sonarlint.core.serverapi.hotspot.ServerHotspot; -import org.sonarsource.sonarlint.core.serverapi.push.SecurityHotspotChangedEvent; -import org.sonarsource.sonarlint.core.serverapi.push.SecurityHotspotClosedEvent; -import org.sonarsource.sonarlint.core.serverapi.push.SecurityHotspotRaisedEvent; -import org.sonarsource.sonarlint.core.storage.StorageService; -import org.sonarsource.sonarlint.core.sync.HotspotSynchronizationService; -import org.sonarsource.sonarlint.core.tracking.matching.ClientTrackedFindingMatchingAttributeMapper; -import org.sonarsource.sonarlint.core.tracking.matching.IssueMatcher; -import org.sonarsource.sonarlint.core.tracking.matching.ServerHotspotMatchingAttributesMapper; -import org.springframework.context.event.EventListener; - -import static java.util.stream.Collectors.toMap; - -@Named -@Singleton -public class SecurityHotspotMatchingService { - private static final int FETCH_ALL_SECURITY_HOTSPOTS_THRESHOLD = 10; - private static final SonarLintLogger LOG = SonarLintLogger.get(); - private final ConfigurationRepository configurationRepository; - private final StorageService storageService; - private final SonarProjectBranchTrackingService branchTrackingService; - private final HotspotSynchronizationService hotspotSynchronizationService; - private final PathTranslationService pathTranslationService; - private final FindingReportingService findingReportingService; - private final ExecutorService executorService; - - public SecurityHotspotMatchingService(ConfigurationRepository configurationRepository, StorageService storageService, - SonarProjectBranchTrackingService branchTrackingService, HotspotSynchronizationService hotspotSynchronizationService, - PathTranslationService pathTranslationService, FindingReportingService findingReportingService) { - this.configurationRepository = configurationRepository; - this.storageService = storageService; - this.branchTrackingService = branchTrackingService; - this.hotspotSynchronizationService = hotspotSynchronizationService; - this.pathTranslationService = pathTranslationService; - this.findingReportingService = findingReportingService; - this.executorService = Executors.newSingleThreadExecutor(r -> new Thread(r, "sonarlint-server-tracking-hotspot-updater")); - } - - public Map>> matchWithServerSecurityHotspots(String configurationScopeId, - Map> clientTrackedHotspotsByIdeRelativePath, boolean shouldFetchHotspotsFromServer, SonarLintCancelMonitor cancelMonitor) { - var effectiveBindingOpt = configurationRepository.getEffectiveBinding(configurationScopeId); - var activeBranchOpt = branchTrackingService.awaitEffectiveSonarProjectBranch(configurationScopeId); - var translationOpt = pathTranslationService.getOrComputePathTranslation(configurationScopeId); - if (effectiveBindingOpt.isEmpty() || activeBranchOpt.isEmpty() || translationOpt.isEmpty()) { - return clientTrackedHotspotsByIdeRelativePath.entrySet().stream() - .map(e -> Map.entry(e.getKey(), e.getValue().stream() - .map(issue -> Either.forRight( - new LocalOnlySecurityHotspotDto(UUID.randomUUID()))) - .collect(Collectors.toList()))) - .collect(toMap(Map.Entry::getKey, Map.Entry::getValue)); - } - var binding = effectiveBindingOpt.get(); - var activeBranch = activeBranchOpt.get(); - if (shouldFetchHotspotsFromServer) { - refreshServerSecurityHotspots(cancelMonitor, binding, activeBranch, clientTrackedHotspotsByIdeRelativePath, translationOpt.get()); - } - var newCodeDefinition = storageService.binding(binding).newCodeDefinition().read(); - return clientTrackedHotspotsByIdeRelativePath.entrySet().stream().map(e -> { - var serverRelativePath = e.getKey(); - var serverHotspots = storageService.binding(binding).findings().loadHotspots(activeBranch, serverRelativePath); - var matches = matchSecurityHotspots(serverHotspots, e.getValue()) - .stream().map(result -> { - if (result.isLeft()) { - var serverSecurityHotspot = result.getLeft(); - var creationDate = serverSecurityHotspot.getCreationDate(); - var isOnNewCode = newCodeDefinition.map(definition -> definition.isOnNewCode(creationDate.toEpochMilli())).orElse(true); - return Either.forLeft( - new ServerMatchedSecurityHotspotDto(UUID.randomUUID(), serverSecurityHotspot.getKey(), creationDate.toEpochMilli(), - HotspotStatus.valueOf(serverSecurityHotspot.getStatus().name()), isOnNewCode)); - } else { - return Either.forRight(new LocalOnlySecurityHotspotDto(result.getRight().getId())); - } - }).collect(Collectors.toList()); - return Map.entry(serverRelativePath, matches); - }).collect(toMap(Map.Entry::getKey, Map.Entry::getValue)); - } - - private void refreshServerSecurityHotspots(SonarLintCancelMonitor cancelMonitor, Binding binding, String activeBranch, - Map> clientTrackedHotspotsByIdeRelativePath, FilePathTranslation translation) { - var serverFileRelativePaths = clientTrackedHotspotsByIdeRelativePath.keySet() - .stream().map(translation::ideToServerPath).collect(Collectors.toSet()); - var downloadAllSecurityHotspotsAtOnce = serverFileRelativePaths.size() > FETCH_ALL_SECURITY_HOTSPOTS_THRESHOLD; - var fetchTasks = new LinkedList>(); - if (downloadAllSecurityHotspotsAtOnce) { - fetchTasks.add(CompletableFuture.runAsync(() -> hotspotSynchronizationService.fetchProjectHotspots(binding, activeBranch, cancelMonitor), executorService)); - } else { - fetchTasks.addAll(serverFileRelativePaths.stream() - .map(serverFileRelativePath -> CompletableFuture - .runAsync(() -> hotspotSynchronizationService.fetchFileHotspots(binding, activeBranch, serverFileRelativePath, cancelMonitor), executorService)) - .collect(Collectors.toList())); - } - CompletableFuture.allOf(fetchTasks.toArray(new CompletableFuture[0])).join(); - } - - private static List> matchSecurityHotspots(Collection serverHotspots, - List clientTrackedHotspots) { - var matcher = new IssueMatcher<>(new ClientTrackedFindingMatchingAttributeMapper(), new ServerHotspotMatchingAttributesMapper()); - var matchingResult = matcher.match(clientTrackedHotspots, serverHotspots); - return clientTrackedHotspots.stream().>map(clientTrackedHotspot -> { - var match = matchingResult.getMatch(clientTrackedHotspot); - if (match != null) { - return Either.forLeft(match); - } else { - return Either.forRight(new LocalOnlySecurityHotspot(UUID.randomUUID())); - } - }).collect(Collectors.toList()); - } - - @EventListener - public void onServerEventReceived(SonarServerEventReceivedEvent event) { - var connectionId = event.getConnectionId(); - var serverEvent = event.getEvent(); - if (serverEvent instanceof SecurityHotspotChangedEvent) { - var hotspotChangedEvent = (SecurityHotspotChangedEvent) serverEvent; - updateStorage(connectionId, hotspotChangedEvent); - republishPreviouslyRaisedHotspots(connectionId, hotspotChangedEvent); - } else if (serverEvent instanceof SecurityHotspotClosedEvent) { - var hotspotClosedEvent = (SecurityHotspotClosedEvent) serverEvent; - updateStorage(connectionId, hotspotClosedEvent); - republishPreviouslyRaisedHotspots(connectionId, hotspotClosedEvent); - } else if (serverEvent instanceof SecurityHotspotRaisedEvent) { - var hotspotRaisedEvent = (SecurityHotspotRaisedEvent) serverEvent; - // We could try to match with an existing hotspot. But we don't do it because we don't invest in hotspots right now. - updateStorage(connectionId, hotspotRaisedEvent); - } - } - - private void updateStorage(String connectionId, SecurityHotspotRaisedEvent event) { - var hotspot = new ServerHotspot( - event.getHotspotKey(), - event.getRuleKey(), - event.getMainLocation().getMessage(), - event.getMainLocation().getFilePath(), - TaintVulnerabilityTrackingService.adapt(event.getMainLocation().getTextRange()), - event.getCreationDate(), - event.getStatus(), - event.getVulnerabilityProbability(), - null); - var projectKey = event.getProjectKey(); - storageService.connection(connectionId).project(projectKey).findings().insert(event.getBranch(), hotspot); - } - - private void updateStorage(String connectionId, SecurityHotspotClosedEvent event) { - var projectKey = event.getProjectKey(); - storageService.connection(connectionId).project(projectKey).findings().deleteHotspot(event.getHotspotKey()); - } - - private void updateStorage(String connectionId, SecurityHotspotChangedEvent event) { - var projectKey = event.getProjectKey(); - storageService.connection(connectionId).project(projectKey).findings().updateHotspot(event.getHotspotKey(), hotspot -> { - var status = event.getStatus(); - if (status != null) { - hotspot.setStatus(status); - } - var assignee = event.getAssignee(); - if (assignee != null) { - hotspot.setAssignee(assignee); - } - }); - } - - private void republishPreviouslyRaisedHotspots(String connectionId, SecurityHotspotChangedEvent event) { - var boundScopes = configurationRepository.getBoundScopesToConnectionAndSonarProject(connectionId, event.getProjectKey()); - boundScopes.forEach(scope -> { - var scopeId = scope.getConfigScopeId(); - findingReportingService.updateAndReportHotspots(scopeId, - raisedHotspotDto -> changedHotspotUpdater(raisedHotspotDto, event)); - }); - } - - private static RaisedHotspotDto changedHotspotUpdater(RaisedHotspotDto raisedHotspotDto, SecurityHotspotChangedEvent event) { - if (event.getHotspotKey().equals(raisedHotspotDto.getServerKey())) { - return raisedHotspotDto.builder().withHotspotStatus(HotspotStatus.valueOf(event.getStatus().name())).buildHotspot(); - } - return raisedHotspotDto; - } - - private void republishPreviouslyRaisedHotspots(String connectionId, SecurityHotspotClosedEvent event) { - var boundScopes = configurationRepository.getBoundScopesToConnectionAndSonarProject(connectionId, event.getProjectKey()); - boundScopes.forEach(scope -> { - var scopeId = scope.getConfigScopeId(); - findingReportingService.updateAndReportHotspots(scopeId, - raisedHotspotDto -> closedHotspotUpdater(raisedHotspotDto, event)); - }); - } - - private static RaisedHotspotDto closedHotspotUpdater(RaisedHotspotDto raisedHotspotDto, SecurityHotspotClosedEvent event) { - if (event.getHotspotKey().equals(raisedHotspotDto.getServerKey())) { - return null; - } - return raisedHotspotDto; - } - - @PreDestroy - public void shutdown() { - if (!MoreExecutors.shutdownAndAwaitTermination(executorService, 1, TimeUnit.SECONDS)) { - LOG.warn("Unable to stop hotspot updater executor service in a timely manner"); - } - } -} diff --git a/backend/core/src/main/java/org/sonarsource/sonarlint/core/tracking/matching/ClientTrackedFindingMatchingAttributeMapper.java b/backend/core/src/main/java/org/sonarsource/sonarlint/core/tracking/matching/ClientTrackedFindingMatchingAttributeMapper.java deleted file mode 100644 index c1dc38cdfa..0000000000 --- a/backend/core/src/main/java/org/sonarsource/sonarlint/core/tracking/matching/ClientTrackedFindingMatchingAttributeMapper.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * SonarLint Core - Implementation - * Copyright (C) 2016-2025 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.tracking.matching; - -import java.util.Optional; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.tracking.ClientTrackedFindingDto; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.tracking.LineWithHashDto; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.tracking.TextRangeWithHashDto; - -public class ClientTrackedFindingMatchingAttributeMapper implements MatchingAttributesMapper { - @Override - public String getRuleKey(ClientTrackedFindingDto issue) { - return issue.getRuleKey(); - } - - @Override - public Optional getLine(ClientTrackedFindingDto issue) { - var lineWithHash = issue.getLineWithHash(); - return Optional.ofNullable(lineWithHash).map(LineWithHashDto::getNumber); - } - - @Override - public Optional getTextRangeHash(ClientTrackedFindingDto issue) { - var issueRange = issue.getTextRangeWithHash(); - return Optional.ofNullable(issueRange).map(TextRangeWithHashDto::getHash); - } - - @Override - public Optional getLineHash(ClientTrackedFindingDto issue) { - var lineWithHash = issue.getLineWithHash(); - return Optional.ofNullable(lineWithHash).map(LineWithHashDto::getHash); - } - - @Override - public String getMessage(ClientTrackedFindingDto issue) { - return issue.getMessage(); - } - - @Override - public Optional getServerIssueKey(ClientTrackedFindingDto issue) { - return Optional.ofNullable(issue.getServerKey()); - } -} diff --git a/backend/rpc-impl/src/main/java/org/sonarsource/sonarlint/core/rpc/impl/AnalysisRpcServiceDelegate.java b/backend/rpc-impl/src/main/java/org/sonarsource/sonarlint/core/rpc/impl/AnalysisRpcServiceDelegate.java index 527e6cdb43..d738a367c6 100644 --- a/backend/rpc-impl/src/main/java/org/sonarsource/sonarlint/core/rpc/impl/AnalysisRpcServiceDelegate.java +++ b/backend/rpc-impl/src/main/java/org/sonarsource/sonarlint/core/rpc/impl/AnalysisRpcServiceDelegate.java @@ -34,7 +34,6 @@ import org.sonarsource.sonarlint.core.rpc.protocol.backend.analysis.AnalysisRpcService; import org.sonarsource.sonarlint.core.rpc.protocol.backend.analysis.AnalyzeFileListParams; import org.sonarsource.sonarlint.core.rpc.protocol.backend.analysis.AnalyzeFilesAndTrackParams; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.analysis.AnalyzeFilesParams; import org.sonarsource.sonarlint.core.rpc.protocol.backend.analysis.AnalyzeFilesResponse; import org.sonarsource.sonarlint.core.rpc.protocol.backend.analysis.AnalyzeFullProjectParams; import org.sonarsource.sonarlint.core.rpc.protocol.backend.analysis.AnalyzeOpenFilesParams; @@ -124,17 +123,6 @@ public CompletableFuture getAutoDetectedNodeJs() }); } - @Override - public CompletableFuture analyzeFiles(AnalyzeFilesParams params) { - var configurationScopeId = params.getConfigurationScopeId(); - return requestAsync(cancelChecker -> { - var analysisResults = getBean(AnalysisService.class) - .analyze(cancelChecker, params.getConfigurationScopeId(), params.getAnalysisId(), params.getFilesToAnalyze(), - params.getExtraProperties(), params.getStartTime(), false, false, false).join(); - return generateAnalyzeFilesResponse(analysisResults); - }, configurationScopeId); - } - @Override public CompletableFuture analyzeFilesAndTrack(AnalyzeFilesAndTrackParams params) { var configurationScopeId = params.getConfigurationScopeId(); diff --git a/backend/rpc-impl/src/main/java/org/sonarsource/sonarlint/core/rpc/impl/IssueTrackingRpcServiceDelegate.java b/backend/rpc-impl/src/main/java/org/sonarsource/sonarlint/core/rpc/impl/IssueTrackingRpcServiceDelegate.java deleted file mode 100644 index 034606d07d..0000000000 --- a/backend/rpc-impl/src/main/java/org/sonarsource/sonarlint/core/rpc/impl/IssueTrackingRpcServiceDelegate.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * SonarLint Core - RPC Implementation - * Copyright (C) 2016-2025 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.impl; - -import java.util.concurrent.CompletableFuture; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.tracking.IssueTrackingRpcService; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.tracking.TrackWithServerIssuesParams; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.tracking.TrackWithServerIssuesResponse; -import org.sonarsource.sonarlint.core.tracking.IssueMatchingService; - -public class IssueTrackingRpcServiceDelegate extends AbstractRpcServiceDelegate implements IssueTrackingRpcService { - public IssueTrackingRpcServiceDelegate(SonarLintRpcServerImpl server) { - super(server); - } - - @Override - public CompletableFuture trackWithServerIssues(TrackWithServerIssuesParams params) { - return requestAsync(cancelMonitor -> new TrackWithServerIssuesResponse(getBean(IssueMatchingService.class).trackWithServerIssues(params.getConfigurationScopeId(), - params.getClientTrackedIssuesByIdeRelativePath(), params.shouldFetchIssuesFromServer(), cancelMonitor)), params.getConfigurationScopeId()); - } -} diff --git a/backend/rpc-impl/src/main/java/org/sonarsource/sonarlint/core/rpc/impl/SecurityHotspotMatchingRpcServiceDelegate.java b/backend/rpc-impl/src/main/java/org/sonarsource/sonarlint/core/rpc/impl/SecurityHotspotMatchingRpcServiceDelegate.java deleted file mode 100644 index 5fc9c4298f..0000000000 --- a/backend/rpc-impl/src/main/java/org/sonarsource/sonarlint/core/rpc/impl/SecurityHotspotMatchingRpcServiceDelegate.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * SonarLint Core - RPC Implementation - * Copyright (C) 2016-2025 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.impl; - -import java.util.concurrent.CompletableFuture; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.tracking.MatchWithServerSecurityHotspotsParams; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.tracking.MatchWithServerSecurityHotspotsResponse; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.tracking.SecurityHotspotMatchingRpcService; -import org.sonarsource.sonarlint.core.tracking.SecurityHotspotMatchingService; - -public class SecurityHotspotMatchingRpcServiceDelegate extends AbstractRpcServiceDelegate implements SecurityHotspotMatchingRpcService { - - public SecurityHotspotMatchingRpcServiceDelegate(SonarLintRpcServerImpl server) { - super(server); - } - - @Override - public CompletableFuture matchWithServerSecurityHotspots(MatchWithServerSecurityHotspotsParams params) { - return requestAsync( - cancelMonitor -> new MatchWithServerSecurityHotspotsResponse(getBean(SecurityHotspotMatchingService.class).matchWithServerSecurityHotspots(params.getConfigurationScopeId(), - params.getClientTrackedHotspotsByIdeRelativePath(), params.shouldFetchHotspotsFromServer(), cancelMonitor)), - params.getConfigurationScopeId()); - } -} diff --git a/backend/rpc-impl/src/main/java/org/sonarsource/sonarlint/core/rpc/impl/SonarLintRpcServerImpl.java b/backend/rpc-impl/src/main/java/org/sonarsource/sonarlint/core/rpc/impl/SonarLintRpcServerImpl.java index f6715a9c40..14122feab5 100644 --- a/backend/rpc-impl/src/main/java/org/sonarsource/sonarlint/core/rpc/impl/SonarLintRpcServerImpl.java +++ b/backend/rpc-impl/src/main/java/org/sonarsource/sonarlint/core/rpc/impl/SonarLintRpcServerImpl.java @@ -63,8 +63,6 @@ import org.sonarsource.sonarlint.core.rpc.protocol.backend.newcode.NewCodeRpcService; import org.sonarsource.sonarlint.core.rpc.protocol.backend.rules.RulesRpcService; import org.sonarsource.sonarlint.core.rpc.protocol.backend.telemetry.TelemetryRpcService; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.tracking.IssueTrackingRpcService; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.tracking.SecurityHotspotMatchingRpcService; import org.sonarsource.sonarlint.core.rpc.protocol.backend.tracking.TaintVulnerabilityTrackingRpcService; import org.sonarsource.sonarlint.core.spring.SpringApplicationContextInitializer; import org.sonarsource.sonarlint.core.storage.StorageService; @@ -214,16 +212,6 @@ public IssueRpcService getIssueService() { return new IssueRpcServiceDelegate(this); } - @Override - public IssueTrackingRpcService getIssueTrackingService() { - return new IssueTrackingRpcServiceDelegate(this); - } - - @Override - public SecurityHotspotMatchingRpcService getSecurityHotspotMatchingService() { - return new SecurityHotspotMatchingRpcServiceDelegate(this); - } - @Override public NewCodeRpcService getNewCodeService() { return new NewCodeRpcServiceDelegate(this); diff --git a/client/rpc-java-client/src/main/java/org/sonarsource/sonarlint/core/rpc/client/SonarLintRpcClientDelegate.java b/client/rpc-java-client/src/main/java/org/sonarsource/sonarlint/core/rpc/client/SonarLintRpcClientDelegate.java index 85b0f7c177..26d5adbb41 100644 --- a/client/rpc-java-client/src/main/java/org/sonarsource/sonarlint/core/rpc/client/SonarLintRpcClientDelegate.java +++ b/client/rpc-java-client/src/main/java/org/sonarsource/sonarlint/core/rpc/client/SonarLintRpcClientDelegate.java @@ -31,10 +31,8 @@ import javax.annotation.CheckForNull; import javax.annotation.Nullable; import org.sonarsource.sonarlint.core.rpc.protocol.SonarLintRpcClient; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.analysis.AnalyzeFilesAndTrackParams; import org.sonarsource.sonarlint.core.rpc.protocol.backend.config.binding.BindingSuggestionDto; import org.sonarsource.sonarlint.core.rpc.protocol.backend.tracking.TaintVulnerabilityDto; -import org.sonarsource.sonarlint.core.rpc.protocol.client.analysis.RawIssueDto; import org.sonarsource.sonarlint.core.rpc.protocol.client.binding.AssistBindingParams; import org.sonarsource.sonarlint.core.rpc.protocol.client.binding.AssistBindingResponse; import org.sonarsource.sonarlint.core.rpc.protocol.client.binding.NoBindingSuggestionFoundParams; @@ -188,14 +186,6 @@ default Path getBaseDir(String configurationScopeId) throws ConfigScopeNotFoundE void didChangeAnalysisReadiness(Set configurationScopeIds, boolean areReadyForAnalysis); - /** - * @deprecated since 10.2, please implement raiseIssues and raiseHotspots instead. - * See {@link org.sonarsource.sonarlint.core.rpc.protocol.backend.analysis.AnalysisRpcService#analyzeFilesAndTrack(AnalyzeFilesAndTrackParams)} - */ - @Deprecated(since = "10.2") - default void didRaiseIssue(String configurationScopeId, UUID analysisId, RawIssueDto rawIssue) { - } - default void raiseIssues(String configurationScopeId, Map> issuesByFileUri, boolean isIntermediatePublication, @Nullable UUID analysisId) { } diff --git a/client/rpc-java-client/src/main/java/org/sonarsource/sonarlint/core/rpc/client/SonarLintRpcClientImpl.java b/client/rpc-java-client/src/main/java/org/sonarsource/sonarlint/core/rpc/client/SonarLintRpcClientImpl.java index a3607c2517..d248aedcf9 100644 --- a/client/rpc-java-client/src/main/java/org/sonarsource/sonarlint/core/rpc/client/SonarLintRpcClientImpl.java +++ b/client/rpc-java-client/src/main/java/org/sonarsource/sonarlint/core/rpc/client/SonarLintRpcClientImpl.java @@ -38,7 +38,6 @@ import org.sonarsource.sonarlint.core.rpc.protocol.client.OpenUrlInBrowserParams; import org.sonarsource.sonarlint.core.rpc.protocol.client.analysis.DidChangeAnalysisReadinessParams; import org.sonarsource.sonarlint.core.rpc.protocol.client.analysis.DidDetectSecretParams; -import org.sonarsource.sonarlint.core.rpc.protocol.client.analysis.DidRaiseIssueParams; import org.sonarsource.sonarlint.core.rpc.protocol.client.analysis.GetFileExclusionsParams; import org.sonarsource.sonarlint.core.rpc.protocol.client.analysis.GetFileExclusionsResponse; import org.sonarsource.sonarlint.core.rpc.protocol.client.analysis.GetInferredAnalysisPropertiesParams; @@ -372,11 +371,6 @@ public void didChangeAnalysisReadiness(DidChangeAnalysisReadinessParams params) notify(() -> delegate.didChangeAnalysisReadiness(params.getConfigurationScopeIds(), params.areReadyForAnalysis())); } - @Override - public void didRaiseIssue(DidRaiseIssueParams params) { - notify(() -> delegate.didRaiseIssue(params.getConfigurationScopeId(), params.getAnalysisId(), params.getRawIssue())); - } - @Override public void raiseIssues(RaiseIssuesParams params) { notify(() -> delegate.raiseIssues(params.getConfigurationScopeId(), params.getIssuesByFileUri(), params.isIntermediatePublication(), params.getAnalysisId())); diff --git a/its/tests/src/test/java/its/AbstractConnectedTests.java b/its/tests/src/test/java/its/AbstractConnectedTests.java index d3185a80b6..03d06396af 100644 --- a/its/tests/src/test/java/its/AbstractConnectedTests.java +++ b/its/tests/src/test/java/its/AbstractConnectedTests.java @@ -160,6 +160,16 @@ protected static void changeIssueStatus(WsClient adminWsClient, String issueKey, } } + protected static void resolveHotspotAsSafe(WsClient adminWsClient, String hotspotKey) { + var request = new PostRequest("/api/hotspots/change_status") + .setParam("hotspot", hotspotKey) + .setParam("status", "REVIEWED") + .setParam("resolution", "SAFE"); + try (var response = adminWsClient.wsConnector().call(request)) { + assertTrue(response.isSuccessful(), "Unable to resolve hotspot"); + } + } + protected static void provisionProject(Orchestrator orchestrator, String projectKey, String projectName) { orchestrator.getServer() .newHttpCall("/api/projects/create") diff --git a/its/tests/src/test/java/its/MockSonarLintRpcClientDelegate.java b/its/tests/src/test/java/its/MockSonarLintRpcClientDelegate.java index 4d650fa3f5..c303ac4435 100644 --- a/its/tests/src/test/java/its/MockSonarLintRpcClientDelegate.java +++ b/its/tests/src/test/java/its/MockSonarLintRpcClientDelegate.java @@ -21,20 +21,20 @@ import java.net.URI; import java.net.URL; -import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; import java.util.concurrent.CancellationException; +import java.util.stream.Collectors; +import org.jetbrains.annotations.Nullable; import org.sonarsource.sonarlint.core.rpc.client.ConfigScopeNotFoundException; import org.sonarsource.sonarlint.core.rpc.client.ConnectionNotFoundException; import org.sonarsource.sonarlint.core.rpc.client.SonarLintCancelChecker; import org.sonarsource.sonarlint.core.rpc.client.SonarLintRpcClientDelegate; import org.sonarsource.sonarlint.core.rpc.protocol.backend.config.binding.BindingSuggestionDto; import org.sonarsource.sonarlint.core.rpc.protocol.backend.tracking.TaintVulnerabilityDto; -import org.sonarsource.sonarlint.core.rpc.protocol.client.analysis.RawIssueDto; import org.sonarsource.sonarlint.core.rpc.protocol.client.binding.AssistBindingParams; import org.sonarsource.sonarlint.core.rpc.protocol.client.binding.AssistBindingResponse; import org.sonarsource.sonarlint.core.rpc.protocol.client.binding.NoBindingSuggestionFoundParams; @@ -44,10 +44,12 @@ import org.sonarsource.sonarlint.core.rpc.protocol.client.event.DidReceiveServerHotspotEvent; import org.sonarsource.sonarlint.core.rpc.protocol.client.fix.FixSuggestionDto; import org.sonarsource.sonarlint.core.rpc.protocol.client.hotspot.HotspotDetailsDto; +import org.sonarsource.sonarlint.core.rpc.protocol.client.hotspot.RaisedHotspotDto; import org.sonarsource.sonarlint.core.rpc.protocol.client.http.GetProxyPasswordAuthenticationResponse; import org.sonarsource.sonarlint.core.rpc.protocol.client.http.ProxyDto; import org.sonarsource.sonarlint.core.rpc.protocol.client.http.X509CertificateDto; import org.sonarsource.sonarlint.core.rpc.protocol.client.issue.IssueDetailsDto; +import org.sonarsource.sonarlint.core.rpc.protocol.client.issue.RaisedIssueDto; import org.sonarsource.sonarlint.core.rpc.protocol.client.log.LogParams; import org.sonarsource.sonarlint.core.rpc.protocol.client.message.MessageType; import org.sonarsource.sonarlint.core.rpc.protocol.client.message.ShowSoonUnsupportedMessageParams; @@ -62,20 +64,29 @@ public class MockSonarLintRpcClientDelegate implements SonarLintRpcClientDelegate { - private final Map> raisedIssues = new HashMap<>(); + private final Map>> raisedIssues = new HashMap<>(); + private final Map>> raisedHotspots = new HashMap<>(); - public List getRaisedIssues(String configurationScopeId) { + public Map> getRaisedIssues(String configurationScopeId) { var issues = raisedIssues.get(configurationScopeId); - return issues != null ? issues : List.of(); + return issues != null ? issues : Map.of(); } - public Map> getRaisedIssues() { + public Map> getRaisedHotspots(String configurationScopeId) { + var hotspots = raisedHotspots.get(configurationScopeId); + return hotspots != null ? hotspots : Map.of(); + } + + public Map>> getRaisedIssues() { return raisedIssues; } - @Override - public void didRaiseIssue(String configurationScopeId, UUID analysisId, RawIssueDto rawIssue) { - raisedIssues.computeIfAbsent(configurationScopeId, k -> new ArrayList<>()).add(rawIssue); + public List getRaisedIssuesAsList(String configurationScopeId) { + return raisedIssues.getOrDefault(configurationScopeId, new HashMap<>()).values().stream().flatMap(List::stream).collect(Collectors.toList()); + } + + public List getRaisedHotspotsAsList(String configurationScopeId) { + return raisedHotspots.getOrDefault(configurationScopeId, new HashMap<>()).values().stream().flatMap(List::stream).collect(Collectors.toList()); } @Override @@ -224,8 +235,19 @@ public void didChangeAnalysisReadiness(Set configurationScopeIds, boolea } + @Override + public void raiseIssues(String configurationScopeId, Map> issuesByFileUri, boolean isIntermediatePublication, @Nullable UUID analysisId) { + raisedIssues.computeIfAbsent(configurationScopeId, k -> new HashMap<>()).putAll(issuesByFileUri); + } + + @Override + public void raiseHotspots(String configurationScopeId, Map> hotspotsByFileUri, boolean isIntermediatePublication, @Nullable UUID analysisId) { + raisedHotspots.computeIfAbsent(configurationScopeId, k -> new HashMap<>()).putAll(hotspotsByFileUri); + } + public void clear() { raisedIssues.clear(); + raisedHotspots.clear(); } } diff --git a/its/tests/src/test/java/its/SonarCloudTests.java b/its/tests/src/test/java/its/SonarCloudTests.java index 4442f7c78b..70c350274c 100644 --- a/its/tests/src/test/java/its/SonarCloudTests.java +++ b/its/tests/src/test/java/its/SonarCloudTests.java @@ -72,7 +72,7 @@ import org.sonarsource.sonarlint.core.rpc.client.SonarLintRpcClientDelegate; import org.sonarsource.sonarlint.core.rpc.impl.BackendJsonRpcLauncher; import org.sonarsource.sonarlint.core.rpc.protocol.SonarLintRpcServer; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.analysis.AnalyzeFilesParams; +import org.sonarsource.sonarlint.core.rpc.protocol.backend.analysis.AnalyzeFilesAndTrackParams; import org.sonarsource.sonarlint.core.rpc.protocol.backend.analysis.ShouldUseEnterpriseCSharpAnalyzerParams; import org.sonarsource.sonarlint.core.rpc.protocol.backend.branch.GetMatchedSonarProjectBranchParams; import org.sonarsource.sonarlint.core.rpc.protocol.backend.config.binding.BindingConfigurationDto; @@ -94,11 +94,9 @@ import org.sonarsource.sonarlint.core.rpc.protocol.backend.initialize.InitializeParams; import org.sonarsource.sonarlint.core.rpc.protocol.backend.initialize.SonarCloudAlternativeEnvironmentDto; import org.sonarsource.sonarlint.core.rpc.protocol.backend.rules.GetEffectiveRuleDetailsParams; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.tracking.ClientTrackedFindingDto; import org.sonarsource.sonarlint.core.rpc.protocol.backend.tracking.ListAllParams; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.tracking.MatchWithServerSecurityHotspotsParams; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.tracking.TextRangeWithHashDto; -import org.sonarsource.sonarlint.core.rpc.protocol.client.analysis.RawIssueDto; +import org.sonarsource.sonarlint.core.rpc.protocol.client.hotspot.RaisedHotspotDto; +import org.sonarsource.sonarlint.core.rpc.protocol.client.issue.RaisedIssueDto; import org.sonarsource.sonarlint.core.rpc.protocol.client.log.LogParams; import org.sonarsource.sonarlint.core.rpc.protocol.common.CleanCodeAttribute; import org.sonarsource.sonarlint.core.rpc.protocol.common.ClientFileDto; @@ -332,7 +330,7 @@ void analysisJavascript() { openBoundConfigurationScope(configScopeId, projectKeyJs); waitForAnalysisToBeReady(configScopeId); - var issues = analyze(projectKeyJs, "src/Person.js", configScopeId); + var issues = analyzeAndGetIssues(projectKeyJs, "src/Person.js", configScopeId); assertThat(issues).hasSize(1); } @@ -347,7 +345,7 @@ void analysisPHP() { openBoundConfigurationScope(configScopeId, projectKeyPhp); waitForAnalysisToBeReady(configScopeId); - var issues = analyze(projectKeyPhp, "src/Math.php", configScopeId); + var issues = analyzeAndGetIssues(projectKeyPhp, "src/Math.php", configScopeId); assertThat(issues).hasSize(1); } @@ -362,7 +360,7 @@ void analysisPython() { openBoundConfigurationScope(configScopeId, projectKeyPython); waitForAnalysisToBeReady(configScopeId); - var issues = analyze(projectKeyPython, "src/hello.py", configScopeId); + var issues = analyzeAndGetIssues(projectKeyPython, "src/hello.py", configScopeId); assertThat(issues).hasSize(1); } @@ -377,7 +375,7 @@ void analysisWeb() { openBoundConfigurationScope(configScopeId, projectKey); waitForAnalysisToBeReady(configScopeId); - var issues = analyze(projectKey, "src/file.html", configScopeId); + var issues = analyzeAndGetIssues(projectKey, "src/file.html", configScopeId); assertThat(issues).hasSize(1); } @@ -386,7 +384,7 @@ void analysisWeb() { void analysisUseConfiguration() { var configScopeId = "analysisUseConfiguration"; openUnboundConfigurationScope(configScopeId); - var issues = analyze(PROJECT_KEY_JAVA, "src/main/java/foo/Foo.java", configScopeId, + var issues = analyzeAndGetIssues(PROJECT_KEY_JAVA, "src/main/java/foo/Foo.java", configScopeId, "sonar.java.binaries", new File("projects/sample-java/target/classes").getAbsolutePath()); assertThat(issues).hasSize(2); @@ -397,7 +395,7 @@ void analysisUseConfiguration() { backend.getConfigurationService().didUpdateBinding(new DidUpdateBindingParams(configScopeId, new BindingConfigurationDto(CONNECTION_ID, projectKey(PROJECT_KEY_JAVA), true))); waitForAnalysisToBeReady(configScopeId); - issues = analyze(PROJECT_KEY_JAVA, "src/main/java/foo/Foo.java", configScopeId, + issues = analyzeAndGetIssues(PROJECT_KEY_JAVA, "src/main/java/foo/Foo.java", configScopeId, "sonar.java.binaries", new File("projects/sample-java/target/classes").getAbsolutePath()); assertThat(issues).isEmpty(); } finally { @@ -435,7 +433,7 @@ void analysisRuby() { openBoundConfigurationScope(configScopeId, projectKeyRuby); waitForAnalysisToBeReady(configScopeId); - var issues = analyze(projectKeyRuby, "src/hello.rb", configScopeId); + var issues = analyzeAndGetIssues(projectKeyRuby, "src/hello.rb", configScopeId); assertThat(issues).hasSize(1); } @@ -450,7 +448,7 @@ void analysisKotlin() { openBoundConfigurationScope(configScopeId, projectKeyKotlin); waitForAnalysisToBeReady(configScopeId); - var issues = analyze(projectKeyKotlin, "src/hello.kt", configScopeId); + var issues = analyzeAndGetIssues(projectKeyKotlin, "src/hello.kt", configScopeId); assertThat(issues).hasSize(1); } @@ -465,7 +463,7 @@ void analysisScala() { openBoundConfigurationScope(configScopeId, projectKeyScala); waitForAnalysisToBeReady(configScopeId); - var issues = analyze(projectKeyScala, "src/Hello.scala", configScopeId); + var issues = analyzeAndGetIssues(projectKeyScala, "src/Hello.scala", configScopeId); assertThat(issues).hasSize(1); } @@ -480,7 +478,7 @@ void analysisXml() { openBoundConfigurationScope(configScopeId, projectKeyXml); waitForAnalysisToBeReady(configScopeId); - var issues = analyze(projectKeyXml, "src/foo.xml", configScopeId); + var issues = analyzeAndGetIssues(projectKeyXml, "src/foo.xml", configScopeId); assertThat(issues).hasSize(1); } @@ -524,9 +522,9 @@ void reportHotspots() { openBoundConfigurationScope(configScopeId, PROJECT_KEY_JAVA_HOTSPOT); waitForAnalysisToBeReady(configScopeId); - var issues = analyze(PROJECT_KEY_JAVA_HOTSPOT, "src/main/java/foo/Foo.java", configScopeId); + var issues = analyzeAndGetHotspots(PROJECT_KEY_JAVA_HOTSPOT, "src/main/java/foo/Foo.java", configScopeId); assertThat(issues) - .extracting(RawIssueDto::getRuleKey, RawIssueDto::getType) + .extracting(RaisedHotspotDto::getRuleKey, RaisedHotspotDto::getType) .containsExactly(tuple("java:S4792", RuleType.SECURITY_HOTSPOT)); } @@ -541,26 +539,15 @@ void loadHotspotRuleDescription() throws Exception { } @Test - void shouldMatchServerSecurityHotspots() throws ExecutionException, InterruptedException { + void shouldMatchServerSecurityHotspots() { var configScopeId = "shouldMatchServerSecurityHotspots"; openBoundConfigurationScope(configScopeId, PROJECT_KEY_JAVA_HOTSPOT); waitForAnalysisToBeReady(configScopeId); - var textRangeWithHash = new TextRangeWithHashDto(9, 4, 9, 45, "qwer"); - var clientTrackedHotspotsByServerRelativePath = Map.of( - Path.of("src/main/java/foo/Foo.java"), - List.of(new ClientTrackedFindingDto(null, null, textRangeWithHash, null, "java:S4792", "Make sure that this logger's configuration is safe.")), - Path.of("src/main/java/bar/Bar.java"), List.of(new ClientTrackedFindingDto(null, null, textRangeWithHash, null, "java:S1234", "Some other rule"))); - var matchWithServerSecurityHotspotsResponse = backend.getSecurityHotspotMatchingService() - .matchWithServerSecurityHotspots(new MatchWithServerSecurityHotspotsParams(configScopeId, clientTrackedHotspotsByServerRelativePath, true)).get(); - assertThat(matchWithServerSecurityHotspotsResponse.getSecurityHotspotsByIdeRelativePath()).hasSize(2); - var fooSecurityHotspots = matchWithServerSecurityHotspotsResponse.getSecurityHotspotsByIdeRelativePath().get(Path.of("src/main/java/foo/Foo.java")); - assertThat(fooSecurityHotspots).hasSize(1); - assertThat(fooSecurityHotspots.get(0).isLeft()).isTrue(); - assertThat(fooSecurityHotspots.get(0).getLeft().getStatus()).isEqualTo(HotspotStatus.TO_REVIEW); - var barSecurityHotspots = matchWithServerSecurityHotspotsResponse.getSecurityHotspotsByIdeRelativePath().get(Path.of("src/main/java/bar/Bar.java")); - assertThat(barSecurityHotspots).hasSize(1); - assertThat(barSecurityHotspots.get(0).isRight()).isTrue(); + var raisedHotspots = analyzeAndGetHotspots(PROJECT_KEY_JAVA_HOTSPOT, "src/main/java/foo/Foo.java", configScopeId); + + assertThat(raisedHotspots).hasSize(1); + assertThat(raisedHotspots.get(0).getStatus()).isEqualTo(HotspotStatus.TO_REVIEW); } } @@ -712,7 +699,7 @@ public void log(LogParams params) { }; } - private static List analyze(String projectKey, String fileName, String configScopeId, String ... properties) { + private static List analyzeAndGetIssues(String projectKey, String fileName, String configScopeId, String ... properties) { final var baseDir = Paths.get("projects/" + projectKey).toAbsolutePath(); final var filePath = baseDir.resolve(fileName); backend.getFileService().didUpdateFileSystem(new DidUpdateFileSystemParams( @@ -721,14 +708,30 @@ private static List analyze(String projectKey, String fileName, Str List.of() )); - var analyzeResponse = backend.getAnalysisService().analyzeFiles( - new AnalyzeFilesParams(configScopeId, UUID.randomUUID(), List.of(filePath.toUri()), toMap(properties), System.currentTimeMillis()) + var analyzeResponse = backend.getAnalysisService().analyzeFilesAndTrack( + new AnalyzeFilesAndTrackParams(configScopeId, UUID.randomUUID(), List.of(filePath.toUri()), toMap(properties), true, System.currentTimeMillis()) ).join(); assertThat(analyzeResponse.getFailedAnalysisFiles()).isEmpty(); var raisedIssues = ((MockSonarLintRpcClientDelegate) client).getRaisedIssues(configScopeId); ((MockSonarLintRpcClientDelegate) client).getRaisedIssues().clear(); - return raisedIssues != null ? raisedIssues : List.of(); + return raisedIssues != null ? raisedIssues.values().stream().flatMap(List::stream).collect(Collectors.toList()) : List.of(); + } + + private static List analyzeAndGetHotspots(String projectKey, String fileName, String configScopeId, String ... properties) { + final var baseDir = Paths.get("projects/" + projectKey).toAbsolutePath(); + final var filePath = baseDir.resolve(fileName); + backend.getFileService().didUpdateFileSystem(new DidUpdateFileSystemParams(List.of(), + List.of(new ClientFileDto(filePath.toUri(), Path.of(fileName), configScopeId, false, null, filePath, null, null, true)), List.of())); + + var analyzeResponse = backend.getAnalysisService().analyzeFilesAndTrack( + new AnalyzeFilesAndTrackParams(configScopeId, UUID.randomUUID(), List.of(filePath.toUri()), toMap(properties), true, System.currentTimeMillis()) + ).join(); + + assertThat(analyzeResponse.getFailedAnalysisFiles()).isEmpty(); + var raisedHotspots = ((MockSonarLintRpcClientDelegate) client).getRaisedHotspots(configScopeId); + ((MockSonarLintRpcClientDelegate) client).getRaisedIssues().clear(); + return raisedHotspots != null ? raisedHotspots.values().stream().flatMap(List::stream).collect(Collectors.toList()) : List.of(); } } diff --git a/its/tests/src/test/java/its/SonarQubeCommunityEditionTests.java b/its/tests/src/test/java/its/SonarQubeCommunityEditionTests.java index 0fe685e866..75010d6a0b 100644 --- a/its/tests/src/test/java/its/SonarQubeCommunityEditionTests.java +++ b/its/tests/src/test/java/its/SonarQubeCommunityEditionTests.java @@ -31,15 +31,9 @@ import java.util.List; import java.util.Map; import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.function.Function; -import java.util.stream.Collectors; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.api.io.TempDir; @@ -51,17 +45,11 @@ import org.sonarsource.sonarlint.core.rpc.client.SonarLintRpcClientDelegate; import org.sonarsource.sonarlint.core.rpc.impl.BackendJsonRpcLauncher; import org.sonarsource.sonarlint.core.rpc.protocol.SonarLintRpcServer; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.config.binding.BindingConfigurationDto; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.config.scope.ConfigurationScopeDto; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.config.scope.DidAddConfigurationScopesParams; import org.sonarsource.sonarlint.core.rpc.protocol.backend.connection.auth.RevokeTokenParams; import org.sonarsource.sonarlint.core.rpc.protocol.backend.connection.config.SonarQubeConnectionConfigurationDto; import org.sonarsource.sonarlint.core.rpc.protocol.backend.initialize.FeatureFlagsDto; import org.sonarsource.sonarlint.core.rpc.protocol.backend.initialize.HttpConfigurationDto; import org.sonarsource.sonarlint.core.rpc.protocol.backend.initialize.InitializeParams; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.tracking.ClientTrackedFindingDto; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.tracking.TextRangeWithHashDto; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.tracking.TrackWithServerIssuesParams; import org.sonarsource.sonarlint.core.rpc.protocol.client.log.LogParams; import org.sonarsource.sonarlint.core.rpc.protocol.common.Either; import org.sonarsource.sonarlint.core.rpc.protocol.common.TokenDto; @@ -69,26 +57,22 @@ import static java.util.Collections.emptySet; import static org.assertj.core.api.Assertions.assertThat; -import static org.awaitility.Awaitility.await; import static org.sonarsource.sonarlint.core.rpc.protocol.common.Language.JAVA; -import static org.sonarsource.sonarlint.core.rpc.protocol.common.RuleType.CODE_SMELL; class SonarQubeCommunityEditionTests extends AbstractConnectedTests { private static final String CONNECTION_ID = "orchestrator"; @RegisterExtension - static OrchestratorExtension ORCHESTRATOR = OrchestratorUtils.defaultEnvBuilder() + static final OrchestratorExtension ORCHESTRATOR = OrchestratorUtils.defaultEnvBuilder() .addPlugin(FileLocation.of("../plugins/java-custom-rules/target/java-custom-rules-plugin.jar")) .setServerProperty("sonar.projectCreation.mainBranchName", MAIN_BRANCH_NAME) .build(); - @TempDir private static Path sonarUserHome; private static WsClient adminWsClient; private static SonarLintRpcServer backend; - private static final Map analysisReadinessByConfigScopeId = new ConcurrentHashMap<>(); private static BackendJsonRpcLauncher serverLauncher; @BeforeAll @@ -98,23 +82,23 @@ static void startBackend() throws IOException { var serverToClientOutputStream = new PipedOutputStream(); var serverToClientInputStream = new PipedInputStream(serverToClientOutputStream); - + SonarLintRpcClientDelegate client = newDummySonarLintClient(); serverLauncher = new BackendJsonRpcLauncher(clientToServerInputStream, serverToClientOutputStream); - var clientLauncher = new ClientJsonRpcLauncher(serverToClientInputStream, clientToServerOutputStream, newDummySonarLintClient()); + var clientLauncher = new ClientJsonRpcLauncher(serverToClientInputStream, clientToServerOutputStream, client); backend = clientLauncher.getServerProxy(); try { var featureFlags = new FeatureFlagsDto(true, true, true, false, true, true, false, true, false, true, false); var enabledLanguages = Set.of(JAVA); backend.initialize( - new InitializeParams(IT_CLIENT_INFO, - IT_TELEMETRY_ATTRIBUTES, HttpConfigurationDto.defaultConfig(), null, featureFlags, sonarUserHome.resolve("storage"), - sonarUserHome.resolve("work"), - Collections.emptySet(), Collections.emptyMap(), enabledLanguages, emptySet(), emptySet(), - List.of(new SonarQubeConnectionConfigurationDto(CONNECTION_ID, ORCHESTRATOR.getServer().getUrl(), true)), - Collections.emptyList(), - sonarUserHome.toString(), - Map.of(), false, null, false, null)) + new InitializeParams(IT_CLIENT_INFO, + IT_TELEMETRY_ATTRIBUTES, HttpConfigurationDto.defaultConfig(), null, featureFlags, sonarUserHome.resolve("storage"), + sonarUserHome.resolve("work"), + Collections.emptySet(), Collections.emptyMap(), enabledLanguages, emptySet(), emptySet(), + List.of(new SonarQubeConnectionConfigurationDto(CONNECTION_ID, ORCHESTRATOR.getServer().getUrl(), true)), + Collections.emptyList(), + sonarUserHome.toString(), + Map.of(), false, null, false, null)) .get(); } catch (Exception e) { throw new IllegalStateException("Cannot initialize the backend", e); @@ -127,11 +111,6 @@ static void createSonarLintUser() { adminWsClient.users().create(new CreateRequest().setLogin(SONARLINT_USER).setPassword(SONARLINT_PWD).setName("SonarLint")); } - @BeforeEach - void clearState() { - analysisReadinessByConfigScopeId.clear(); - } - @AfterAll static void stopBackend() throws ExecutionException, InterruptedException { serverLauncher.getServer().shutdown().get(); @@ -155,60 +134,7 @@ void test_revoke_token() { assertThat(searchResult.getUserTokensCount()).isZero(); } - @Nested - class StorageSynchronizationWithOnlyJavaEnabled { - - private static final String PROJECT_KEY_LANGUAGE_MIX = "sample-language-mix"; - - @BeforeEach - void prepare() { - provisionProject(ORCHESTRATOR, PROJECT_KEY_LANGUAGE_MIX, "Sample Language Mix"); - ORCHESTRATOR.getServer().restoreProfile(FileLocation.ofClasspath("/java-sonarlint.xml")); - ORCHESTRATOR.getServer().restoreProfile(FileLocation.ofClasspath("/python-sonarlint.xml")); - ORCHESTRATOR.getServer().associateProjectToQualityProfile(PROJECT_KEY_LANGUAGE_MIX, "java", "SonarLint IT Java"); - ORCHESTRATOR.getServer().associateProjectToQualityProfile(PROJECT_KEY_LANGUAGE_MIX, "py", "SonarLint IT Python"); - - // Build project to have bytecode and analyze - analyzeMavenProject(ORCHESTRATOR, "sample-language-mix", Map.of("sonar.projectKey", PROJECT_KEY_LANGUAGE_MIX)); - } - - @Test - void should_match_server_issues_of_enabled_languages() throws ExecutionException, InterruptedException { - var configScopeId = "should_match_server_issues_of_enabled_languages"; - backend.getConfigurationService().didAddConfigurationScopes(new DidAddConfigurationScopesParams( - List.of(new ConfigurationScopeDto(configScopeId, null, true, "sample-language-mix", new BindingConfigurationDto(CONNECTION_ID, PROJECT_KEY_LANGUAGE_MIX, - true))))); - waitForAnalysisToBeReady(configScopeId); - - var javaClientTrackedFindingDto = new ClientTrackedFindingDto(null, null, new TextRangeWithHashDto(14, 4, 14, 14, "hashedHash"), - null, "java:S106", "Replace this use of System.out by a logger."); - var pythonClientTrackedFindingDto = new ClientTrackedFindingDto(null, null, new TextRangeWithHashDto(2, 4, 2, 9, "hashedHash"), - null, "python:PrintStatementUsage", "Replace print statement by built-in function."); - var trackWithServerIssuesParams = new TrackWithServerIssuesParams(configScopeId, Map.of(Path.of("src/main/java/foo/Foo.java"), - List.of(javaClientTrackedFindingDto), Path.of("src/main/java/foo/main.py"), List.of(pythonClientTrackedFindingDto)), true); - var issuesByIdeRelativePath = backend.getIssueTrackingService().trackWithServerIssues(trackWithServerIssuesParams).get().getIssuesByIdeRelativePath(); - - var mainPyIssues = issuesByIdeRelativePath.get(Path.of("src/main/java/foo/main.py")); - assertThat(mainPyIssues).hasSize(1); - assertThat(mainPyIssues.get(0).isRight()).isTrue(); - - var fooJavaIssues = issuesByIdeRelativePath.get(Path.of("src/main/java/foo/Foo.java")); - assertThat(fooJavaIssues).hasSize(1); - - if (ORCHESTRATOR.getServer().version().isGreaterThanOrEquals(9, 5)) { - assertThat(fooJavaIssues.get(0).isLeft()).isTrue(); - assertThat(fooJavaIssues.get(0).getLeft().getType()).isEqualTo(CODE_SMELL); - } else { - assertThat(fooJavaIssues.get(0).isRight()).isTrue(); - } - } - } - - private static void waitForAnalysisToBeReady(String configScopeId) { - await().atMost(1, TimeUnit.MINUTES).untilAsserted(() -> assertThat(analysisReadinessByConfigScopeId).containsEntry(configScopeId, true)); - } - - //TODO Possibly add tests for a method which will replace SonarLintEngine.getPluginDetails() + // TODO Possibly add tests for a method which will replace SonarLintEngine.getPluginDetails() private static SonarLintRpcClientDelegate newDummySonarLintClient() { return new MockSonarLintRpcClientDelegate() { @@ -221,11 +147,6 @@ public Either getCredentials(String connectionId) return super.getCredentials(connectionId); } - @Override - public void didChangeAnalysisReadiness(Set configurationScopeIds, boolean areReadyForAnalysis) { - analysisReadinessByConfigScopeId.putAll(configurationScopeIds.stream().collect(Collectors.toMap(Function.identity(), k -> areReadyForAnalysis))); - } - @Override public void log(LogParams params) { rpcClientLogs.add(params); diff --git a/its/tests/src/test/java/its/SonarQubeDeveloperEditionTests.java b/its/tests/src/test/java/its/SonarQubeDeveloperEditionTests.java index 7c88108847..bf23cddbb6 100644 --- a/its/tests/src/test/java/its/SonarQubeDeveloperEditionTests.java +++ b/its/tests/src/test/java/its/SonarQubeDeveloperEditionTests.java @@ -79,7 +79,7 @@ import org.sonarsource.sonarlint.core.rpc.client.SonarLintRpcClientDelegate; import org.sonarsource.sonarlint.core.rpc.impl.BackendJsonRpcLauncher; import org.sonarsource.sonarlint.core.rpc.protocol.SonarLintRpcServer; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.analysis.AnalyzeFilesParams; +import org.sonarsource.sonarlint.core.rpc.protocol.backend.analysis.AnalyzeFilesAndTrackParams; import org.sonarsource.sonarlint.core.rpc.protocol.backend.branch.DidVcsRepositoryChangeParams; import org.sonarsource.sonarlint.core.rpc.protocol.backend.branch.GetMatchedSonarProjectBranchParams; import org.sonarsource.sonarlint.core.rpc.protocol.backend.config.binding.BindingConfigurationDto; @@ -98,14 +98,12 @@ import org.sonarsource.sonarlint.core.rpc.protocol.backend.initialize.InitializeParams; import org.sonarsource.sonarlint.core.rpc.protocol.backend.rules.GetEffectiveRuleDetailsParams; import org.sonarsource.sonarlint.core.rpc.protocol.backend.rules.RuleDescriptionTabDto; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.tracking.ClientTrackedFindingDto; import org.sonarsource.sonarlint.core.rpc.protocol.backend.tracking.ListAllParams; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.tracking.MatchWithServerSecurityHotspotsParams; import org.sonarsource.sonarlint.core.rpc.protocol.backend.tracking.TaintVulnerabilityDto; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.tracking.TextRangeWithHashDto; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.tracking.TrackWithServerIssuesParams; -import org.sonarsource.sonarlint.core.rpc.protocol.client.analysis.RawIssueDto; import org.sonarsource.sonarlint.core.rpc.protocol.client.hotspot.HotspotDetailsDto; +import org.sonarsource.sonarlint.core.rpc.protocol.client.hotspot.RaisedHotspotDto; +import org.sonarsource.sonarlint.core.rpc.protocol.client.issue.RaisedFindingDto; +import org.sonarsource.sonarlint.core.rpc.protocol.client.issue.RaisedIssueDto; import org.sonarsource.sonarlint.core.rpc.protocol.client.log.LogParams; import org.sonarsource.sonarlint.core.rpc.protocol.client.taint.vulnerability.DidChangeTaintVulnerabilitiesParams; import org.sonarsource.sonarlint.core.rpc.protocol.common.CleanCodeAttribute; @@ -190,7 +188,6 @@ static void createSonarLintUser() { private static final List didSynchronizeConfigurationScopes = new CopyOnWriteArrayList<>(); private static final Map analysisReadinessByConfigScopeId = new ConcurrentHashMap<>(); - @BeforeAll static void start() throws IOException { var clientToServerOutputStream = new PipedOutputStream(); @@ -208,16 +205,16 @@ static void start() throws IOException { var languages = Set.of(JAVA, GO, PHP, JS, PYTHON, HTML, RUBY, KOTLIN, SCALA, XML, COBOL, CLOUDFORMATION, DOCKER, KUBERNETES, TERRAFORM); var featureFlags = new FeatureFlagsDto(true, true, true, true, true, true, true, true, false, true, false); backend.initialize( - new InitializeParams(IT_CLIENT_INFO, IT_TELEMETRY_ATTRIBUTES, HttpConfigurationDto.defaultConfig(), null, featureFlags, - sonarUserHome.resolve("storage"), - sonarUserHome.resolve("work"), - emptySet(), PluginLocator.getEmbeddedPluginsByKeyForTests(), - languages, emptySet(), emptySet(), - List.of(new SonarQubeConnectionConfigurationDto(CONNECTION_ID, ORCHESTRATOR.getServer().getUrl(), false), - new SonarQubeConnectionConfigurationDto(CONNECTION_ID_WRONG_CREDENTIALS, ORCHESTRATOR.getServer().getUrl(), false)), - emptyList(), - sonarUserHome.toString(), - Map.of(), false, null, false, null)) + new InitializeParams(IT_CLIENT_INFO, IT_TELEMETRY_ATTRIBUTES, HttpConfigurationDto.defaultConfig(), null, featureFlags, + sonarUserHome.resolve("storage"), + sonarUserHome.resolve("work"), + emptySet(), PluginLocator.getEmbeddedPluginsByKeyForTests(), + languages, emptySet(), emptySet(), + List.of(new SonarQubeConnectionConfigurationDto(CONNECTION_ID, ORCHESTRATOR.getServer().getUrl(), false), + new SonarQubeConnectionConfigurationDto(CONNECTION_ID_WRONG_CREDENTIALS, ORCHESTRATOR.getServer().getUrl(), false)), + emptyList(), + sonarUserHome.toString(), + Map.of(), false, null, false, null)) .get(); } catch (Exception e) { throw new IllegalStateException("Cannot initialize the backend", e); @@ -241,7 +238,6 @@ static void stop() throws ExecutionException, InterruptedException { @Nested class AnalysisTests { - @BeforeEach void start() { Map globalProps = new HashMap<>(); @@ -403,7 +399,7 @@ void shouldRaiseIssuesOnAKubernetesProject() { openBoundConfigurationScope(configScopeId, projectKey, true); waitForAnalysisToBeReady(configScopeId); - var rawIssues = analyzeFile(configScopeId, "sample-kubernetes", "src/sample.yaml"); + var rawIssues = analyzeFileForHotspots(configScopeId, "sample-kubernetes", "src/sample.yaml"); assertThat(rawIssues).hasSize(1); } @@ -440,7 +436,7 @@ void shouldRaiseDataflowIssuesOnAPythonProject() { var rawIssues = analyzeFile(configScopeId, "sample-dbd", "src/hello.py"); assertThat(rawIssues) - .extracting(RawIssueDto::getRuleKey, RawIssueDto::getPrimaryMessage) + .extracting(RaisedIssueDto::getRuleKey, RaisedIssueDto::getPrimaryMessage) .containsOnly(tuple("pythonbugs:S6466", "Fix this access on a collection that may trigger an 'IndexError'.")); } @@ -455,9 +451,7 @@ void customSensorsShouldNotBeExecuted() { openBoundConfigurationScope(configScopeId, projectKey, true); waitForAnalysisToBeReady(configScopeId); - var rawIssues = analyzeFile(configScopeId, "sample-java", "src/main/java/foo/Foo.java"); - - assertThat(rawIssues).isEmpty(); + analyzeFileAndVerifyNoIssues(configScopeId, "sample-java", "src/main/java/foo/Foo.java"); } // TODO should be moved to a medium test @@ -561,8 +555,7 @@ void shouldHonorServerSideSettings() { await().untilAsserted(() -> assertThat(analysisReadinessByConfigScopeId).containsEntry(configScopeId, true)); await().untilAsserted(() -> assertThat(rpcClientLogs.stream().anyMatch(s -> s.getMessage().equals("Stored project analyzer configuration"))).isTrue()); - rawIssues = analyzeFile(configScopeId, "sample-java", "src/main/java/foo/Foo.java"); - assertThat(rawIssues).isEmpty(); + analyzeFileAndVerifyNoIssues(configScopeId, "sample-java", "src/main/java/foo/Foo.java"); rpcClientLogs.clear(); analysisReadinessByConfigScopeId.clear(); @@ -662,13 +655,12 @@ void shouldUpdateQualityProfileInLocalStorageWhenProfileChangedOnServer() { var rawIssues = analyzeFile(configScopeId, "sample-java", "src/main/java/foo/Foo.java"); assertThat(rawIssues) - .extracting(RawIssueDto::getRuleKey) + .extracting(RaisedIssueDto::getRuleKey) .containsOnly("java:S2325"); }); } @Test - @OnlyOnSonarQube(from = "9.9") void shouldUpdateIssueInLocalStorageWhenIssueResolvedOnServer() { var configScopeId = "shouldUpdateIssueInLocalStorageWhenIssueResolvedOnServer"; var projectKey = "projectKey-sse2"; @@ -685,16 +677,11 @@ void shouldUpdateIssueInLocalStorageWhenIssueResolvedOnServer() { resolveIssueAsWontFix(adminWsClient, issueKey); waitAtMost(1, TimeUnit.MINUTES).untilAsserted(() -> { - var issuesResponse = backend.getIssueTrackingService().trackWithServerIssues(new TrackWithServerIssuesParams(configScopeId, Map.of( - Path.of("src/main/java/foo/Foo.java"), - List.of(new ClientTrackedFindingDto(null, null, new TextRangeWithHashDto(14, 4, 14, 14, "hashedHash"), - null, "java:S106", "Replace this use of System.out by a logger."))), - true)).get(); - - var fooIssues = issuesResponse.getIssuesByIdeRelativePath().get(Path.of("src/main/java/foo/Foo.java")); - assertThat(fooIssues).hasSize(1); - assertThat(fooIssues.get(0).isLeft()).isTrue(); - assertThat(fooIssues.get(0).getLeft().isResolved()).isTrue(); + var fooIssues = analyzeFile(configScopeId, "sample-java", "src/main/java/foo/Foo.java"); + + assertThat(fooIssues) + .extracting(RaisedFindingDto::getRuleKey, RaisedFindingDto::isResolved) + .contains(tuple("java:S106", true)); }); } } @@ -738,38 +725,28 @@ void should_sync_branches_from_server() throws ExecutionException, InterruptedEx } @Test - void should_match_issues_from_branch() throws ExecutionException, InterruptedException { + void should_match_issues_from_branch() { var configScopeId = "should_match_issues_from_branch"; var projectKey = "sample-java"; var projectName = "my-sample-java"; var featureBranch = "branch-1.x"; - var ruleKey_s1172 = "java:S1172"; provisionProject(ORCHESTRATOR, projectKey, projectName); analyzeProject(projectKey, projectKey); analyzeProject(projectKey, projectKey, "sonar.branch.name", featureBranch); var issuesBranch = adminWsClient.issues().search(new SearchRequest().setBranch(featureBranch).setComponentKeys(List.of(projectKey))); - var issue_s1172 = issuesBranch.getIssuesList().stream().filter(issue -> issue.getRule().equals(ruleKey_s1172)).findFirst().orElseThrow(); - adminWsClient.issues().doTransition(new DoTransitionRequest().setIssue(issue_s1172.getKey()).setTransition("falsepositive")); + var issueToMarkFP = issuesBranch.getIssuesList().stream().filter(issue -> issue.getRule().equals("java:S1172")).findFirst().orElseThrow(); + adminWsClient.issues().doTransition(new DoTransitionRequest().setIssue(issueToMarkFP.getKey()).setTransition("falsepositive")); openBoundConfigurationScope(configScopeId, projectKey, true); waitForAnalysisToBeReady(configScopeId); - var clientTrackedDto_s100 = new ClientTrackedFindingDto(null, null, new TextRangeWithHashDto(4, 14, 4, 23, "hashedHash"), - null, "java:S100", "Rename this method name to match the regular expression '^[a-z][a-zA-Z0-9]*$'."); // this one is not matched but its on the server - var clientTrackedDto_s1172 = new ClientTrackedFindingDto(null, null, new TextRangeWithHashDto(8, 23, 8, 24, "hashedHash"), - null, ruleKey_s1172, "Remove this unused method parameter \"i\"."); - var clientTrackedDto_s106 = new ClientTrackedFindingDto(null, null, new TextRangeWithHashDto(14, 4, 14, 14, "hashedHash"), - null, "java:S106", "Replace this use of System.out by a logger."); // not resolved on both branches - var trackWithServerIssuesParams = new TrackWithServerIssuesParams(configScopeId, Map.of(Path.of("src/main/java/foo/Foo.java"), - List.of(clientTrackedDto_s100, clientTrackedDto_s1172, clientTrackedDto_s106)), true); - var issuesOnMainBranch = backend.getIssueTrackingService().trackWithServerIssues(trackWithServerIssuesParams).get().getIssuesByIdeRelativePath(); - - var fooIssuesMainBranch = issuesOnMainBranch.get(Path.of("src/main/java/foo/Foo.java")); - assertThat(fooIssuesMainBranch).hasSize(3); - assertThat(fooIssuesMainBranch.stream().filter(Either::isLeft).count()).isEqualTo(3); - assertThat(fooIssuesMainBranch.stream().filter(issue -> issue.getLeft().isResolved()).count()).isZero(); + var raisedIssues = analyzeFile(configScopeId, "sample-java", "src/main/java/foo/Foo.java"); + + assertThat(raisedIssues) + .extracting(RaisedIssueDto::getRuleKey, RaisedIssueDto::isResolved) + .contains(tuple("java:S1172", false)); didSynchronizeConfigurationScopes.clear(); matchedBranchNameForProject = featureBranch; @@ -780,12 +757,11 @@ void should_match_issues_from_branch() throws ExecutionException, InterruptedExc .isEqualTo(featureBranch)); waitForSync(configScopeId); - var issuesOnFeatureBranch = backend.getIssueTrackingService().trackWithServerIssues(trackWithServerIssuesParams).get().getIssuesByIdeRelativePath(); + raisedIssues = analyzeFile(configScopeId, "sample-java", "src/main/java/foo/Foo.java"); - var fooIssuesFeatureBranch = issuesOnFeatureBranch.get(Path.of("src/main/java/foo/Foo.java")); - assertThat(fooIssuesFeatureBranch).hasSize(3); - assertThat(fooIssuesFeatureBranch.stream().filter(Either::isLeft).count()).isEqualTo(3); - assertThat(fooIssuesFeatureBranch.stream().filter(issue -> issue.getLeft().isResolved()).count()).isEqualTo(1); + assertThat(raisedIssues) + .extracting(RaisedIssueDto::getRuleKey, RaisedIssueDto::isResolved) + .contains(tuple("java:S1172", true)); } } @@ -838,14 +814,16 @@ void shouldSyncTaintVulnerabilities() throws ExecutionException, InterruptedExce // In SQ 10.8+, old MAJOR severity maps to overridden MEDIUM impact assertThat(taintVulnerability.getImpacts()).containsExactly(entry(SoftwareQuality.SECURITY, ImpactSeverity.MEDIUM)); assertThat(taintVulnerability.getSeverityMode().isRight()).isTrue(); - assertThat(taintVulnerability.getSeverityMode().getRight().getImpacts().get(0)).extracting("softwareQuality", "impactSeverity").containsExactly(SoftwareQuality.SECURITY, ImpactSeverity.MEDIUM); + assertThat(taintVulnerability.getSeverityMode().getRight().getImpacts().get(0)).extracting("softwareQuality", "impactSeverity").containsExactly(SoftwareQuality.SECURITY, + ImpactSeverity.MEDIUM); assertThat(taintVulnerability.getSeverityMode().getRight().getCleanCodeAttribute()).isEqualTo(CleanCodeAttribute.COMPLETE); } else if (ORCHESTRATOR.getServer().version().isGreaterThanOrEquals(10, 2)) { assertThat(taintVulnerability.getCleanCodeAttribute()).isEqualTo(CleanCodeAttribute.COMPLETE); // In 10.2 <= SQ < 10.8, the impact severity is not overridden assertThat(taintVulnerability.getImpacts()).containsExactly(entry(SoftwareQuality.SECURITY, ImpactSeverity.HIGH)); assertThat(taintVulnerability.getSeverityMode().isRight()).isTrue(); - assertThat(taintVulnerability.getSeverityMode().getRight().getImpacts().get(0)).extracting("softwareQuality", "impactSeverity").containsExactly(SoftwareQuality.SECURITY, ImpactSeverity.HIGH); + assertThat(taintVulnerability.getSeverityMode().getRight().getImpacts().get(0)).extracting("softwareQuality", "impactSeverity").containsExactly(SoftwareQuality.SECURITY, + ImpactSeverity.HIGH); assertThat(taintVulnerability.getSeverityMode().getRight().getCleanCodeAttribute()).isEqualTo(CleanCodeAttribute.COMPLETE); } else { assertThat(taintVulnerability.getCleanCodeAttribute()).isNull(); @@ -1022,7 +1000,8 @@ void shouldShowHotspotWhenOpenedFromSonarQube() throws InvalidProtocolBufferExce private int requestOpenHotspotWithParams(String projectKey, String hotspotKey) { var request = HttpRequest.newBuilder() .GET() - .uri(URI.create("http://localhost:" + serverLauncher.getServer().getEmbeddedServerPort() + "/sonarlint/api/hotspots/show?server=" + URLEncoder.encode(ORCHESTRATOR.getServer().getUrl(), StandardCharsets.UTF_8) + "&project=" + .uri(URI.create("http://localhost:" + serverLauncher.getServer().getEmbeddedServerPort() + "/sonarlint/api/hotspots/show?server=" + + URLEncoder.encode(ORCHESTRATOR.getServer().getUrl(), StandardCharsets.UTF_8) + "&project=" + projectKey + "&hotspot=" + hotspotKey)) .build(); HttpResponse response; @@ -1054,11 +1033,11 @@ void reportHotspots() { waitForAnalysisToBeReady(configScopeId); await().untilAsserted(() -> assertThat(rpcClientLogs.stream().anyMatch(s -> s.getMessage().equals("Stored server info"))).isTrue()); - var rawIssues = analyzeFile(configScopeId, PROJECT_KEY_JAVA_HOTSPOT, "src/main/java/foo/Foo.java", "sonar.java.binaries", + var rawIssues = analyzeFileForHotspots(configScopeId, PROJECT_KEY_JAVA_HOTSPOT, "src/main/java/foo/Foo.java", "sonar.java.binaries", new File("projects/sample-java-hotspot/target/classes").getAbsolutePath()); assertThat(rawIssues) - .extracting(RawIssueDto::getRuleKey, RawIssueDto::getType) + .extracting(RaisedHotspotDto::getRuleKey, RaisedHotspotDto::getType) .containsExactly(tuple(javaRuleKey("S4792"), RuleType.SECURITY_HOTSPOT)); } @@ -1076,28 +1055,22 @@ void loadHotspotRuleDescription() throws Exception { } @Test - void shouldMatchServerSecurityHotspots() throws ExecutionException, InterruptedException { + void shouldMatchServerSecurityHotspots() throws InvalidProtocolBufferException { var configScopeId = "shouldMatchServerSecurityHotspots"; openBoundConfigurationScope(configScopeId, PROJECT_KEY_JAVA_HOTSPOT, true); waitForAnalysisToBeReady(configScopeId); - var textRangeWithHash = new TextRangeWithHashDto(9, 4, 9, 45, "qwer"); - var clientTrackedHotspotsByServerRelativePath = Map.of( - Path.of("src/main/java/foo/Foo.java"), - List.of(new ClientTrackedFindingDto(null, null, textRangeWithHash, null, "java:S4792", "Make sure that this logger's configuration is safe.")), - Path.of("src/main/java/bar/Bar.java"), List.of(new ClientTrackedFindingDto(null, null, textRangeWithHash, null, "java:S1234", "Some other rule"))); - - var matchWithServerSecurityHotspotsResponse = backend.getSecurityHotspotMatchingService() - .matchWithServerSecurityHotspots(new MatchWithServerSecurityHotspotsParams(configScopeId, clientTrackedHotspotsByServerRelativePath, true)).get(); - assertThat(matchWithServerSecurityHotspotsResponse.getSecurityHotspotsByIdeRelativePath()).hasSize(2); - var fooSecurityHotspots = matchWithServerSecurityHotspotsResponse.getSecurityHotspotsByIdeRelativePath().get(Path.of("src/main/java/foo/Foo.java")); - assertThat(fooSecurityHotspots).hasSize(1); - assertThat(fooSecurityHotspots.get(0).isLeft()).isTrue(); - assertThat(fooSecurityHotspots.get(0).getLeft().getStatus()).isEqualTo(HotspotStatus.TO_REVIEW); - var barSecurityHotspots = matchWithServerSecurityHotspotsResponse.getSecurityHotspotsByIdeRelativePath().get(Path.of("src/main/java/bar/Bar.java")); - assertThat(barSecurityHotspots).hasSize(1); - assertThat(barSecurityHotspots.get(0).isRight()).isTrue(); + var hotspotKey = getFirstHotspotKey(PROJECT_KEY_JAVA_HOTSPOT); + resolveHotspotAsSafe(adminWsClient, hotspotKey); + + waitAtMost(1, TimeUnit.MINUTES).untilAsserted(() -> { + // wait server event + var fooIssues = analyzeFileForHotspots(configScopeId, "sample-java-hotspot", "src/main/java/foo/Foo.java"); + assertThat(fooIssues) + .extracting(RaisedFindingDto::getRuleKey, RaisedHotspotDto::getStatus) + .contains(tuple("java:S4792", HotspotStatus.SAFE)); + }); } } @@ -1196,7 +1169,6 @@ void shouldReturnAllContextsWithOthersSelectedIfNoContextProvided() throws Execu openBoundConfigurationScope(configScopeId, projectKey, true); waitForAnalysisToBeReady(configScopeId); - var activeRuleDetailsResponse = backend.getRulesService().getEffectiveRuleDetails(new GetEffectiveRuleDetailsParams(configScopeId, "javasecurity:S2083", null)).get(); var description = activeRuleDetailsResponse.details().getDescription(); @@ -1405,23 +1377,51 @@ private void analyzeProject(String projectDirName, String projectKey, String... ORCHESTRATOR.executeBuild(scanner); } - private List analyzeFile(String configScopeId, String baseDir, String filePathStr, String... properties) { + private List analyzeFile(String configScopeId, String baseDir, String filePathStr, String... properties) { var filePath = Path.of("projects").resolve(baseDir).resolve(filePathStr); var fileUri = filePath.toUri(); backend.getFileService().didUpdateFileSystem(new DidUpdateFileSystemParams( List.of(new ClientFileDto(fileUri, Path.of(filePathStr), configScopeId, false, null, filePath.toAbsolutePath(), null, null, true)), List.of(), - List.of() - )); + List.of())); - var analyzeResponse = backend.getAnalysisService().analyzeFiles( - new AnalyzeFilesParams(configScopeId, UUID.randomUUID(), List.of(fileUri), toMap(properties), System.currentTimeMillis()) - ).join(); + var analyzeResponse = backend.getAnalysisService().analyzeFilesAndTrack( + new AnalyzeFilesAndTrackParams(configScopeId, UUID.randomUUID(), List.of(fileUri), toMap(properties), true, System.currentTimeMillis())).join(); assertThat(analyzeResponse.getFailedAnalysisFiles()).isEmpty(); + await().atMost(5, TimeUnit.SECONDS).untilAsserted(() -> assertThat(((MockSonarLintRpcClientDelegate) client).getRaisedIssuesAsList(configScopeId)).isNotEmpty()); var raisedIssues = ((MockSonarLintRpcClientDelegate) client).getRaisedIssues(configScopeId); ((MockSonarLintRpcClientDelegate) client).getRaisedIssues().clear(); - return raisedIssues != null ? raisedIssues : List.of(); + return raisedIssues != null ? raisedIssues.values().stream().flatMap(List::stream).collect(Collectors.toList()) : List.of(); + } + + private void analyzeFileAndVerifyNoIssues(String configScopeId, String baseDir, String filePathStr, String... properties) { + var filePath = Path.of("projects").resolve(baseDir).resolve(filePathStr); + var fileUri = filePath.toUri(); + backend.getFileService().didUpdateFileSystem(new DidUpdateFileSystemParams(List.of(), + List.of(new ClientFileDto(fileUri, Path.of(filePathStr), configScopeId, false, null, filePath.toAbsolutePath(), null, null, true)), List.of())); + + var analyzeResponse = backend.getAnalysisService().analyzeFilesAndTrack( + new AnalyzeFilesAndTrackParams(configScopeId, UUID.randomUUID(), List.of(fileUri), toMap(properties), true, System.currentTimeMillis())).join(); + + assertThat(analyzeResponse.getFailedAnalysisFiles()).isEmpty(); + await().during(5, TimeUnit.SECONDS).untilAsserted(() -> assertThat(((MockSonarLintRpcClientDelegate) client).getRaisedIssuesAsList(configScopeId)).isEmpty()); + } + + private List analyzeFileForHotspots(String configScopeId, String baseDir, String filePathStr, String... properties) { + var filePath = Path.of("projects").resolve(baseDir).resolve(filePathStr); + var fileUri = filePath.toUri(); + backend.getFileService().didUpdateFileSystem(new DidUpdateFileSystemParams(List.of(), + List.of(new ClientFileDto(fileUri, Path.of(filePathStr), configScopeId, false, null, filePath.toAbsolutePath(), null, null, true)), List.of())); + + var analyzeResponse = backend.getAnalysisService().analyzeFilesAndTrack( + new AnalyzeFilesAndTrackParams(configScopeId, UUID.randomUUID(), List.of(fileUri), toMap(properties), true, System.currentTimeMillis())).join(); + + assertThat(analyzeResponse.getFailedAnalysisFiles()).isEmpty(); + await().atMost(5, TimeUnit.SECONDS).untilAsserted(() -> assertThat(((MockSonarLintRpcClientDelegate) client).getRaisedHotspotsAsList(configScopeId)).isNotEmpty()); + var raisedHotspots = ((MockSonarLintRpcClientDelegate) client).getRaisedHotspots(configScopeId); + ((MockSonarLintRpcClientDelegate) client).clear(); + return raisedHotspots != null ? raisedHotspots.values().stream().flatMap(List::stream).collect(Collectors.toList()) : List.of(); } private static SonarLintRpcClientDelegate newDummySonarLintClient() { diff --git a/its/tests/src/test/java/its/SonarQubeEnterpriseEditionTests.java b/its/tests/src/test/java/its/SonarQubeEnterpriseEditionTests.java index 7e0636b5ce..096c272f57 100644 --- a/its/tests/src/test/java/its/SonarQubeEnterpriseEditionTests.java +++ b/its/tests/src/test/java/its/SonarQubeEnterpriseEditionTests.java @@ -57,9 +57,10 @@ import org.sonarsource.sonarlint.core.rpc.client.ConnectionNotFoundException; import org.sonarsource.sonarlint.core.rpc.client.SonarLintRpcClientDelegate; import org.sonarsource.sonarlint.core.rpc.impl.BackendJsonRpcLauncher; +import org.sonarsource.sonarlint.core.rpc.protocol.backend.analysis.AnalyzeFilesAndTrackParams; +import org.sonarsource.sonarlint.core.rpc.protocol.client.issue.RaisedIssueDto; import org.sonarsource.sonarlint.core.rpc.protocol.common.Either; import org.sonarsource.sonarlint.core.rpc.protocol.SonarLintRpcServer; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.analysis.AnalyzeFilesParams; import org.sonarsource.sonarlint.core.rpc.protocol.backend.config.binding.BindingConfigurationDto; import org.sonarsource.sonarlint.core.rpc.protocol.backend.config.scope.ConfigurationScopeDto; import org.sonarsource.sonarlint.core.rpc.protocol.backend.config.scope.DidAddConfigurationScopesParams; @@ -69,7 +70,6 @@ import org.sonarsource.sonarlint.core.rpc.protocol.backend.initialize.FeatureFlagsDto; import org.sonarsource.sonarlint.core.rpc.protocol.backend.initialize.HttpConfigurationDto; import org.sonarsource.sonarlint.core.rpc.protocol.backend.initialize.InitializeParams; -import org.sonarsource.sonarlint.core.rpc.protocol.client.analysis.RawIssueDto; import org.sonarsource.sonarlint.core.rpc.protocol.client.log.LogParams; import org.sonarsource.sonarlint.core.rpc.protocol.common.ClientFileDto; import org.sonarsource.sonarlint.core.rpc.protocol.common.TokenDto; @@ -214,7 +214,7 @@ void analysisC_old_build_wrapper_prop(@TempDir File buildWrapperOutput) throws E var rawIssues = analyzeFile(configScopeId, PROJECT_KEY_C, "src/file.c", "sonar.cfamily.build-wrapper-output", buildWrapperOutput.getAbsolutePath()); assertThat(rawIssues) - .extracting(RawIssueDto::getRuleKey) + .extracting(RaisedIssueDto::getRuleKey) .containsOnly("c:S3805", singlePointOfExitRuleKey); } @@ -245,7 +245,7 @@ void analysisC_new_prop() { var rawIssues = analyzeFile(configScopeId, PROJECT_KEY_C, "src/file.c", "sonar.cfamily.build-wrapper-content", buildWrapperContent); assertThat(rawIssues) - .extracting(RawIssueDto::getRuleKey) + .extracting(RaisedIssueDto::getRuleKey) .containsOnly("c:S3805", singlePointOfExitRuleKey); } @@ -299,7 +299,7 @@ void analysisCustomSecrets() { var rawIssues = analyzeFile(configScopeId, PROJECT_KEY_CUSTOM_SECRETS, "src/file.md"); assertThat(rawIssues) - .extracting(RawIssueDto::getRuleKey, RawIssueDto::getPrimaryMessage) + .extracting(RaisedIssueDto::getRuleKey, RaisedIssueDto::getPrimaryMessage) .containsOnly(tuple("secrets:custom_secret_rule", "User-specified secrets should not be disclosed.")); } } @@ -343,7 +343,7 @@ void analysisWithDeprecatedRuleKey() { var rawIssues = analyzeFile(configScopeId, PROJECT_KEY_C, "src/file.c", "sonar.cfamily.build-wrapper-content", buildWrapperContent); assertThat(rawIssues) - .extracting(RawIssueDto::getRuleKey) + .extracting(RaisedIssueDto::getRuleKey) .containsOnly("c:S3805", "c:S1005"); } } @@ -355,7 +355,7 @@ private static void bindProject(String configScopeId, String projectName, String await().atMost(30, SECONDS).untilAsserted(() -> assertThat(analysisReadinessByConfigScopeId).containsEntry(configScopeId, true)); } - private List analyzeFile(String configScopeId, String projectDir, String filePathStr, String... properties) { + private List analyzeFile(String configScopeId, String projectDir, String filePathStr, String... properties) { var filePath = Path.of("projects").resolve(projectDir).resolve(filePathStr); var fileUri = filePath.toUri(); backend.getFileService().didUpdateFileSystem(new DidUpdateFileSystemParams( @@ -364,13 +364,14 @@ private List analyzeFile(String configScopeId, String projectDir, S List.of() )); - var analyzeResponse = backend.getAnalysisService().analyzeFiles( - new AnalyzeFilesParams(configScopeId, UUID.randomUUID(), List.of(fileUri), toMap(properties), System.currentTimeMillis()) + var analyzeResponse = backend.getAnalysisService().analyzeFilesAndTrack( + new AnalyzeFilesAndTrackParams(configScopeId, UUID.randomUUID(), List.of(fileUri), toMap(properties), true, System.currentTimeMillis()) ).join(); assertThat(analyzeResponse.getFailedAnalysisFiles()).isEmpty(); var raisedIssues = ((MockSonarLintRpcClientDelegate) client).getRaisedIssues(configScopeId); - return raisedIssues != null ? raisedIssues : List.of(); + ((MockSonarLintRpcClientDelegate) client).getRaisedIssues().clear(); + return raisedIssues != null ? raisedIssues.values().stream().flatMap(List::stream).collect(Collectors.toList()) : List.of(); } private static void removeGroupPermission(String groupName, String permission) { diff --git a/its/tests/src/test/java/its/StandaloneTests.java b/its/tests/src/test/java/its/StandaloneTests.java index 494041e0b0..22eb9fe2df 100644 --- a/its/tests/src/test/java/its/StandaloneTests.java +++ b/its/tests/src/test/java/its/StandaloneTests.java @@ -37,6 +37,7 @@ import java.util.Set; import java.util.UUID; import java.util.concurrent.ExecutionException; +import java.util.stream.Collectors; import org.apache.commons.io.FileUtils; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; @@ -47,7 +48,7 @@ import org.sonarsource.sonarlint.core.rpc.client.SonarLintRpcClientDelegate; import org.sonarsource.sonarlint.core.rpc.impl.BackendJsonRpcLauncher; import org.sonarsource.sonarlint.core.rpc.protocol.SonarLintRpcServer; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.analysis.AnalyzeFilesParams; +import org.sonarsource.sonarlint.core.rpc.protocol.backend.analysis.AnalyzeFilesAndTrackParams; import org.sonarsource.sonarlint.core.rpc.protocol.backend.file.DidUpdateFileSystemParams; import org.sonarsource.sonarlint.core.rpc.protocol.backend.initialize.ClientConstantInfoDto; import org.sonarsource.sonarlint.core.rpc.protocol.backend.initialize.FeatureFlagsDto; @@ -59,7 +60,7 @@ import org.sonarsource.sonarlint.core.rpc.protocol.backend.rules.RuleParamType; import org.sonarsource.sonarlint.core.rpc.protocol.backend.rules.StandaloneRuleConfigDto; import org.sonarsource.sonarlint.core.rpc.protocol.backend.rules.UpdateStandaloneRulesConfigurationParams; -import org.sonarsource.sonarlint.core.rpc.protocol.client.analysis.RawIssueDto; +import org.sonarsource.sonarlint.core.rpc.protocol.client.issue.RaisedIssueDto; import org.sonarsource.sonarlint.core.rpc.protocol.client.log.LogParams; import org.sonarsource.sonarlint.core.rpc.protocol.common.ClientFileDto; @@ -145,18 +146,18 @@ private static void assertRuleHasParam(RuleDefinitionDto rule, String paramKey, @Test void globalExtension() throws Exception { - var inputFile = prepareInputFile("foo.glob", "foo", false); + prepareInputFile("foo.glob", "foo", false); var raisedIssues = analyzeFile(CONFIG_SCOPE_ID, baseDir.getAbsolutePath(), "foo.glob", "sonar.cobol.file.suffixes", "glob"); - assertThat(raisedIssues).extracting("ruleKey", "fileUri.path", "primaryMessage").containsOnly( - tuple("global:inc", inputFile.getPath(), "Issue number 0")); + assertThat(raisedIssues).extracting("ruleKey", "primaryMessage").containsOnly( + tuple("global:inc", "Issue number 0")); backend.getRulesService().updateStandaloneRulesConfiguration(new UpdateStandaloneRulesConfigurationParams( Map.of("global:inc", new StandaloneRuleConfigDto(true, Map.of("stringParam", "polop", "textParam", "", "multipleIntegersParam", "80,160", "unknown", "parameter"))))); raisedIssues = analyzeFile(CONFIG_SCOPE_ID, baseDir.getAbsolutePath(), "foo.glob", "sonar.cobol.file.suffixes", "glob"); - assertThat(raisedIssues).extracting("ruleKey", "fileUri.path", "primaryMessage").containsOnly( - tuple("global:inc", inputFile.getPath(), "Issue number 1")); + assertThat(raisedIssues).extracting("ruleKey", "primaryMessage").containsOnly( + tuple("global:inc", "Issue number 1")); } private ClientInputFile prepareInputFile(String relativePath, String content, final boolean isTest, Charset encoding) throws IOException { @@ -178,7 +179,7 @@ public void log(LogParams params) { }; } - private List analyzeFile(String configScopeId, String baseDir, String filePathStr, String... properties) { + private List analyzeFile(String configScopeId, String baseDir, String filePathStr, String... properties) { var filePath = Path.of("projects").resolve(baseDir).resolve(filePathStr); var fileUri = filePath.toUri(); backend.getFileService().didUpdateFileSystem(new DidUpdateFileSystemParams( @@ -187,8 +188,8 @@ private List analyzeFile(String configScopeId, String baseDir, Stri List.of() )); - var analyzeResponse = backend.getAnalysisService().analyzeFiles( - new AnalyzeFilesParams(configScopeId, UUID.randomUUID(), List.of(fileUri), toMap(properties), System.currentTimeMillis()) + var analyzeResponse = backend.getAnalysisService().analyzeFilesAndTrack( + new AnalyzeFilesAndTrackParams(configScopeId, UUID.randomUUID(), List.of(fileUri), toMap(properties), true, System.currentTimeMillis()) ).join(); assertThat(analyzeResponse.getFailedAnalysisFiles()).isEmpty(); @@ -196,6 +197,6 @@ private List analyzeFile(String configScopeId, String baseDir, Stri await().atMost(Duration.ofMillis(200)).untilAsserted(() -> assertThat(((MockSonarLintRpcClientDelegate) client).getRaisedIssues(configScopeId)).isNotEmpty()); var raisedIssues = ((MockSonarLintRpcClientDelegate) client).getRaisedIssues(configScopeId); ((MockSonarLintRpcClientDelegate) client).getRaisedIssues().clear(); - return raisedIssues != null ? raisedIssues : List.of(); + return raisedIssues != null ? raisedIssues.values().stream().flatMap(List::stream).collect(Collectors.toList()) : List.of(); } } diff --git a/medium-tests/src/test/java/mediumtest/ConnectedStorageProblemsMediumTests.java b/medium-tests/src/test/java/mediumtest/ConnectedStorageProblemsMediumTests.java index 0e79e85263..448264dad1 100644 --- a/medium-tests/src/test/java/mediumtest/ConnectedStorageProblemsMediumTests.java +++ b/medium-tests/src/test/java/mediumtest/ConnectedStorageProblemsMediumTests.java @@ -69,7 +69,6 @@ void corrupted_plugin_should_not_prevent_startup(SonarLintTestHarness harness, @ .withEnabledLanguageInStandaloneMode(org.sonarsource.sonarlint.core.rpc.protocol.common.Language.JAVA) .withEnabledLanguageInStandaloneMode(org.sonarsource.sonarlint.core.rpc.protocol.common.Language.JS).build(client); - backend.getAnalysisService().analyzeFilesAndTrack(new AnalyzeFilesAndTrackParams(CONFIG_SCOPE_ID, UUID.randomUUID(), List.of(inputFile.toUri()), Map.of(), false, Instant.now().toEpochMilli())).get(); diff --git a/medium-tests/src/test/java/mediumtest/analysis/AnalysisMediumTests.java b/medium-tests/src/test/java/mediumtest/analysis/AnalysisMediumTests.java index 99b90d3fa4..9a3b6cdf19 100644 --- a/medium-tests/src/test/java/mediumtest/analysis/AnalysisMediumTests.java +++ b/medium-tests/src/test/java/mediumtest/analysis/AnalysisMediumTests.java @@ -45,7 +45,6 @@ import org.sonarsource.sonarlint.core.commons.api.SonarLanguage; import org.sonarsource.sonarlint.core.commons.testutils.GitUtils; import org.sonarsource.sonarlint.core.rpc.protocol.backend.analysis.AnalyzeFilesAndTrackParams; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.analysis.AnalyzeFilesParams; import org.sonarsource.sonarlint.core.rpc.protocol.backend.analysis.DidChangeAnalysisPropertiesParams; import org.sonarsource.sonarlint.core.rpc.protocol.backend.analysis.DidChangePathToCompileCommandsParams; import org.sonarsource.sonarlint.core.rpc.protocol.backend.analysis.GetAnalysisConfigParams; @@ -57,7 +56,7 @@ import org.sonarsource.sonarlint.core.rpc.protocol.backend.file.DidOpenFileParams; import org.sonarsource.sonarlint.core.rpc.protocol.backend.file.DidUpdateFileSystemParams; import org.sonarsource.sonarlint.core.rpc.protocol.backend.rules.GetEffectiveRuleDetailsParams; -import org.sonarsource.sonarlint.core.rpc.protocol.client.analysis.RawIssueDto; +import org.sonarsource.sonarlint.core.rpc.protocol.client.issue.RaisedIssueDto; import org.sonarsource.sonarlint.core.rpc.protocol.client.plugin.DidSkipLoadingPluginParams; import org.sonarsource.sonarlint.core.rpc.protocol.client.progress.ProgressEndNotification; import org.sonarsource.sonarlint.core.rpc.protocol.client.progress.ReportProgressParams; @@ -70,6 +69,7 @@ import org.sonarsource.sonarlint.core.rpc.protocol.common.RuleType; import org.sonarsource.sonarlint.core.rpc.protocol.common.SoftwareQuality; import org.sonarsource.sonarlint.core.rpc.protocol.common.TextRangeDto; +import org.sonarsource.sonarlint.core.test.utils.SonarLintBackendFixture; import org.sonarsource.sonarlint.core.test.utils.junit5.SonarLintTest; import org.sonarsource.sonarlint.core.test.utils.junit5.SonarLintTestHarness; import utils.OnDiskTestClientInputFile; @@ -78,13 +78,10 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; import static org.junit.jupiter.api.Assumptions.assumeTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.refEq; -import static org.mockito.Mockito.never; import static org.mockito.Mockito.timeout; -import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static utils.AnalysisUtils.waitForRaisedIssues; @ExtendWith(LogTestStartAndEnd.class) class AnalysisMediumTests { @@ -112,10 +109,10 @@ void it_should_skip_analysis_if_no_file_provided(SonarLintTestHarness harness, @ var analysisId = UUID.randomUUID(); var result = backend.getAnalysisService() - .analyzeFiles(new AnalyzeFilesParams(CONFIG_SCOPE_ID, analysisId, List.of(tempDir.resolve("File.java").toUri()), Map.of(), System.currentTimeMillis())).join(); + .analyzeFilesAndTrack(new AnalyzeFilesAndTrackParams(CONFIG_SCOPE_ID, analysisId, List.of(tempDir.resolve("File.java").toUri()), Map.of(), false, System.currentTimeMillis())).join(); assertThat(result.getFailedAnalysisFiles()).isEmpty(); - verify(client, never()).didRaiseIssue(eq(CONFIG_SCOPE_ID), eq(analysisId), any()); + await().during(2, TimeUnit.SECONDS).untilAsserted(() -> assertThat(client.getRaisedIssuesForScopeIdAsList(CONFIG_SCOPE_ID)).isEmpty()); } @SonarLintTest @@ -186,24 +183,22 @@ void it_should_analyze_xml_file_in_standalone_mode(SonarLintTestHarness harness, .build(client); var analysisId = UUID.randomUUID(); - var result = backend.getAnalysisService().analyzeFiles(new AnalyzeFilesParams(CONFIG_SCOPE_ID, analysisId, List.of(fileUri), Map.of(), System.currentTimeMillis())).join(); + var result = backend.getAnalysisService() + .analyzeFilesAndTrack(new AnalyzeFilesAndTrackParams(CONFIG_SCOPE_ID, analysisId, List.of(fileUri), Map.of(), false, System.currentTimeMillis())).join(); assertThat(result.getFailedAnalysisFiles()).isEmpty(); - var rawIssueCaptor = ArgumentCaptor.forClass(RawIssueDto.class); - verify(client).didRaiseIssue(eq(CONFIG_SCOPE_ID), eq(analysisId), rawIssueCaptor.capture()); - var rawIssue = rawIssueCaptor.getValue(); - assertThat(rawIssue.getSeverity()).isEqualTo(IssueSeverity.MINOR); - assertThat(rawIssue.getType()).isEqualTo(RuleType.CODE_SMELL); - assertThat(rawIssue.getCleanCodeAttribute()).isEqualTo(CleanCodeAttribute.CONVENTIONAL); - assertThat(rawIssue.getImpacts()).isEqualTo(Map.of(SoftwareQuality.MAINTAINABILITY, ImpactSeverity.LOW)); - assertThat(rawIssue.getRuleKey()).isEqualTo("xml:S3421"); - assertThat(rawIssue.getPrimaryMessage()).isEqualTo("Replace \"pom.version\" with \"project.version\"."); - assertThat(rawIssue.getFileUri()).isEqualTo(fileUri); - assertThat(rawIssue.getFlows()).isEmpty(); - assertThat(rawIssue.getQuickFixes()).isEmpty(); - assertThat(rawIssue.getTextRange()).usingRecursiveComparison().isEqualTo(new TextRangeDto(6, 11, 6, 25)); - assertThat(rawIssue.getRuleDescriptionContextKey()).isNull(); - assertThat(rawIssue.getVulnerabilityProbability()).isNull(); + var raisedIssueDto = awaitRaisedIssuesNotification(client, CONFIG_SCOPE_ID).get(0); + assertThat(raisedIssueDto.getSeverity()).isEqualTo(IssueSeverity.MINOR); + assertThat(raisedIssueDto.getType()).isEqualTo(RuleType.CODE_SMELL); + assertThat(raisedIssueDto.getCleanCodeAttribute()).isEqualTo(CleanCodeAttribute.CONVENTIONAL); + assertThat(raisedIssueDto.getImpacts().get(0).getSoftwareQuality()).isEqualTo(SoftwareQuality.MAINTAINABILITY); + assertThat(raisedIssueDto.getImpacts().get(0).getImpactSeverity()).isEqualTo(ImpactSeverity.LOW); + assertThat(raisedIssueDto.getRuleKey()).isEqualTo("xml:S3421"); + assertThat(raisedIssueDto.getPrimaryMessage()).isEqualTo("Replace \"pom.version\" with \"project.version\"."); + assertThat(raisedIssueDto.getFlows()).isEmpty(); + assertThat(raisedIssueDto.getQuickFixes()).isEmpty(); + assertThat(raisedIssueDto.getTextRange()).usingRecursiveComparison().isEqualTo(new TextRangeDto(6, 11, 6, 25)); + assertThat(raisedIssueDto.getRuleDescriptionContextKey()).isNull(); } @SonarLintTest @@ -229,14 +224,13 @@ void it_should_analyze_xml_file_in_connected_mode(SonarLintTestHarness harness, .build(client); var analysisId = UUID.randomUUID(); - var result = backend.getAnalysisService().analyzeFiles(new AnalyzeFilesParams(CONFIG_SCOPE_ID, analysisId, List.of(fileUri), Map.of(), System.currentTimeMillis())).join(); + var result = backend.getAnalysisService() + .analyzeFilesAndTrack(new AnalyzeFilesAndTrackParams(CONFIG_SCOPE_ID, analysisId, List.of(fileUri), Map.of(), false, System.currentTimeMillis())).join(); assertThat(result.getFailedAnalysisFiles()).isEmpty(); - var rawIssueCaptor = ArgumentCaptor.forClass(RawIssueDto.class); - verify(client).didRaiseIssue(eq(CONFIG_SCOPE_ID), eq(analysisId), rawIssueCaptor.capture()); - var rawIssue = rawIssueCaptor.getValue(); - assertThat(rawIssue.getSeverity()).isEqualTo(IssueSeverity.BLOCKER); - assertThat(rawIssue.getRuleKey()).isEqualTo("xml:S3421"); + var raisedIssueDto = client.getRaisedIssuesForScopeId(CONFIG_SCOPE_ID).get(fileUri).get(0); + assertThat(raisedIssueDto.getSeverity()).isEqualTo(IssueSeverity.BLOCKER); + assertThat(raisedIssueDto.getRuleKey()).isEqualTo("xml:S3421"); } @SonarLintTest @@ -254,7 +248,8 @@ void it_should_notify_client_on_plugin_skip(SonarLintTestHarness harness, @TempD .build(client); var analysisId = UUID.randomUUID(); - var result = backend.getAnalysisService().analyzeFiles(new AnalyzeFilesParams(CONFIG_SCOPE_ID, analysisId, List.of(fileUri), Map.of(), System.currentTimeMillis())).join(); + var result = backend.getAnalysisService() + .analyzeFilesAndTrack(new AnalyzeFilesAndTrackParams(CONFIG_SCOPE_ID, analysisId, List.of(fileUri), Map.of(), false, System.currentTimeMillis())).join(); assertThat(result.getFailedAnalysisFiles()).isEmpty(); verify(client, timeout(200)).didSkipLoadingPlugin(CONFIG_SCOPE_ID, Language.JAVA, DidSkipLoadingPluginParams.SkipReason.UNSATISFIED_JRE, "11", "10"); @@ -274,7 +269,8 @@ void it_should_notify_client_on_secret_detection(SonarLintTestHarness harness, @ .build(client); var analysisId = UUID.randomUUID(); - var result = backend.getAnalysisService().analyzeFiles(new AnalyzeFilesParams(CONFIG_SCOPE_ID, analysisId, List.of(fileUri), Map.of(), System.currentTimeMillis())).join(); + var result = backend.getAnalysisService() + .analyzeFilesAndTrack(new AnalyzeFilesAndTrackParams(CONFIG_SCOPE_ID, analysisId, List.of(fileUri), Map.of(), false, System.currentTimeMillis())).join(); assertThat(result.getFailedAnalysisFiles()).isEmpty(); verify(client, timeout(1000)).didDetectSecret(CONFIG_SCOPE_ID); @@ -294,7 +290,8 @@ void it_should_notify_client_on_analysis_progress(SonarLintTestHarness harness, .build(client); var analysisId = UUID.randomUUID(); - backend.getAnalysisService().analyzeFiles(new AnalyzeFilesParams(CONFIG_SCOPE_ID, analysisId, List.of(fileUri), Map.of(), System.currentTimeMillis())).join(); + backend.getAnalysisService() + .analyzeFilesAndTrack(new AnalyzeFilesAndTrackParams(CONFIG_SCOPE_ID, analysisId, List.of(fileUri), Map.of(), false, System.currentTimeMillis())).join(); verify(client).startProgress(refEq(new StartProgressParams(analysisId.toString(), CONFIG_SCOPE_ID, "Analyzing 1 file", null, true, false))); var reportProgressCaptor = ArgumentCaptor.forClass(ReportProgressParams.class); @@ -318,7 +315,8 @@ void analysis_response_should_contain_raw_issues(SonarLintTestHarness harness, @ .build(client); var analysisId = UUID.randomUUID(); - var result = backend.getAnalysisService().analyzeFiles(new AnalyzeFilesParams(CONFIG_SCOPE_ID, analysisId, List.of(fileUri), Map.of(), System.currentTimeMillis())).join(); + var result = backend.getAnalysisService() + .analyzeFilesAndTrack(new AnalyzeFilesAndTrackParams(CONFIG_SCOPE_ID, analysisId, List.of(fileUri), Map.of(), false, System.currentTimeMillis())).join(); assertThat(result.getFailedAnalysisFiles()).isEmpty(); assertThat(result.getRawIssues()).hasSize(1); @@ -345,15 +343,14 @@ void it_should_report_issues_for_multi_file_analysis_taking_data_from_module_fil .build(client); var analysisId = UUID.randomUUID(); - var result = backend.getAnalysisService().analyzeFiles(new AnalyzeFilesParams(CONFIG_SCOPE_ID, analysisId, - List.of(fileIssueUri), Map.of(), System.currentTimeMillis())).join(); + var result = backend.getAnalysisService() + .analyzeFilesAndTrack(new AnalyzeFilesAndTrackParams(CONFIG_SCOPE_ID, analysisId, + List.of(fileIssueUri), Map.of(), false, System.currentTimeMillis())).join(); assertThat(result.getFailedAnalysisFiles()).isEmpty(); - var rawIssueCaptor = ArgumentCaptor.forClass(RawIssueDto.class); - verify(client, timeout(2000)).didRaiseIssue(eq(CONFIG_SCOPE_ID), eq(analysisId), rawIssueCaptor.capture()); - var rawIssue = rawIssueCaptor.getValue(); - assertThat(rawIssue.getSeverity()).isEqualTo(IssueSeverity.BLOCKER); - assertThat(rawIssue.getRuleKey()).isEqualTo("python:S930"); + var raisedIssueDto = awaitRaisedIssuesNotification(client, CONFIG_SCOPE_ID).get(0); + assertThat(raisedIssueDto.getSeverity()).isEqualTo(IssueSeverity.BLOCKER); + assertThat(raisedIssueDto.getRuleKey()).isEqualTo("python:S930"); } @SonarLintTest @@ -373,12 +370,11 @@ void it_should_report_multi_file_issues_for_files_added_after_initialization(Son .build(client); var analysisId = UUID.randomUUID(); - var result = backend.getAnalysisService().analyzeFiles(new AnalyzeFilesParams(CONFIG_SCOPE_ID, analysisId, - List.of(fileIssueUri), Map.of(), System.currentTimeMillis())).join(); + var result = backend.getAnalysisService().analyzeFilesAndTrack(new AnalyzeFilesAndTrackParams(CONFIG_SCOPE_ID, analysisId, + List.of(fileIssueUri), Map.of(), false, System.currentTimeMillis())).join(); assertThat(result.getFailedAnalysisFiles()).isEmpty(); - var rawIssueCaptor = ArgumentCaptor.forClass(RawIssueDto.class); - verify(client, times(0)).didRaiseIssue(eq(CONFIG_SCOPE_ID), eq(analysisId), rawIssueCaptor.capture()); + await().during(2, TimeUnit.SECONDS).untilAsserted(() -> assertThat(client.getRaisedIssuesForScopeIdAsList(CONFIG_SCOPE_ID)).isEmpty()); backend.getConfigurationService().didAddConfigurationScopes(new DidAddConfigurationScopesParams(List.of( new ConfigurationScopeDto(CONFIG_SCOPE_ID, null, false, CONFIG_SCOPE_ID, null)))); @@ -389,15 +385,13 @@ void it_should_report_multi_file_issues_for_files_added_after_initialization(Son List.of(), List.of())); - result = backend.getAnalysisService().analyzeFiles(new AnalyzeFilesParams(CONFIG_SCOPE_ID, analysisId, - List.of(fileIssueUri), Map.of(), System.currentTimeMillis())).join(); + result = backend.getAnalysisService().analyzeFilesAndTrack(new AnalyzeFilesAndTrackParams(CONFIG_SCOPE_ID, analysisId, + List.of(fileIssueUri), Map.of(), false, System.currentTimeMillis())).join(); assertThat(result.getFailedAnalysisFiles()).isEmpty(); - rawIssueCaptor = ArgumentCaptor.forClass(RawIssueDto.class); - verify(client, timeout(2000)).didRaiseIssue(eq(CONFIG_SCOPE_ID), eq(analysisId), rawIssueCaptor.capture()); - var rawIssue = rawIssueCaptor.getValue(); - assertThat(rawIssue.getSeverity()).isEqualTo(IssueSeverity.BLOCKER); - assertThat(rawIssue.getRuleKey()).isEqualTo("python:S930"); + var raisedIssueDto = awaitRaisedIssuesNotification(client, CONFIG_SCOPE_ID).get(0); + assertThat(raisedIssueDto.getSeverity()).isEqualTo(IssueSeverity.BLOCKER); + assertThat(raisedIssueDto.getRuleKey()).isEqualTo("python:S930"); } @SonarLintTest @@ -434,25 +428,24 @@ void it_should_report_issues_for_multi_file_analysis_only_for_leaf_config_scopes .build(client); var analysisId = UUID.randomUUID(); - var leafConfigScopeResult = backend.getAnalysisService().analyzeFiles(new AnalyzeFilesParams(leafConfigScope, analysisId, - List.of(file2IssueUri), Map.of(), System.currentTimeMillis())).join(); + var leafConfigScopeResult = backend.getAnalysisService().analyzeFilesAndTrack(new AnalyzeFilesAndTrackParams(leafConfigScope, analysisId, + List.of(file2IssueUri), Map.of(), false, System.currentTimeMillis())).join(); assertThat(leafConfigScopeResult.getFailedAnalysisFiles()).isEmpty(); - var rawIssueCaptor = ArgumentCaptor.forClass(RawIssueDto.class); - verify(client).didRaiseIssue(eq(leafConfigScope), eq(analysisId), rawIssueCaptor.capture()); - var rawIssue = rawIssueCaptor.getValue(); - assertThat(rawIssue.getSeverity()).isEqualTo(IssueSeverity.BLOCKER); - assertThat(rawIssue.getRuleKey()).isEqualTo("python:S930"); + var raisedIssueDto = awaitRaisedIssuesNotification(client, leafConfigScope).get(0); + assertThat(raisedIssueDto.getSeverity()).isEqualTo(IssueSeverity.BLOCKER); + assertThat(raisedIssueDto.getRuleKey()).isEqualTo("python:S930"); - var parentConfigScopeResult = backend.getAnalysisService().analyzeFiles(new AnalyzeFilesParams(parentConfigScope, - analysisId, List.of(file1IssueUri), Map.of(), System.currentTimeMillis())).join(); + var parentConfigScopeResult = backend.getAnalysisService().analyzeFilesAndTrack(new AnalyzeFilesAndTrackParams(parentConfigScope, + analysisId, List.of(file1IssueUri), Map.of(), false, System.currentTimeMillis())).join(); assertThat(parentConfigScopeResult.getFailedAnalysisFiles()).isEmpty(); - verify(client, times(0)).didRaiseIssue(eq(parentConfigScope), eq(analysisId), rawIssueCaptor.capture()); + await().during(2, TimeUnit.SECONDS).untilAsserted(() -> assertThat(client.getRaisedIssuesForScopeIdAsList(parentConfigScope)).isEmpty()); } @SonarLintTest - void it_should_update_module_file_system_on_file_events_creating_file(SonarLintTestHarness harness, @TempDir Path baseDir) { + void it_should_update_module_file_system_on_file_events_creating_file(SonarLintTestHarness harness, @TempDir Path tempDir) throws IOException { + var baseDir = Files.createDirectory(tempDir.resolve("it_should_update_module_file_system_on_file_events_creating_file")); var fileIssue = createFile(baseDir, "fileIssue.py", "from fileFuncDef import foo\n" + "foo(1,2,3)\n"); @@ -467,12 +460,11 @@ void it_should_update_module_file_system_on_file_events_creating_file(SonarLintT .build(client); var analysisId = UUID.randomUUID(); - var parentConfigScopeResult = backend.getAnalysisService().analyzeFiles(new AnalyzeFilesParams(CONFIG_SCOPE_ID, analysisId, - List.of(fileIssueUri), Map.of(), System.currentTimeMillis())).join(); + var parentConfigScopeResult = backend.getAnalysisService().analyzeFilesAndTrack(new AnalyzeFilesAndTrackParams(CONFIG_SCOPE_ID, analysisId, + List.of(fileIssueUri), Map.of(), false, System.currentTimeMillis())).join(); assertThat(parentConfigScopeResult.getFailedAnalysisFiles()).isEmpty(); - var rawIssueCaptor = ArgumentCaptor.forClass(RawIssueDto.class); - verify(client, times(0)).didRaiseIssue(eq(CONFIG_SCOPE_ID), eq(analysisId), rawIssueCaptor.capture()); + await().during(2, TimeUnit.SECONDS).untilAsserted(() -> assertThat(client.getRaisedIssuesForScopeIdAsList(CONFIG_SCOPE_ID)).isEmpty()); var fileFuncDef = createFile(baseDir, "fileFuncDef.py", "def foo(a):\n" + @@ -484,15 +476,13 @@ void it_should_update_module_file_system_on_file_events_creating_file(SonarLintT List.of(), List.of())); - parentConfigScopeResult = backend.getAnalysisService().analyzeFiles(new AnalyzeFilesParams(CONFIG_SCOPE_ID, - analysisId, List.of(fileIssueUri), Map.of(), System.currentTimeMillis())).join(); + parentConfigScopeResult = backend.getAnalysisService().analyzeFilesAndTrack(new AnalyzeFilesAndTrackParams(CONFIG_SCOPE_ID, + analysisId, List.of(fileIssueUri), Map.of(), false, System.currentTimeMillis())).join(); assertThat(parentConfigScopeResult.getFailedAnalysisFiles()).isEmpty(); - rawIssueCaptor = ArgumentCaptor.forClass(RawIssueDto.class); - verify(client).didRaiseIssue(eq(CONFIG_SCOPE_ID), eq(analysisId), rawIssueCaptor.capture()); - var rawIssue = rawIssueCaptor.getValue(); - assertThat(rawIssue.getSeverity()).isEqualTo(IssueSeverity.BLOCKER); - assertThat(rawIssue.getRuleKey()).isEqualTo("python:S930"); + var raisedIssueDto = awaitRaisedIssuesNotification(client, CONFIG_SCOPE_ID).get(0); + assertThat(raisedIssueDto.getSeverity()).isEqualTo(IssueSeverity.BLOCKER); + assertThat(raisedIssueDto.getRuleKey()).isEqualTo("python:S930"); } @Disabled("SLCORE-1113") @@ -518,25 +508,22 @@ void it_should_update_module_file_system_on_file_events_deleting_file(SonarLintT .build(client); var analysisId = UUID.randomUUID(); - var result = backend.getAnalysisService().analyzeFiles(new AnalyzeFilesParams(CONFIG_SCOPE_ID, analysisId, - List.of(fileIssueUri), Map.of(), System.currentTimeMillis())).join(); + var result = backend.getAnalysisService().analyzeFilesAndTrack(new AnalyzeFilesAndTrackParams(CONFIG_SCOPE_ID, analysisId, + List.of(fileIssueUri), Map.of(), false, System.currentTimeMillis())).join(); assertThat(result.getFailedAnalysisFiles()).isEmpty(); - var rawIssueCaptor = ArgumentCaptor.forClass(RawIssueDto.class); - verify(client).didRaiseIssue(eq(CONFIG_SCOPE_ID), eq(analysisId), rawIssueCaptor.capture()); - var rawIssue = rawIssueCaptor.getValue(); - assertThat(rawIssue.getSeverity()).isEqualTo(IssueSeverity.BLOCKER); - assertThat(rawIssue.getRuleKey()).isEqualTo("python:S930"); + var raisedIssueDto = awaitRaisedIssuesNotification(client, CONFIG_SCOPE_ID).get(0); + assertThat(raisedIssueDto.getSeverity()).isEqualTo(IssueSeverity.BLOCKER); + assertThat(raisedIssueDto.getRuleKey()).isEqualTo("python:S930"); removeFile(baseDir, "fileFuncDef.py"); backend.getFileService().didUpdateFileSystem(new DidUpdateFileSystemParams(List.of(), List.of(), List.of(fileFuncDefUri))); - result = backend.getAnalysisService().analyzeFiles(new AnalyzeFilesParams(CONFIG_SCOPE_ID, analysisId, - List.of(fileIssueUri), Map.of(), System.currentTimeMillis())).join(); + result = backend.getAnalysisService().analyzeFilesAndTrack(new AnalyzeFilesAndTrackParams(CONFIG_SCOPE_ID, analysisId, + List.of(fileIssueUri), Map.of(), false, System.currentTimeMillis())).join(); assertThat(result.getFailedAnalysisFiles()).isEmpty(); - rawIssueCaptor = ArgumentCaptor.forClass(RawIssueDto.class); - verify(client, times(0)).didRaiseIssue(eq(CONFIG_SCOPE_ID), eq(analysisId), rawIssueCaptor.capture()); + assertThat(client.getRaisedHotspotsForScopeIdAsList(CONFIG_SCOPE_ID)).isEmpty(); } @Disabled("SLCORE-1113") @@ -562,15 +549,13 @@ void it_should_update_module_file_system_on_file_events_editing_file(SonarLintTe .build(client); var analysisId = UUID.randomUUID(); - var result = backend.getAnalysisService().analyzeFiles(new AnalyzeFilesParams(CONFIG_SCOPE_ID, analysisId, - List.of(fileIssueUri), Map.of(), System.currentTimeMillis())).join(); + var result = backend.getAnalysisService().analyzeFilesAndTrack(new AnalyzeFilesAndTrackParams(CONFIG_SCOPE_ID, analysisId, + List.of(fileIssueUri), Map.of(), false, System.currentTimeMillis())).join(); assertThat(result.getFailedAnalysisFiles()).isEmpty(); - var rawIssueCaptor = ArgumentCaptor.forClass(RawIssueDto.class); - verify(client).didRaiseIssue(eq(CONFIG_SCOPE_ID), eq(analysisId), rawIssueCaptor.capture()); - var rawIssue = rawIssueCaptor.getValue(); - assertThat(rawIssue.getSeverity()).isEqualTo(IssueSeverity.BLOCKER); - assertThat(rawIssue.getRuleKey()).isEqualTo("python:S930"); + var raisedIssueDto = awaitRaisedIssuesNotification(client, CONFIG_SCOPE_ID).get(0); + assertThat(raisedIssueDto.getSeverity()).isEqualTo(IssueSeverity.BLOCKER); + assertThat(raisedIssueDto.getRuleKey()).isEqualTo("python:S930"); editFile(baseDir, "fileFuncDef.py", ""); backend.getFileService().didUpdateFileSystem(new DidUpdateFileSystemParams( @@ -579,12 +564,11 @@ void it_should_update_module_file_system_on_file_events_editing_file(SonarLintTe List.of(), List.of())); - result = backend.getAnalysisService().analyzeFiles(new AnalyzeFilesParams(CONFIG_SCOPE_ID, analysisId, - List.of(fileIssueUri), Map.of(), System.currentTimeMillis())).join(); + result = backend.getAnalysisService().analyzeFilesAndTrack(new AnalyzeFilesAndTrackParams(CONFIG_SCOPE_ID, analysisId, + List.of(fileIssueUri), Map.of(), false, System.currentTimeMillis())).join(); assertThat(result.getFailedAnalysisFiles()).isEmpty(); - rawIssueCaptor = ArgumentCaptor.forClass(RawIssueDto.class); - verify(client, times(0)).didRaiseIssue(eq(CONFIG_SCOPE_ID), eq(analysisId), rawIssueCaptor.capture()); + assertThat(client.getRaisedHotspotsForScopeIdAsList(CONFIG_SCOPE_ID)).isEmpty(); } @SonarLintTest @@ -655,7 +639,7 @@ void it_should_skip_analysis_and_keep_rules_if_disabled_language_for_analysis(So assertThat(result.getFailedAnalysisFiles()).isEmpty(); assertThat(result.getRawIssues()).isEmpty(); - verify(client, never()).didRaiseIssue(eq(CONFIG_SCOPE_ID), eq(analysisId), any()); + await().during(2, TimeUnit.SECONDS).untilAsserted(() -> assertThat(client.getRaisedIssuesForScopeIdAsList(CONFIG_SCOPE_ID)).isEmpty()); var allRules = backend.getRulesService().listAllStandaloneRulesDefinitions().join(); assertThat(allRules.getRulesByKey().keySet()) @@ -808,9 +792,9 @@ void it_should_unload_rules_cache_on_config_scope_closed(SonarLintTestHarness ha // analyse files to warmup caches var analysisId1 = UUID.randomUUID(); - backend.getAnalysisService().analyzeFiles(new AnalyzeFilesParams(CONFIG_SCOPE_ID, analysisId1, List.of(fileUri), Map.of(), System.currentTimeMillis())).join(); + backend.getAnalysisService().analyzeFilesAndTrack(new AnalyzeFilesAndTrackParams(CONFIG_SCOPE_ID, analysisId1, List.of(fileUri), Map.of(),true, System.currentTimeMillis())).join(); var analysisId2 = UUID.randomUUID(); - backend.getAnalysisService().analyzeFiles(new AnalyzeFilesParams(configScope2, analysisId2, List.of(fileUri2), Map.of(), System.currentTimeMillis())).join(); + backend.getAnalysisService().analyzeFilesAndTrack(new AnalyzeFilesAndTrackParams(configScope2, analysisId2, List.of(fileUri2), Map.of(), true, System.currentTimeMillis())).join(); // unload one of the projects backend.getConfigurationService().didRemoveConfigurationScope(new DidRemoveConfigurationScopeParams(configScope2)); @@ -881,9 +865,9 @@ void it_should_not_unload_rules_cache_on_config_scope_closed_if_another_config_s // analyse files to warmup caches var analysisId1 = UUID.randomUUID(); - backend.getAnalysisService().analyzeFiles(new AnalyzeFilesParams(CONFIG_SCOPE_ID, analysisId1, List.of(fileUri), Map.of(), System.currentTimeMillis())).join(); + backend.getAnalysisService().analyzeFilesAndTrack(new AnalyzeFilesAndTrackParams(CONFIG_SCOPE_ID, analysisId1, List.of(fileUri), Map.of(), true, System.currentTimeMillis())).join(); var analysisId2 = UUID.randomUUID(); - backend.getAnalysisService().analyzeFiles(new AnalyzeFilesParams(configScope2, analysisId2, List.of(fileUri2), Map.of(), System.currentTimeMillis())).join(); + backend.getAnalysisService().analyzeFilesAndTrack(new AnalyzeFilesAndTrackParams(configScope2, analysisId2, List.of(fileUri2), Map.of(), true, System.currentTimeMillis())).join(); // unload one of the projects backend.getConfigurationService().didRemoveConfigurationScope(new DidRemoveConfigurationScopeParams(configScope2)); @@ -911,16 +895,16 @@ void it_should_analyse_file_with_non_file_uri_schema(SonarLintTestHarness harnes backend.getAnalysisService() .analyzeFilesAndTrack(new AnalyzeFilesAndTrackParams(CONFIG_SCOPE_ID, UUID.randomUUID(), List.of(fileUri1), Map.of(), false, System.currentTimeMillis())).join(); - await().atMost(2, TimeUnit.SECONDS).untilAsserted(() -> assertThat(client.getRaisedIssuesForScopeId(CONFIG_SCOPE_ID)).hasSize(1)); - var raisedIssues = client.getRaisedIssuesForScopeIdAsList(CONFIG_SCOPE_ID); + var raisedIssues = awaitRaisedIssuesNotification(client, CONFIG_SCOPE_ID); assertThat(raisedIssues).hasSize(1); + client.cleanRaisedIssues(); backend.getAnalysisService() .analyzeFilesAndTrack(new AnalyzeFilesAndTrackParams(CONFIG_SCOPE_ID, UUID.randomUUID(), List.of(fileUri2), Map.of(), false, System.currentTimeMillis())).join(); await().atMost(2, TimeUnit.SECONDS).untilAsserted(() -> assertThat(client.getRaisedIssuesForScopeId(CONFIG_SCOPE_ID)).hasSize(1)); - raisedIssues = client.getRaisedIssuesForScopeIdAsList(CONFIG_SCOPE_ID); + raisedIssues = awaitRaisedIssuesNotification(client, CONFIG_SCOPE_ID); assertThat(raisedIssues).hasSize(1); } @@ -1028,4 +1012,9 @@ private static void removeFile(Path folderPath, String fileName) { throw new RuntimeException(e); } } + + private static List awaitRaisedIssuesNotification(SonarLintBackendFixture.FakeSonarLintRpcClient client, String configScopeId) { + waitForRaisedIssues(client, configScopeId); + return client.getRaisedIssuesForScopeIdAsList(configScopeId); + } } diff --git a/medium-tests/src/test/java/mediumtest/hotspots/MatchWithServerHotspotsMediumTests.java b/medium-tests/src/test/java/mediumtest/hotspots/MatchWithServerHotspotsMediumTests.java deleted file mode 100644 index 530c6d2466..0000000000 --- a/medium-tests/src/test/java/mediumtest/hotspots/MatchWithServerHotspotsMediumTests.java +++ /dev/null @@ -1,187 +0,0 @@ -/* - * SonarLint Core - Medium Tests - * Copyright (C) 2016-2025 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 mediumtest.hotspots; - -import java.nio.file.Path; -import java.time.Duration; -import java.time.Instant; -import java.util.List; -import java.util.Map; -import java.util.concurrent.CompletableFuture; -import java.util.stream.Collectors; -import java.util.stream.IntStream; -import org.sonarsource.sonarlint.core.commons.HotspotReviewStatus; -import org.sonarsource.sonarlint.core.commons.api.TextRange; -import org.sonarsource.sonarlint.core.commons.api.TextRangeWithHash; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.hotspot.HotspotStatus; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.tracking.ClientTrackedFindingDto; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.tracking.LineWithHashDto; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.tracking.LocalOnlySecurityHotspotDto; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.tracking.MatchWithServerSecurityHotspotsParams; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.tracking.MatchWithServerSecurityHotspotsResponse; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.tracking.ServerMatchedSecurityHotspotDto; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.tracking.TextRangeWithHashDto; -import org.sonarsource.sonarlint.core.rpc.protocol.common.Either; -import org.sonarsource.sonarlint.core.test.utils.SonarLintTestRpcServer; -import org.sonarsource.sonarlint.core.test.utils.junit5.SonarLintTest; -import org.sonarsource.sonarlint.core.test.utils.junit5.SonarLintTestHarness; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.sonarsource.sonarlint.core.test.utils.storage.ServerSecurityHotspotFixture.aServerHotspot; - -class MatchWithServerHotspotsMediumTests { - - @SonarLintTest - void it_should_not_track_server_hotspots_when_configuration_scope_is_not_bound(SonarLintTestHarness harness) { - var backend = harness.newBackend() - .withUnboundConfigScope("configScopeId") - .build(); - - var response = matchWithServerHotspots( - backend, new MatchWithServerSecurityHotspotsParams("configScopeId", Map.of(Path.of("filePath"), List.of(new ClientTrackedFindingDto(null, null, null, null, "ruleKey", "message"))), false)); - - assertThat(response) - .succeedsWithin(Duration.ofSeconds(2)) - .satisfies(result -> assertThat(result.getSecurityHotspotsByIdeRelativePath()) - .hasEntrySatisfying(Path.of("filePath"), hotspots -> assertThat(hotspots).hasSize(1).allSatisfy(hotspot -> assertThat(hotspot.isRight()).isTrue()))); - } - - @SonarLintTest - void it_should_track_local_only_hotspots(SonarLintTestHarness harness) { - var server = harness.newFakeSonarQubeServer().start(); - var backend = harness.newBackend() - .withSonarQubeConnection("connectionId", server) - .withBoundConfigScope("configScopeId", "connectionId", "projectKey") - .build(); - - var response = matchWithServerHotspots(backend, new MatchWithServerSecurityHotspotsParams("configScopeId", - Map.of(Path.of("file/path"), List.of(new ClientTrackedFindingDto(null, null, new TextRangeWithHashDto(1, 2, 3, 4, "hash"), new LineWithHashDto(1, "linehash"), "ruleKey", "message"))), - false)); - - assertThat(response) - .succeedsWithin(Duration.ofSeconds(2)) - .satisfies(result -> assertThat(result.getSecurityHotspotsByIdeRelativePath()) - .hasEntrySatisfying(Path.of("file/path"), hotspots -> { - assertThat(hotspots).hasSize(1).allSatisfy(hotspot -> assertThat(hotspot.isRight()).isTrue()); - assertThat(hotspots).usingRecursiveComparison().ignoringFields("lsp4jEither.right.id") - .isEqualTo(List.of(Either.forRight(new LocalOnlySecurityHotspotDto(null)))); - })); - } - - @SonarLintTest - void it_should_track_hotspots_for_unknown_branch(SonarLintTestHarness harness) { - var server = harness.newFakeSonarQubeServer().start(); - var backend = harness.newBackend() - .withSonarQubeConnection("connectionId", server) - .withBoundConfigScope("configScopeId", "connectionId", "projectKey") - .build(); - - var response = matchWithServerHotspots(backend, new MatchWithServerSecurityHotspotsParams("configScopeId", - Map.of(Path.of("file/path"), List.of(new ClientTrackedFindingDto(null, null, new TextRangeWithHashDto(1, 2, 3, 4, "hash"), new LineWithHashDto(1, "linehash"), "ruleKey", "message"))), - false)); - - assertThat(response) - .succeedsWithin(Duration.ofSeconds(20)) - .satisfies(result -> assertThat(result.getSecurityHotspotsByIdeRelativePath()) - .hasEntrySatisfying(Path.of("file/path"), hotspots -> { - assertThat(hotspots).hasSize(1).allSatisfy(hotspot -> assertThat(hotspot.isRight()).isTrue()); - assertThat(hotspots).usingRecursiveComparison().ignoringFields("lsp4jEither.right.id") - .isEqualTo(List.of(Either.forRight(new LocalOnlySecurityHotspotDto(null)))); - })); - } - - @SonarLintTest - void it_should_track_with_a_known_server_hotspot_at_the_same_location(SonarLintTestHarness harness) { - var serverHotspot = aServerHotspot("hotspotKey").withTextRange(new TextRangeWithHash(1, 2, 3, 4, "hash")).withIntroductionDate(Instant.EPOCH.plusSeconds(1)) - .withStatus(HotspotReviewStatus.SAFE); - var client = harness.newFakeClient().build(); - var server = harness.newFakeSonarQubeServer() - .withProject("projectKey").start(); - var backend = harness.newBackend() - .withSonarQubeConnection("connectionId", server, storage -> storage - .withProject("projectKey", project -> project.withMainBranch("main", branch -> branch.withHotspot(serverHotspot)))) - .withBoundConfigScope("configScopeId", "connectionId", "projectKey") - .build(client); - - var response = matchWithServerHotspots(backend, new MatchWithServerSecurityHotspotsParams("configScopeId", - Map.of(Path.of("file/path"), List.of(new ClientTrackedFindingDto(null, null, new TextRangeWithHashDto(1, 2, 3, 4, "hash"), new LineWithHashDto(1, "linehash"), "ruleKey", "message"))), - false)); - - assertThat(response) - .succeedsWithin(Duration.ofSeconds(2)) - .satisfies(result -> assertThat(result.getSecurityHotspotsByIdeRelativePath()) - .hasEntrySatisfying(Path.of("file/path"), hotspots -> assertThat(hotspots).usingRecursiveComparison().ignoringFields("lsp4jEither.left.id") - .isEqualTo(List.of(Either.forLeft( - new ServerMatchedSecurityHotspotDto(null, "hotspotKey", 1000L, HotspotStatus.SAFE, true)))))); - } - - @SonarLintTest - void it_should_track_with_a_server_only_hotspot_when_fetching_from_legacy_server_requested(SonarLintTestHarness harness) { - var server = harness.newFakeSonarQubeServer("10.0").withProject("projectKey", - project -> project.withBranch("main", branch -> branch.withHotspot("hotspotKey", - hotspot -> hotspot.withRuleKey("rule:key").withMessage("message").withFilePath("file/path").withAuthor("author").withStatus(HotspotReviewStatus.TO_REVIEW) - .withCreationDate(Instant.ofEpochMilli(123456789)) - .withTextRange(new TextRange(1, 2, 3, 4))))) - .start(); - var client = harness.newFakeClient().build(); - var backend = harness.newBackend() - .withSonarQubeConnection("connectionId", server, storage -> storage.withServerVersion("10.0") - .withProject("projectKey", project -> project.withMainBranch("main"))) - .withBoundConfigScope("configScopeId", "connectionId", "projectKey") - .build(client); - - var response = matchWithServerHotspots(backend, new MatchWithServerSecurityHotspotsParams("configScopeId", - Map.of(Path.of("file/path"), - List.of(new ClientTrackedFindingDto(null, null, new TextRangeWithHashDto(1, 2, 3, 4, "hash"), new LineWithHashDto(1, "linehash"), "rule:key", "message"))), - true)); - - assertThat(response) - .succeedsWithin(Duration.ofSeconds(2)) - .satisfies(result -> assertThat(result.getSecurityHotspotsByIdeRelativePath()) - .hasEntrySatisfying(Path.of("file/path"), hotspots -> assertThat(hotspots).usingRecursiveComparison().ignoringFields("lsp4jEither.left.id") - .isEqualTo(List.of(Either.forLeft( - new ServerMatchedSecurityHotspotDto(null, "hotspotKey", 123456000L, HotspotStatus.TO_REVIEW, true)))))); - } - - @SonarLintTest - void it_should_download_all_hotspots_at_once_when_tracking_hotspots_from_more_than_10_files(SonarLintTestHarness harness) { - var server = harness.newFakeSonarQubeServer("10.0").withProject("projectKey", - project -> project.withBranch("main", - branch -> branch.withIssue("issueKey", "rule:key", "message", "author", "file/path", "OPEN", null, Instant.now(), new TextRange(1, 2, 3, 4)))) - .start(); - var backend = harness.newBackend() - .withSonarQubeConnection("connectionId", server.baseUrl(), storage -> storage.withServerVersion("9.5")) - .withBoundConfigScope("configScopeId", "connectionId", "projectKey") - .build(); - var hotspotsByServerRelativePath = IntStream.rangeClosed(1, 11).boxed().collect(Collectors.>toMap(index -> Path.of("file/path" + index), - i -> List.of(new ClientTrackedFindingDto(null, null, new TextRangeWithHashDto(1, 2, 3, 4, "hash"), new LineWithHashDto(1, "linehash"), "rule:key", "message")))); - - var response = matchWithServerHotspots(backend, new MatchWithServerSecurityHotspotsParams("configScopeId", hotspotsByServerRelativePath, true)); - - assertThat(response) - .succeedsWithin(Duration.ofSeconds(4)) - .satisfies(result -> assertThat(result.getSecurityHotspotsByIdeRelativePath()) - .hasSize(11)); - } - - private CompletableFuture matchWithServerHotspots(SonarLintTestRpcServer backend, MatchWithServerSecurityHotspotsParams params) { - return backend.getSecurityHotspotMatchingService().matchWithServerSecurityHotspots(params); - } -} diff --git a/medium-tests/src/test/java/mediumtest/issues/CheckResolutionStatusChangePermittedMediumTests.java b/medium-tests/src/test/java/mediumtest/issues/CheckResolutionStatusChangePermittedMediumTests.java index b22bd62314..3f44259366 100644 --- a/medium-tests/src/test/java/mediumtest/issues/CheckResolutionStatusChangePermittedMediumTests.java +++ b/medium-tests/src/test/java/mediumtest/issues/CheckResolutionStatusChangePermittedMediumTests.java @@ -20,10 +20,15 @@ package mediumtest.issues; import com.google.protobuf.Message; +import java.io.IOException; +import java.nio.file.Files; import java.nio.file.Path; import java.time.Duration; +import java.time.Instant; +import java.time.temporal.ChronoUnit; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; @@ -32,27 +37,39 @@ import org.assertj.core.api.InstanceOfAssertFactories; import org.eclipse.lsp4j.jsonrpc.ResponseErrorException; import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.api.io.TempDir; +import org.sonar.scanner.protocol.Constants; +import org.sonarsource.sonarlint.core.commons.RuleType; +import org.sonarsource.sonarlint.core.commons.api.TextRange; +import org.sonarsource.sonarlint.core.rpc.protocol.backend.analysis.AnalyzeFilesAndTrackParams; import org.sonarsource.sonarlint.core.rpc.protocol.backend.issue.CheckStatusChangePermittedParams; import org.sonarsource.sonarlint.core.rpc.protocol.backend.issue.CheckStatusChangePermittedResponse; import org.sonarsource.sonarlint.core.rpc.protocol.backend.issue.ResolutionStatus; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.tracking.ClientTrackedFindingDto; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.tracking.LineWithHashDto; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.tracking.LocalOnlyIssueDto; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.tracking.TextRangeWithHashDto; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.tracking.TrackWithServerIssuesParams; +import org.sonarsource.sonarlint.core.rpc.protocol.common.ClientFileDto; +import org.sonarsource.sonarlint.core.rpc.protocol.common.IssueSeverity; +import org.sonarsource.sonarlint.core.rpc.protocol.common.Language; import org.sonarsource.sonarlint.core.serverapi.proto.sonarqube.ws.Issues; import org.sonarsource.sonarlint.core.test.utils.SonarLintTestRpcServer; import org.sonarsource.sonarlint.core.test.utils.junit5.SonarLintTest; import org.sonarsource.sonarlint.core.test.utils.junit5.SonarLintTestHarness; import utils.MockWebServerExtensionWithProtobuf; +import utils.TestPlugin; import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.when; +import static utils.AnalysisUtils.createFile; +import static utils.AnalysisUtils.waitForAnalysisReady; +import static utils.AnalysisUtils.waitForRaisedIssues; class CheckResolutionStatusChangePermittedMediumTests { - private static final Path FILE_PATH = Path.of("file/path"); + private static final String CONFIG_SCOPE_ID = "configScopeId"; + private static final String CONNECTION_ID = "connectionId"; + @RegisterExtension public final MockWebServerExtensionWithProtobuf mockWebServerExtension = new MockWebServerExtensionWithProtobuf(); @@ -65,7 +82,7 @@ void tearDown() { void it_should_fail_when_the_connection_is_unknown(SonarLintTestHarness harness) { var backend = harness.newBackend().build(); - var response = checkStatusChangePermitted(backend, "connectionId", "issueKey"); + var response = checkStatusChangePermitted(backend, CONNECTION_ID, "issueKey"); assertThat(response) .failsWithin(Duration.ofSeconds(2)) @@ -79,10 +96,10 @@ void it_should_fail_when_the_connection_is_unknown(SonarLintTestHarness harness) void it_should_allow_2_statuses_when_user_has_permission_for_sonarqube_103(SonarLintTestHarness harness) { fakeServerWithIssue("issueKey", List.of("wontfix", "falsepositive")); var backend = harness.newBackend() - .withSonarQubeConnection("connectionId", mockWebServerExtension.endpointParams().getBaseUrl(), storage -> storage.withServerVersion("10.3")) + .withSonarQubeConnection(CONNECTION_ID, mockWebServerExtension.endpointParams().getBaseUrl(), storage -> storage.withServerVersion("10.3")) .build(); - var response = checkStatusChangePermitted(backend, "connectionId", "issueKey"); + var response = checkStatusChangePermitted(backend, CONNECTION_ID, "issueKey"); assertThat(response) .succeedsWithin(Duration.ofSeconds(2)) @@ -95,10 +112,10 @@ void it_should_allow_2_statuses_when_user_has_permission_for_sonarqube_103(Sonar void it_should_allow_2_statuses_when_user_has_permission_for_sonarqube_104(SonarLintTestHarness harness) { fakeServerWithIssue("issueKey", List.of("accept", "falsepositive")); var backend = harness.newBackend() - .withSonarQubeConnection("connectionId", mockWebServerExtension.endpointParams().getBaseUrl(), storage -> storage.withServerVersion("10.4")) + .withSonarQubeConnection(CONNECTION_ID, mockWebServerExtension.endpointParams().getBaseUrl(), storage -> storage.withServerVersion("10.4")) .build(); - var response = checkStatusChangePermitted(backend, "connectionId", "issueKey"); + var response = checkStatusChangePermitted(backend, CONNECTION_ID, "issueKey"); assertThat(response) .succeedsWithin(Duration.ofSeconds(2)) @@ -112,10 +129,10 @@ void it_should_allow_2_statuses_when_user_has_permission_for_sonarcloud(SonarLin fakeServerWithIssue("issueKey", "orgKey", List.of("wontfix", "falsepositive")); var backend = harness.newBackend() .withSonarCloudUrl(mockWebServerExtension.endpointParams().getBaseUrl()) - .withSonarCloudConnection("connectionId", "orgKey") + .withSonarCloudConnection(CONNECTION_ID, "orgKey") .build(); - var response = checkStatusChangePermitted(backend, "connectionId", "issueKey"); + var response = checkStatusChangePermitted(backend, CONNECTION_ID, "issueKey"); assertThat(response) .succeedsWithin(Duration.ofSeconds(2)) @@ -129,10 +146,10 @@ void it_should_fallback_to_server_check_if_the_issue_uuid_is_not_found_in_local_ var issueKey = UUID.randomUUID().toString(); fakeServerWithIssue(issueKey, List.of("accept", "falsepositive")); var backend = harness.newBackend() - .withSonarQubeConnection("connectionId", mockWebServerExtension.endpointParams().getBaseUrl(), storage -> storage.withServerVersion("10.4")) + .withSonarQubeConnection(CONNECTION_ID, mockWebServerExtension.endpointParams().getBaseUrl(), storage -> storage.withServerVersion("10.4")) .build(); - var response = checkStatusChangePermitted(backend, "connectionId", issueKey); + var response = checkStatusChangePermitted(backend, CONNECTION_ID, issueKey); assertThat(response) .succeedsWithin(Duration.ofSeconds(2)) @@ -145,10 +162,10 @@ void it_should_fallback_to_server_check_if_the_issue_uuid_is_not_found_in_local_ void it_should_not_permit_status_change_when_issue_misses_required_transitions(SonarLintTestHarness harness) { fakeServerWithIssue("issueKey", List.of("confirm")); var backend = harness.newBackend() - .withSonarQubeConnection("connectionId", mockWebServerExtension.endpointParams().getBaseUrl(), storage -> storage.withServerVersion("10.3")) + .withSonarQubeConnection(CONNECTION_ID, mockWebServerExtension.endpointParams().getBaseUrl(), storage -> storage.withServerVersion("10.3")) .build(); - var response = checkStatusChangePermitted(backend, "connectionId", "issueKey"); + var response = checkStatusChangePermitted(backend, CONNECTION_ID, "issueKey"); assertThat(response) .succeedsWithin(Duration.ofSeconds(2)) @@ -161,10 +178,10 @@ void it_should_not_permit_status_change_when_issue_misses_required_transitions(S void it_should_fail_if_no_issue_is_returned_by_web_api(SonarLintTestHarness harness) { fakeServerWithResponse("issueKey", null, Issues.SearchWsResponse.newBuilder().build()); var backend = harness.newBackend() - .withSonarQubeConnection("connectionId", mockWebServerExtension.endpointParams().getBaseUrl(), storage -> storage.withServerVersion("10.3")) + .withSonarQubeConnection(CONNECTION_ID, mockWebServerExtension.endpointParams().getBaseUrl(), storage -> storage.withServerVersion("10.3")) .build(); - var response = checkStatusChangePermitted(backend, "connectionId", "issueKey"); + var response = checkStatusChangePermitted(backend, CONNECTION_ID, "issueKey"); assertThat(response) .failsWithin(Duration.ofSeconds(2)) @@ -178,10 +195,10 @@ void it_should_fail_if_no_issue_is_returned_by_web_api(SonarLintTestHarness harn @SonarLintTest void it_should_fail_if_web_api_returns_an_error(SonarLintTestHarness harness) { var backend = harness.newBackend() - .withSonarQubeConnection("connectionId", mockWebServerExtension.endpointParams().getBaseUrl(), storage -> storage.withServerVersion("10.3")) + .withSonarQubeConnection(CONNECTION_ID, mockWebServerExtension.endpointParams().getBaseUrl(), storage -> storage.withServerVersion("10.3")) .build(); - var response = checkStatusChangePermitted(backend, "connectionId", "issueKey"); + var response = checkStatusChangePermitted(backend, CONNECTION_ID, "issueKey"); assertThat(response) .failsWithin(Duration.ofSeconds(2)) @@ -194,10 +211,10 @@ void it_should_fail_if_web_api_returns_an_error(SonarLintTestHarness harness) { void it_should_fail_if_web_api_returns_unexpected_body(SonarLintTestHarness harness) { fakeServerWithWrongBody("issueKey"); var backend = harness.newBackend() - .withSonarQubeConnection("connectionId", mockWebServerExtension.endpointParams().getBaseUrl(), storage -> storage.withServerVersion("10.3")) + .withSonarQubeConnection(CONNECTION_ID, mockWebServerExtension.endpointParams().getBaseUrl(), storage -> storage.withServerVersion("10.3")) .build(); - var response = checkStatusChangePermitted(backend, "connectionId", "issueKey"); + var response = checkStatusChangePermitted(backend, CONNECTION_ID, "issueKey"); assertThat(response) .failsWithin(Duration.ofSeconds(2)) @@ -208,22 +225,49 @@ void it_should_fail_if_web_api_returns_unexpected_body(SonarLintTestHarness harn }); } + @Disabled("SC is difficult to setup for this test") @SonarLintTest - void it_should_not_permit_status_change_on_local_only_issues_for_sonarcloud(SonarLintTestHarness harness) throws ExecutionException, InterruptedException { - var client = harness.newFakeClient().build(); + void it_should_not_permit_status_change_on_local_only_issues_for_sonarcloud(SonarLintTestHarness harness, @TempDir Path baseDir) { + var filePath = createFile(baseDir, "pom.xml", "\n" + + "\n" + + " 4.0.0\n" + + " com.foo\n" + + " bar\n" + + " ${pom.version}\n" + + ""); + var fileUri = filePath.toUri(); + var branchName = "main"; + var projectKey = "projectKey"; + var client = harness.newFakeClient() + .withInitialFs(CONFIG_SCOPE_ID, baseDir, List.of( + new ClientFileDto(fileUri, baseDir.relativize(filePath), CONFIG_SCOPE_ID, false, null, filePath, null, null, true))) + .build(); + var server = harness.newFakeSonarCloudServer("org") + .withQualityProfile("qpKey", qualityProfile -> qualityProfile + .withLanguage("xml").withActiveRule("xml:S3421", activeRule -> activeRule + .withSeverity(IssueSeverity.BLOCKER) + )) + .withProject(projectKey, + project -> project + .withQualityProfile("qpKey") + .withBranch(branchName)) + .withPlugin(TestPlugin.XML) + .start(); var backend = harness.newBackend() - .withSonarCloudConnection("connectionId", "orgKey", true, storage -> storage - .withProject("projectKey", project -> project.withMainBranch("main"))) - .withBoundConfigScope("configScopeId", "connectionId", "projectKey") + .withSonarCloudConnection(CONNECTION_ID, server.baseUrl()) + .withBoundConfigScope(CONFIG_SCOPE_ID, CONNECTION_ID, projectKey) + .withExtraEnabledLanguagesInConnectedMode(Language.XML) + .withFullSynchronization() .build(client); + client.waitForSynchronization(); + waitForAnalysisReady(client, CONFIG_SCOPE_ID); - var trackedIssues = backend.getIssueTrackingService().trackWithServerIssues(new TrackWithServerIssuesParams("configScopeId", - Map.of(FILE_PATH, List.of(new ClientTrackedFindingDto(null, null, new TextRangeWithHashDto(1, 2, 3, 4, "hash"), new LineWithHashDto(1, "linehash"), "ruleKey", "message"))), - false)); - Thread.sleep(2000); - var localOnlyIssue = trackedIssues.get().getIssuesByIdeRelativePath().get(FILE_PATH).get(0).getRight(); + backend.getAnalysisService().analyzeFilesAndTrack(new AnalyzeFilesAndTrackParams(CONFIG_SCOPE_ID, UUID.randomUUID(), + List.of(fileUri), Map.of(), false, 0)).join(); - var response = checkStatusChangePermitted(backend, "connectionId", localOnlyIssue.getId().toString()); + waitForRaisedIssues(client, CONFIG_SCOPE_ID); + var localOnlyIssue = client.getRaisedIssuesForScopeIdAsList(CONFIG_SCOPE_ID).get(0); + var response = checkStatusChangePermitted(backend, CONNECTION_ID, localOnlyIssue.getId().toString()); assertThat(response) .succeedsWithin(Duration.ofSeconds(2)) @@ -232,24 +276,54 @@ void it_should_not_permit_status_change_on_local_only_issues_for_sonarcloud(Sona .containsExactly(false, "Marking a local-only issue as resolved requires SonarQube Server 10.2+", List.of()); } + @Disabled("SLCORE-966") @SonarLintTest - void it_should_not_permit_status_change_on_local_only_issues_for_sonarqube_prior_to_10_2(SonarLintTestHarness harness) throws ExecutionException, InterruptedException { - var client = harness.newFakeClient().build(); - var server = harness.newFakeSonarQubeServer() - .withProject("projectKey").start(); + void it_should_not_permit_status_change_on_local_only_issues_for_sonarqube_prior_to_10_2(SonarLintTestHarness harness, @TempDir Path testDir) throws IOException { + var baseDir = testDir.resolve("it_should_not_permit_status_change_on_local_only_issues_for_sonarqube_prior_to_10_2"); + Files.createDirectory(baseDir); + var filePath = createFile(baseDir, "pom.xml", "\n" + + "\n" + + " 4.0.0\n" + + " com.foo\n" + + " bar\n" + + " ${pom.version}\n" + + ""); + var fileUri = filePath.toUri(); + var branchName = "main"; + var projectKey = "projectKey"; + var client = harness.newFakeClient() + .withInitialFs(CONFIG_SCOPE_ID, baseDir, List.of( + new ClientFileDto(fileUri, baseDir.relativize(filePath), CONFIG_SCOPE_ID, false, null, filePath, null, null, true))) + .build(); + var server = harness.newFakeSonarQubeServer("10.1") + .withQualityProfile("qpKey", qualityProfile -> qualityProfile + .withLanguage("xml").withActiveRule("xml:S3421", activeRule -> activeRule + .withSeverity(IssueSeverity.BLOCKER) + )) + .withProject(projectKey, + project -> project + .withQualityProfile("qpKey") + .withBranch(branchName)) + .withPlugin(TestPlugin.XML) + .start(); var backend = harness.newBackend() - .withSonarQubeConnection("connectionId", server, storage -> storage.withServerVersion("10.1") - .withProject("projectKey", project -> project.withMainBranch("main"))) - .withBoundConfigScope("configScopeId", "connectionId", "projectKey") + .withSonarQubeConnection(CONNECTION_ID, server) + .withBoundConfigScope(CONFIG_SCOPE_ID, CONNECTION_ID, projectKey) + .withExtraEnabledLanguagesInConnectedMode(Language.XML) + .withFullSynchronization() .build(client); + client.waitForSynchronization(); + waitForAnalysisReady(client, CONFIG_SCOPE_ID); - var trackedIssues = backend.getIssueTrackingService().trackWithServerIssues(new TrackWithServerIssuesParams("configScopeId", - Map.of(FILE_PATH, List.of(new ClientTrackedFindingDto(null, null, new TextRangeWithHashDto(1, 2, 3, 4, "hash"), new LineWithHashDto(1, "linehash"), "ruleKey", "message"))), - false)); - Thread.sleep(2000); - var localOnlyIssue = trackedIssues.get().getIssuesByIdeRelativePath().get(FILE_PATH).get(0).getRight(); + backend.getAnalysisService().analyzeFilesAndTrack(new AnalyzeFilesAndTrackParams(CONFIG_SCOPE_ID, UUID.randomUUID(), + List.of(fileUri), Map.of(), false, 0)).join(); - var response = checkStatusChangePermitted(backend, "connectionId", localOnlyIssue.getId().toString()); + waitForRaisedIssues(client, CONFIG_SCOPE_ID); + var localOnlyIssue = client.getRaisedIssuesForScopeIdAsList(CONFIG_SCOPE_ID).get(0); + assertThat(localOnlyIssue.getSeverity()).isEqualTo(IssueSeverity.BLOCKER); + assertThat(localOnlyIssue.getRuleKey()).isEqualTo("xml:S3421"); + + var response = checkStatusChangePermitted(backend, CONNECTION_ID, localOnlyIssue.getId().toString()); assertThat(response) .succeedsWithin(Duration.ofSeconds(2)) @@ -258,25 +332,57 @@ void it_should_not_permit_status_change_on_local_only_issues_for_sonarqube_prior .containsExactly(false, "Marking a local-only issue as resolved requires SonarQube Server 10.2+", List.of()); } + @Disabled("SLCORE-966") @SonarLintTest - void it_should_permit_status_change_on_local_only_issues_for_sonarqube_10_2_plus(SonarLintTestHarness harness) throws ExecutionException, InterruptedException { - var client = harness.newFakeClient().build(); - var server = harness.newFakeSonarQubeServer() - .withProject("projectKey").start(); + void it_should_permit_status_change_on_local_only_issues_for_sonarqube_10_2_plus(SonarLintTestHarness harness, @TempDir Path baseDir) { + var filePath = createFile(baseDir, "pom.xml", "\n" + + "\n" + + " 4.0.0\n" + + " com.foo\n" + + " bar\n" + + " ${pom.version}\n" + + ""); + var fileUri = filePath.toUri(); + var branchName = "branchName"; + var projectKey = "projectKey"; + var serverIssueKey = "myIssueKey"; + var introductionDate = Instant.now().truncatedTo(ChronoUnit.SECONDS); + var client = harness.newFakeClient() + .withInitialFs(CONFIG_SCOPE_ID, baseDir, List.of( + new ClientFileDto(fileUri, baseDir.relativize(filePath), CONFIG_SCOPE_ID, false, null, filePath, null, null, true))) + .build(); + when(client.matchSonarProjectBranch(eq(CONFIG_SCOPE_ID), eq("main"), eq(Set.of("main", branchName)), any())) + .thenReturn(branchName); + var server = harness.newFakeSonarQubeServer("10.2") + .withQualityProfile("qpKey", qualityProfile -> qualityProfile + .withLanguage("xml").withActiveRule("xml:S3421", activeRule -> activeRule + .withSeverity(IssueSeverity.MAJOR) + )) + .withProject(projectKey, + project -> project + .withQualityProfile("qpKey") + .withBranch(branchName, + branch -> branch.withIssue(serverIssueKey, "xml:S3421", "message", + "author", baseDir.relativize(filePath).toString(), "1356c67d7ad1638d816bfb822dd2c25d", Constants.Severity.MAJOR, RuleType.CODE_SMELL, + "OPEN", null, introductionDate, new TextRange(1, 1, 1, 1)) + )) + .withPlugin(TestPlugin.XML) + .start(); var backend = harness.newBackend() - .withSonarQubeConnection("connectionId", server, storage -> storage - .withServerVersion("10.2") - .withProject("projectKey", project -> project.withMainBranch("main"))) - .withBoundConfigScope("configScopeId", "connectionId", "projectKey") + .withSonarQubeConnection(CONNECTION_ID, server) + .withBoundConfigScope(CONFIG_SCOPE_ID, CONNECTION_ID, projectKey) + .withExtraEnabledLanguagesInConnectedMode(Language.XML) + .withFullSynchronization() .build(client); + client.waitForSynchronization(); - var trackedIssues = backend.getIssueTrackingService().trackWithServerIssues(new TrackWithServerIssuesParams("configScopeId", - Map.of(FILE_PATH, List.of(new ClientTrackedFindingDto(null, null, new TextRangeWithHashDto(1, 2, 3, 4, "hash"), new LineWithHashDto(1, "linehash"), "ruleKey", "message"))), - false)); + backend.getAnalysisService().analyzeFilesAndTrack(new AnalyzeFilesAndTrackParams(CONFIG_SCOPE_ID, UUID.randomUUID(), + List.of(fileUri), Map.of(), false, 0)).join(); - var localOnlyIssue = trackedIssues.get().getIssuesByIdeRelativePath().get(FILE_PATH).get(0).getRight(); + waitForRaisedIssues(client, CONFIG_SCOPE_ID); + var localOnlyIssue = client.getRaisedIssuesForScopeIdAsList(CONFIG_SCOPE_ID).get(0); - var response = checkStatusChangePermitted(backend, "connectionId", localOnlyIssue.getId().toString()); + var response = checkStatusChangePermitted(backend, CONNECTION_ID, localOnlyIssue.getId().toString()); assertThat(response) .succeedsWithin(Duration.ofSeconds(2)) @@ -285,26 +391,51 @@ void it_should_permit_status_change_on_local_only_issues_for_sonarqube_10_2_plus .containsExactly(true, null, List.of(ResolutionStatus.WONT_FIX, ResolutionStatus.FALSE_POSITIVE)); } + @Disabled("SLCORE-966") @SonarLintTest - void it_should_permit_status_change_on_local_only_issues_for_sonarqube_10_4_plus(SonarLintTestHarness harness) { - var server = harness.newFakeSonarQubeServer() - .withProject("projectKey").start(); - var backend = harness.newBackend() - .withSonarQubeConnection("connectionId", server, storage -> storage.withServerVersion("10.4").withProject("projectKey", project -> project.withMainBranch("main"))) - .withBoundConfigScope("configScopeId", "connectionId", "projectKey") + void it_should_permit_status_change_on_local_only_issues_for_sonarqube_10_4_plus(SonarLintTestHarness harness, @TempDir Path testDir) throws IOException { + var baseDir = testDir.resolve("it_should_permit_status_change_on_local_only_issues_for_sonarqube_10_4_plus"); + Files.createDirectory(baseDir); + var filePath = createFile(baseDir, "pom.xml", "\n" + + "\n" + + " 4.0.0\n" + + " com.foo\n" + + " bar\n" + + " ${pom.version}\n" + + ""); + var fileUri = filePath.toUri(); + var branchName = "main"; + var projectKey = "projectKey"; + var client = harness.newFakeClient() + .withInitialFs(CONFIG_SCOPE_ID, baseDir, List.of( + new ClientFileDto(fileUri, baseDir.relativize(filePath), CONFIG_SCOPE_ID, false, null, filePath, null, null, true))) .build(); - var trackedIssues = backend.getIssueTrackingService().trackWithServerIssues(new TrackWithServerIssuesParams("configScopeId", - Map.of(FILE_PATH, List.of(new ClientTrackedFindingDto(null, null, new TextRangeWithHashDto(1, 2, 3, 4, "hash"), new LineWithHashDto(1, "linehash"), "ruleKey", "message"))), - false)); - - LocalOnlyIssueDto localOnlyIssue = null; - try { - localOnlyIssue = trackedIssues.get().getIssuesByIdeRelativePath().get(FILE_PATH).get(0).getRight(); - } catch (Exception e) { - fail(); - } - - var response = checkStatusChangePermitted(backend, "connectionId", localOnlyIssue.getId().toString()); + var server = harness.newFakeSonarQubeServer("10.4") + .withQualityProfile("qpKey", qualityProfile -> qualityProfile + .withLanguage("xml").withActiveRule("xml:S3421", activeRule -> activeRule + .withSeverity(IssueSeverity.MAJOR) + )) + .withProject(projectKey, + project -> project + .withQualityProfile("qpKey") + .withBranch(branchName)) + .withPlugin(TestPlugin.XML) + .start(); + var backend = harness.newBackend() + .withSonarQubeConnection(CONNECTION_ID, server) + .withBoundConfigScope(CONFIG_SCOPE_ID, CONNECTION_ID, projectKey) + .withExtraEnabledLanguagesInConnectedMode(Language.XML) + .withFullSynchronization() + .build(client); + client.waitForSynchronization(); + waitForAnalysisReady(client, CONFIG_SCOPE_ID); + + backend.getAnalysisService().analyzeFilesAndTrack(new AnalyzeFilesAndTrackParams(CONFIG_SCOPE_ID, UUID.randomUUID(), + List.of(fileUri), Map.of(), false, 0)).join(); + + waitForRaisedIssues(client, CONFIG_SCOPE_ID); + var localOnlyIssue = client.getRaisedIssuesForScopeIdAsList(CONFIG_SCOPE_ID).get(0); + var response = checkStatusChangePermitted(backend, CONNECTION_ID, localOnlyIssue.getId().toString()); assertThat(response) .succeedsWithin(Duration.ofSeconds(2)) diff --git a/medium-tests/src/test/java/mediumtest/issues/IssuesStatusChangeMediumTests.java b/medium-tests/src/test/java/mediumtest/issues/IssuesStatusChangeMediumTests.java index d22ecac215..aea92d061a 100644 --- a/medium-tests/src/test/java/mediumtest/issues/IssuesStatusChangeMediumTests.java +++ b/medium-tests/src/test/java/mediumtest/issues/IssuesStatusChangeMediumTests.java @@ -30,22 +30,23 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import org.eclipse.lsp4j.jsonrpc.ResponseErrorException; +import org.junit.jupiter.api.io.TempDir; import org.sonarsource.sonarlint.core.commons.LineWithHash; import org.sonarsource.sonarlint.core.commons.LocalOnlyIssue; import org.sonarsource.sonarlint.core.commons.LocalOnlyIssueResolution; import org.sonarsource.sonarlint.core.commons.RuleType; import org.sonarsource.sonarlint.core.commons.api.TextRangeWithHash; +import org.sonarsource.sonarlint.core.rpc.protocol.backend.analysis.AnalyzeFilesAndTrackParams; import org.sonarsource.sonarlint.core.rpc.protocol.backend.issue.AddIssueCommentParams; import org.sonarsource.sonarlint.core.rpc.protocol.backend.issue.ChangeIssueStatusParams; import org.sonarsource.sonarlint.core.rpc.protocol.backend.issue.ReopenAllIssuesForFileParams; import org.sonarsource.sonarlint.core.rpc.protocol.backend.issue.ReopenIssueParams; import org.sonarsource.sonarlint.core.rpc.protocol.backend.issue.ResolutionStatus; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.tracking.ClientTrackedFindingDto; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.tracking.LineWithHashDto; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.tracking.TextRangeWithHashDto; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.tracking.TrackWithServerIssuesParams; +import org.sonarsource.sonarlint.core.rpc.protocol.common.ClientFileDto; +import org.sonarsource.sonarlint.core.rpc.protocol.common.IssueSeverity; import org.sonarsource.sonarlint.core.test.utils.junit5.SonarLintTest; import org.sonarsource.sonarlint.core.test.utils.junit5.SonarLintTestHarness; +import utils.TestPlugin; import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; import static com.github.tomakehurst.wiremock.client.WireMock.equalToJson; @@ -56,21 +57,26 @@ import static org.awaitility.Awaitility.waitAtMost; import static org.sonarsource.sonarlint.core.test.utils.server.ServerFixture.ServerStatus.DOWN; import static org.sonarsource.sonarlint.core.test.utils.storage.ServerIssueFixtures.aServerIssue; +import static utils.AnalysisUtils.createFile; +import static utils.AnalysisUtils.waitForRaisedIssues; class IssuesStatusChangeMediumTests { + private static final String CONFIGURATION_SCOPE_ID = "configScopeId"; + private static final String CONNECTION_ID = "connectionId"; + @SonarLintTest void it_should_update_the_status_on_sonarqube_when_changing_the_status_on_a_server_matched_issue(SonarLintTestHarness harness) { var serverIssue = aServerIssue("myIssueKey").withTextRange(new TextRangeWithHash(1, 2, 3, 4, "hash")).withIntroductionDate(Instant.EPOCH.plusSeconds(1)).withType(RuleType.BUG); var server = harness.newFakeSonarQubeServer().start(); var backend = harness.newBackend() - .withSonarQubeConnection("connectionId", server.baseUrl(), storage -> storage + .withSonarQubeConnection(CONNECTION_ID, server.baseUrl(), storage -> storage .withProject("projectKey", project -> project.withMainBranch("main", branch -> branch.withIssue(serverIssue))) .withServerVersion("9.8")) - .withBoundConfigScope("configScopeId", "connectionId", "projectKey") + .withBoundConfigScope(CONFIGURATION_SCOPE_ID, CONNECTION_ID, "projectKey") .build(); - var response = backend.getIssueService().changeStatus(new ChangeIssueStatusParams("configScopeId", "myIssueKey", + var response = backend.getIssueService().changeStatus(new ChangeIssueStatusParams(CONFIGURATION_SCOPE_ID, "myIssueKey", ResolutionStatus.WONT_FIX, false)); assertThat(response).succeedsWithin(Duration.ofSeconds(2)); @@ -86,7 +92,7 @@ void it_should_update_the_status_on_sonarqube_when_changing_the_status_on_a_serv void it_should_update_the_telemetry_when_changing_the_status_on_a_server_matched_issue(SonarLintTestHarness harness) { var server = harness.newFakeSonarQubeServer().start(); var backend = harness.newBackend() - .withSonarQubeConnection("connectionId", server.baseUrl(), storage -> storage + .withSonarQubeConnection(CONNECTION_ID, server.baseUrl(), storage -> storage .withProject("projectKey", project -> project.withMainBranch("main", branch -> branch.withIssue( @@ -96,11 +102,11 @@ void it_should_update_the_telemetry_when_changing_the_status_on_a_server_matched .withIntroductionDate(Instant.EPOCH.plusSeconds(1)) .withType(RuleType.BUG)))) .withServerVersion("9.8")) - .withBoundConfigScope("configScopeId", "connectionId", "projectKey") + .withBoundConfigScope(CONFIGURATION_SCOPE_ID, CONNECTION_ID, "projectKey") .withTelemetryEnabled() .build(); - var response = backend.getIssueService().changeStatus(new ChangeIssueStatusParams("configScopeId", "myIssueKey", + var response = backend.getIssueService().changeStatus(new ChangeIssueStatusParams(CONFIGURATION_SCOPE_ID, "myIssueKey", ResolutionStatus.WONT_FIX, false)); assertThat(response).succeedsWithin(Duration.ofSeconds(2)); @@ -114,13 +120,13 @@ void it_should_fail_the_future_when_the_server_returns_an_error(SonarLintTestHar var serverIssue = aServerIssue("myIssueKey").withTextRange(new TextRangeWithHash(1, 2, 3, 4, "hash")).withIntroductionDate(Instant.EPOCH.plusSeconds(1)).withType(RuleType.BUG); var server = harness.newFakeSonarQubeServer().withStatus(DOWN).start(); var backend = harness.newBackend() - .withSonarQubeConnection("connectionId", server.baseUrl(), storage -> storage + .withSonarQubeConnection(CONNECTION_ID, server.baseUrl(), storage -> storage .withProject("projectKey", project -> project.withMainBranch("main", branch -> branch.withIssue(serverIssue))) .withServerVersion("9.8")) - .withBoundConfigScope("configScopeId", "connectionId", "projectKey") + .withBoundConfigScope(CONFIGURATION_SCOPE_ID, CONNECTION_ID, "projectKey") .build(); - var response = backend.getIssueService().changeStatus(new ChangeIssueStatusParams("configScopeId", "myIssueKey", + var response = backend.getIssueService().changeStatus(new ChangeIssueStatusParams(CONFIGURATION_SCOPE_ID, "myIssueKey", ResolutionStatus.WONT_FIX, false)); assertThat(response) @@ -133,56 +139,84 @@ void it_should_fail_the_future_when_the_server_returns_an_error(SonarLintTestHar } @SonarLintTest - void it_should_update_local_only_storage_when_the_issue_exists_locally(SonarLintTestHarness harness) throws ExecutionException, InterruptedException { + void it_should_update_local_only_storage_when_the_issue_exists_locally(SonarLintTestHarness harness, @TempDir Path baseDir) { + var filePath = createFile(baseDir, "pom.xml", "\n" + + "\n" + + " 4.0.0\n" + + " com.foo\n" + + " bar\n" + + " ${pom.version}\n" + + ""); + var fileUri = filePath.toUri(); var server = harness.newFakeSonarQubeServer() - .withProject("projectKey") + .withQualityProfile("qpKey", qualityProfile -> qualityProfile + .withLanguage("xml").withActiveRule("xml:S3421", activeRule -> activeRule.withSeverity(IssueSeverity.BLOCKER))) + .withProject("projectKey", + project -> project.withQualityProfile("qpKey")) .start(); - var client = harness.newFakeClient().build(); + var client = harness.newFakeClient() + .withInitialFs(CONFIGURATION_SCOPE_ID, List.of( + new ClientFileDto(fileUri, baseDir.relativize(filePath), CONFIGURATION_SCOPE_ID, false, null, filePath, null, null, true))) + .build(); var backend = harness.newBackend() - .withSonarQubeConnection("connectionId", server.baseUrl()) - .withBoundConfigScope("configScopeId", "connectionId", "projectKey") + .withSonarQubeConnection(CONNECTION_ID, server) + .withBoundConfigScope(CONFIGURATION_SCOPE_ID, CONNECTION_ID, "projectKey") + .withConnectedEmbeddedPluginAndEnabledLanguage(TestPlugin.XML) .withFullSynchronization() .build(client); client.waitForSynchronization(); - var trackedIssues = backend.getIssueTrackingService().trackWithServerIssues(new TrackWithServerIssuesParams("configScopeId", - Map.of(Path.of("file/path"), - List.of(new ClientTrackedFindingDto(null, null, new TextRangeWithHashDto(1, 2, 3, 4, "hash"), new LineWithHashDto(1, "linehash"), "ruleKey", "message"))), - false)); + backend.getAnalysisService().analyzeFilesAndTrack(new AnalyzeFilesAndTrackParams(CONFIGURATION_SCOPE_ID, UUID.randomUUID(), + List.of(fileUri), Map.of(), false, 0)).join(); - var localOnlyIssue = trackedIssues.get().getIssuesByIdeRelativePath().get(Path.of("file/path")).get(0).getRight(); + waitForRaisedIssues(client, CONFIGURATION_SCOPE_ID); + var localOnlyIssue = client.getRaisedIssuesForScopeId(CONFIGURATION_SCOPE_ID).get(fileUri).get(0); - var response = backend.getIssueService().changeStatus(new ChangeIssueStatusParams("configScopeId", localOnlyIssue.getId().toString(), + var response = backend.getIssueService().changeStatus(new ChangeIssueStatusParams(CONFIGURATION_SCOPE_ID, localOnlyIssue.getId().toString(), ResolutionStatus.WONT_FIX, false)); assertThat(response).succeedsWithin(Duration.ofSeconds(2)); - var issueLoaded = backend.getLocalOnlyIssueStorageService().get().loadForFile("configScopeId", Path.of("file/path")); + var issueLoaded = backend.getLocalOnlyIssueStorageService().get().loadForFile(CONFIGURATION_SCOPE_ID, baseDir.relativize(filePath)); assertThat(issueLoaded).hasSize(1); assertThat(issueLoaded.get(0).getId()).isEqualTo(localOnlyIssue.getId()); assertThat(issueLoaded.get(0).getResolution().getStatus()).isEqualTo(org.sonarsource.sonarlint.core.commons.IssueStatus.WONT_FIX); } @SonarLintTest - void it_should_sync_anticipated_transitions_with_sonarqube_when_the_issue_exists_locally(SonarLintTestHarness harness) throws ExecutionException, InterruptedException { + void it_should_sync_anticipated_transitions_with_sonarqube_when_the_issue_exists_locally(SonarLintTestHarness harness, @TempDir Path baseDir) { + var filePath = createFile(baseDir, "pom.xml", "\n" + + "\n" + + " 4.0.0\n" + + " com.foo\n" + + " bar\n" + + " ${pom.version}\n" + + ""); + var fileUri = filePath.toUri(); var server = harness.newFakeSonarQubeServer() - .withProject("projectKey") + .withQualityProfile("qpKey", qualityProfile -> qualityProfile + .withLanguage("xml").withActiveRule("xml:S3421", activeRule -> activeRule.withSeverity(IssueSeverity.BLOCKER))) + .withProject("projectKey", + project -> project.withQualityProfile("qpKey")) .start(); - var client = harness.newFakeClient().build(); + var client = harness.newFakeClient() + .withInitialFs(CONFIGURATION_SCOPE_ID, List.of( + new ClientFileDto(fileUri, baseDir.relativize(filePath), CONFIGURATION_SCOPE_ID, false, null, filePath, null, null, true))) + .build(); var backend = harness.newBackend() - .withSonarQubeConnection("connectionId", server.baseUrl()) - .withBoundConfigScope("configScopeId", "connectionId", "projectKey") + .withSonarQubeConnection(CONNECTION_ID, server) + .withBoundConfigScope(CONFIGURATION_SCOPE_ID, CONNECTION_ID, "projectKey") + .withConnectedEmbeddedPluginAndEnabledLanguage(TestPlugin.XML) .withFullSynchronization() .build(client); client.waitForSynchronization(); - var trackedIssues = backend.getIssueTrackingService().trackWithServerIssues(new TrackWithServerIssuesParams("configScopeId", - Map.of(Path.of("file/path"), - List.of(new ClientTrackedFindingDto(null, null, new TextRangeWithHashDto(1, 2, 3, 4, "hash"), new LineWithHashDto(1, "linehash"), "ruleKey", "message"))), - false)); + backend.getAnalysisService().analyzeFilesAndTrack(new AnalyzeFilesAndTrackParams(CONFIGURATION_SCOPE_ID, UUID.randomUUID(), + List.of(fileUri), Map.of(), true, 0)).join(); - var localOnlyIssue = trackedIssues.get().getIssuesByIdeRelativePath().get(Path.of("file/path")).get(0).getRight(); + waitForRaisedIssues(client, CONFIGURATION_SCOPE_ID); + var localOnlyIssue = client.getRaisedIssuesForScopeId(CONFIGURATION_SCOPE_ID).get(fileUri).get(0); - var response = backend.getIssueService().changeStatus(new ChangeIssueStatusParams("configScopeId", localOnlyIssue.getId().toString(), + var response = backend.getIssueService().changeStatus(new ChangeIssueStatusParams(CONFIGURATION_SCOPE_ID, localOnlyIssue.getId().toString(), ResolutionStatus.WONT_FIX, false)); assertThat(response).succeedsWithin(Duration.ofSeconds(2)); @@ -191,51 +225,64 @@ void it_should_sync_anticipated_transitions_with_sonarqube_when_the_issue_exists .verify(WireMock.postRequestedFor(urlEqualTo("/api/issues/anticipated_transitions?projectKey=projectKey")) .withHeader("Content-Type", equalTo("application/json; charset=UTF-8")) .withRequestBody( - equalToJson("[{\"filePath\":\"file/path\",\"line\":1,\"hash\":\"linehash\",\"ruleKey\":\"ruleKey\",\"issueMessage\":\"message\",\"transition\":\"wontfix\"}]"))); + equalToJson( + "[{\"filePath\":\"pom.xml\",\"line\":6,\"hash\":\"07bac3d9d23dc1b0d7156598e01d40b0\",\"ruleKey\":\"xml:S3421\",\"issueMessage\":\"Replace \\\"pom.version\\\" with \\\"project.version\\\".\",\"transition\":\"wontfix\"}]"))); }); } @SonarLintTest - void it_should_update_telemetry_when_changing_status_of_a_local_only_issue(SonarLintTestHarness harness) throws ExecutionException, InterruptedException { + void it_should_update_telemetry_when_changing_status_of_a_local_only_issue(SonarLintTestHarness harness, @TempDir Path baseDir) { + var filePath = createFile(baseDir, "pom.xml", "\n" + + "\n" + + " 4.0.0\n" + + " com.foo\n" + + " bar\n" + + " ${pom.version}\n" + + ""); + var fileUri = filePath.toUri(); var server = harness.newFakeSonarQubeServer() - .withProject("projectKey") + .withQualityProfile("qpKey", qualityProfile -> qualityProfile + .withLanguage("xml").withActiveRule("xml:S3421", activeRule -> activeRule.withSeverity(IssueSeverity.BLOCKER))) + .withProject("projectKey", + project -> project.withQualityProfile("qpKey")) .start(); var client = harness.newFakeClient() + .withInitialFs(CONFIGURATION_SCOPE_ID, List.of( + new ClientFileDto(fileUri, baseDir.relativize(filePath), CONFIGURATION_SCOPE_ID, false, null, filePath, null, null, true))) .build(); var backend = harness.newBackend() - .withSonarQubeConnection("connectionId", server) - .withBoundConfigScope("configScopeId", "connectionId", "projectKey") + .withSonarQubeConnection(CONNECTION_ID, server) + .withBoundConfigScope(CONFIGURATION_SCOPE_ID, CONNECTION_ID, "projectKey") + .withConnectedEmbeddedPluginAndEnabledLanguage(TestPlugin.XML) .withFullSynchronization() .withTelemetryEnabled() .build(client); - client.waitForSynchronization(); - var trackedIssues = backend.getIssueTrackingService().trackWithServerIssues(new TrackWithServerIssuesParams("configScopeId", - Map.of(Path.of("file/path"), - List.of(new ClientTrackedFindingDto(null, null, new TextRangeWithHashDto(1, 2, 3, 4, "hash"), new LineWithHashDto(1, "linehash"), "ruleKey", "message"))), - false)); + backend.getAnalysisService().analyzeFilesAndTrack(new AnalyzeFilesAndTrackParams(CONFIGURATION_SCOPE_ID, UUID.randomUUID(), + List.of(fileUri), Map.of(), false, 0)).join(); - var localOnlyIssue = trackedIssues.get().getIssuesByIdeRelativePath().get(Path.of("file/path")).get(0).getRight(); + waitForRaisedIssues(client, CONFIGURATION_SCOPE_ID); + var localOnlyIssue = client.getRaisedIssuesForScopeId(CONFIGURATION_SCOPE_ID).get(fileUri).get(0); - var response = backend.getIssueService().changeStatus(new ChangeIssueStatusParams("configScopeId", localOnlyIssue.getId().toString(), + var response = backend.getIssueService().changeStatus(new ChangeIssueStatusParams(CONFIGURATION_SCOPE_ID, localOnlyIssue.getId().toString(), ResolutionStatus.WONT_FIX, false)); assertThat(response).succeedsWithin(Duration.ofSeconds(2)); assertThat(backend.telemetryFilePath()) .content().asBase64Decoded().asString() - .contains("\"issueStatusChangedRuleKeys\":[\"ruleKey\"]"); + .contains("\"issueStatusChangedRuleKeys\":[\"xml:S3421\"]"); } @SonarLintTest void it_should_fail_when_the_issue_does_not_exists(SonarLintTestHarness harness) { var server = harness.newFakeSonarQubeServer().withStatus(DOWN).start(); var backend = harness.newBackend() - .withSonarQubeConnection("connectionId", server) - .withBoundConfigScope("configScopeId", "connectionId", "projectKey") + .withSonarQubeConnection(CONNECTION_ID, server) + .withBoundConfigScope(CONFIGURATION_SCOPE_ID, CONNECTION_ID, "projectKey") .build(); - var params = new ChangeIssueStatusParams("configScopeId", "myIssueKey", ResolutionStatus.WONT_FIX, false); + var params = new ChangeIssueStatusParams(CONFIGURATION_SCOPE_ID, "myIssueKey", ResolutionStatus.WONT_FIX, false); var issueService = backend.getIssueService(); assertThat(issueService.changeStatus(params)) @@ -249,12 +296,12 @@ void it_should_fail_when_the_issue_does_not_exists(SonarLintTestHarness harness) void it_should_add_new_comment_to_server_issue(SonarLintTestHarness harness) { var server = harness.newFakeSonarQubeServer().start(); var backend = harness.newBackend() - .withSonarQubeConnection("connectionId", server, + .withSonarQubeConnection(CONNECTION_ID, server, storage -> storage.withProject("projectKey", project -> project.withMainBranch("main", branch -> branch.withIssue(aServerIssue("myIssueKey"))))) - .withBoundConfigScope("configScopeId", "connectionId", "projectKey") + .withBoundConfigScope(CONFIGURATION_SCOPE_ID, CONNECTION_ID, "projectKey") .build(); - var response = backend.getIssueService().addComment(new AddIssueCommentParams("configScopeId", "myIssueKey", "That's " + + var response = backend.getIssueService().addComment(new AddIssueCommentParams(CONFIGURATION_SCOPE_ID, "myIssueKey", "That's " + "serious issue")); assertThat(response).succeedsWithin(Duration.ofSeconds(2)); @@ -271,12 +318,12 @@ void it_should_add_new_comment_to_server_issue_with_uuid_key(SonarLintTestHarnes var issueKey = UUID.randomUUID().toString(); var server = harness.newFakeSonarQubeServer().start(); var backend = harness.newBackend() - .withSonarQubeConnection("connectionId", server, + .withSonarQubeConnection(CONNECTION_ID, server, storage -> storage.withProject("projectKey", project -> project.withMainBranch("main", branch -> branch.withIssue(aServerIssue(issueKey))))) - .withBoundConfigScope("configScopeId", "connectionId", "projectKey") + .withBoundConfigScope(CONFIGURATION_SCOPE_ID, CONNECTION_ID, "projectKey") .build(); - var response = backend.getIssueService().addComment(new AddIssueCommentParams("configScopeId", issueKey, "That's " + + var response = backend.getIssueService().addComment(new AddIssueCommentParams(CONFIGURATION_SCOPE_ID, issueKey, "That's " + "serious issue")); assertThat(response).succeedsWithin(Duration.ofSeconds(2)); @@ -293,15 +340,15 @@ void it_should_add_new_comment_to_resolved_local_only_issue(SonarLintTestHarness var server = harness.newFakeSonarQubeServer().start(); var issueId = UUID.randomUUID(); var backend = harness.newBackend() - .withSonarQubeConnection("connectionId", server) - .withBoundConfigScope("configScopeId", "connectionId", "projectKey", storage -> storage.withLocalOnlyIssue(aLocalOnlyIssueResolved(issueId))) + .withSonarQubeConnection(CONNECTION_ID, server) + .withBoundConfigScope(CONFIGURATION_SCOPE_ID, CONNECTION_ID, "projectKey", storage -> storage.withLocalOnlyIssue(aLocalOnlyIssueResolved(issueId))) .build(); - var response = backend.getIssueService().addComment(new AddIssueCommentParams("configScopeId", issueId.toString(), "That's " + + var response = backend.getIssueService().addComment(new AddIssueCommentParams(CONFIGURATION_SCOPE_ID, issueId.toString(), "That's " + "serious issue")); assertThat(response).succeedsWithin(Duration.ofSeconds(2)); - var storedIssues = backend.getLocalOnlyIssueStorageService().get().loadForFile("configScopeId", Path.of("file/path")); + var storedIssues = backend.getLocalOnlyIssueStorageService().get().loadForFile(CONFIGURATION_SCOPE_ID, Path.of("file/path")); assertThat(storedIssues) .extracting(LocalOnlyIssue::getResolution) .extracting(LocalOnlyIssueResolution::getComment) @@ -312,12 +359,12 @@ void it_should_add_new_comment_to_resolved_local_only_issue(SonarLintTestHarness void it_should_throw_if_server_response_is_not_OK_during_add_new_comment_to_issue(SonarLintTestHarness harness) { var server = harness.newFakeSonarQubeServer().withStatus(DOWN).start(); var backend = harness.newBackend() - .withSonarQubeConnection("connectionId", server, + .withSonarQubeConnection(CONNECTION_ID, server, storage -> storage.withProject("projectKey", project -> project.withMainBranch("main", branch -> branch.withIssue(aServerIssue("myIssueKey"))))) - .withBoundConfigScope("configScopeId", "connectionId", "projectKey") + .withBoundConfigScope(CONFIGURATION_SCOPE_ID, CONNECTION_ID, "projectKey") .build(); - var response = backend.getIssueService().addComment(new AddIssueCommentParams("configScopeId", "myIssueKey", "That's " + + var response = backend.getIssueService().addComment(new AddIssueCommentParams(CONFIGURATION_SCOPE_ID, "myIssueKey", "That's " + "serious issue")); assertThat(response) @@ -333,11 +380,11 @@ void it_should_throw_if_server_response_is_not_OK_during_add_new_comment_to_issu void it_should_throw_if_issue_is_unknown_when_adding_a_comment(SonarLintTestHarness harness) { var server = harness.newFakeSonarQubeServer().start(); var backend = harness.newBackend() - .withSonarQubeConnection("connectionId", server) - .withBoundConfigScope("configScopeId", "connectionId", "projectKey") + .withSonarQubeConnection(CONNECTION_ID, server) + .withBoundConfigScope(CONFIGURATION_SCOPE_ID, CONNECTION_ID, "projectKey") .build(); - var response = backend.getIssueService().addComment(new AddIssueCommentParams("configScopeId", "myIssueKey", "That's " + + var response = backend.getIssueService().addComment(new AddIssueCommentParams(CONFIGURATION_SCOPE_ID, "myIssueKey", "That's " + "serious issue")); assertThat(response) @@ -353,12 +400,12 @@ void it_should_throw_if_issue_is_unknown_when_adding_a_comment(SonarLintTestHarn void it_should_throw_if_issue_with_uuid_key_is_unknown_when_adding_a_comment(SonarLintTestHarness harness) { var server = harness.newFakeSonarQubeServer().start(); var backend = harness.newBackend() - .withSonarQubeConnection("connectionId", server) - .withBoundConfigScope("configScopeId", "connectionId", "projectKey") + .withSonarQubeConnection(CONNECTION_ID, server) + .withBoundConfigScope(CONFIGURATION_SCOPE_ID, CONNECTION_ID, "projectKey") .build(); var issueKey = UUID.randomUUID().toString(); - var response = backend.getIssueService().addComment(new AddIssueCommentParams("configScopeId", issueKey, "That's " + + var response = backend.getIssueService().addComment(new AddIssueCommentParams(CONFIGURATION_SCOPE_ID, issueKey, "That's " + "serious issue")); assertThat(response) @@ -375,17 +422,17 @@ void it_should_reopen_issue_by_id(SonarLintTestHarness harness) throws Execution var server = harness.newFakeSonarQubeServer().start(); var issueId = UUID.randomUUID(); var backend = harness.newBackend() - .withSonarQubeConnection("connectionId", server) - .withBoundConfigScope("configScopeId", "connectionId", "projectKey", storage -> storage.withLocalOnlyIssue(aLocalOnlyIssueResolved(issueId))) + .withSonarQubeConnection(CONNECTION_ID, server) + .withBoundConfigScope(CONFIGURATION_SCOPE_ID, CONNECTION_ID, "projectKey", storage -> storage.withLocalOnlyIssue(aLocalOnlyIssueResolved(issueId))) .build(); - var storedIssues = backend.getLocalOnlyIssueStorageService().get().loadAll("configScopeId"); + var storedIssues = backend.getLocalOnlyIssueStorageService().get().loadAll(CONFIGURATION_SCOPE_ID); assertThat(storedIssues).extracting(LocalOnlyIssue::getId).containsOnly(issueId); - var response = backend.getIssueService().reopenIssue(new ReopenIssueParams("configScopeId", issueId.toString(), false)); + var response = backend.getIssueService().reopenIssue(new ReopenIssueParams(CONFIGURATION_SCOPE_ID, issueId.toString(), false)); assertThat(response).succeedsWithin(Duration.ofSeconds(2)); assertThat(response.get().isSuccess()).isTrue(); - storedIssues = backend.getLocalOnlyIssueStorageService().get().loadAll("configScopeId"); + storedIssues = backend.getLocalOnlyIssueStorageService().get().loadAll(CONFIGURATION_SCOPE_ID); assertThat(storedIssues).isEmpty(); } @@ -396,8 +443,8 @@ void it_should_load_issues(SonarLintTestHarness harness) { var issueId2 = UUID.randomUUID(); var otherFileIssueId = UUID.randomUUID(); var backend = harness.newBackend() - .withSonarQubeConnection("connectionId", server) - .withBoundConfigScope("configScopeId", "connectionId", "projectKey", + .withSonarQubeConnection(CONNECTION_ID, server) + .withBoundConfigScope(CONFIGURATION_SCOPE_ID, CONNECTION_ID, "projectKey", storage -> storage .withLocalOnlyIssue(new LocalOnlyIssue( otherFileIssueId, @@ -411,9 +458,9 @@ void it_should_load_issues(SonarLintTestHarness harness) { .withLocalOnlyIssue(aLocalOnlyIssueResolved(issueId2))) .build(); - var issuesForFile = backend.getLocalOnlyIssueStorageService().get().loadForFile("configScopeId", Path.of("file/path")); - var issuesForOtherFile = backend.getLocalOnlyIssueStorageService().get().loadForFile("configScopeId", Path.of("file/path1")); - var allIssues = backend.getLocalOnlyIssueStorageService().get().loadAll("configScopeId"); + var issuesForFile = backend.getLocalOnlyIssueStorageService().get().loadForFile(CONFIGURATION_SCOPE_ID, Path.of("file/path")); + var issuesForOtherFile = backend.getLocalOnlyIssueStorageService().get().loadForFile(CONFIGURATION_SCOPE_ID, Path.of("file/path1")); + var allIssues = backend.getLocalOnlyIssueStorageService().get().loadAll(CONFIGURATION_SCOPE_ID); assertThat(issuesForFile).extracting(LocalOnlyIssue::getId).containsOnly(issueId1, issueId2); assertThat(issuesForOtherFile).extracting(LocalOnlyIssue::getId).containsOnly(otherFileIssueId); assertThat(allIssues).extracting(LocalOnlyIssue::getId).containsOnly(issueId1, issueId2, otherFileIssueId); @@ -426,8 +473,8 @@ void it_should_reopen_all_issues_for_file(SonarLintTestHarness harness) throws E var issueId2 = UUID.randomUUID(); var otherFileIssueId = UUID.randomUUID(); var backend = harness.newBackend() - .withSonarQubeConnection("connectionId", server) - .withBoundConfigScope("configScopeId", "connectionId", "projectKey", + .withSonarQubeConnection(CONNECTION_ID, server) + .withBoundConfigScope(CONFIGURATION_SCOPE_ID, CONNECTION_ID, "projectKey", storage -> storage .withLocalOnlyIssue(aLocalOnlyIssueResolved(issueId1)) .withLocalOnlyIssue(aLocalOnlyIssueResolved(issueId2)) @@ -440,15 +487,15 @@ void it_should_reopen_all_issues_for_file(SonarLintTestHarness harness) throws E "message", new LocalOnlyIssueResolution(org.sonarsource.sonarlint.core.commons.IssueStatus.WONT_FIX, Instant.now().truncatedTo(ChronoUnit.MILLIS), "comment")))) .build(); - var storedIssues = backend.getLocalOnlyIssueStorageService().get().loadAll("configScopeId"); + var storedIssues = backend.getLocalOnlyIssueStorageService().get().loadAll(CONFIGURATION_SCOPE_ID); assertThat(storedIssues).extracting(LocalOnlyIssue::getId).containsOnly(issueId1, issueId2, otherFileIssueId); var response = backend.getIssueService() - .reopenAllIssuesForFile(new ReopenAllIssuesForFileParams("configScopeId", Path.of("file/path"))); + .reopenAllIssuesForFile(new ReopenAllIssuesForFileParams(CONFIGURATION_SCOPE_ID, Path.of("file/path"))); assertThat(response).succeedsWithin(Duration.ofSeconds(2)); assertThat(response.get().isSuccess()).isTrue(); - storedIssues = backend.getLocalOnlyIssueStorageService().get().loadAll("configScopeId"); + storedIssues = backend.getLocalOnlyIssueStorageService().get().loadAll(CONFIGURATION_SCOPE_ID); assertThat(storedIssues).extracting(LocalOnlyIssue::getId).containsOnly(otherFileIssueId); } @@ -458,15 +505,15 @@ void it_should_return_false_on_reopen_issue_with_invalid_id(SonarLintTestHarness var issueId = UUID.randomUUID(); var invalidIssueId = "invalid-id"; var backend = harness.newBackend() - .withSonarQubeConnection("connectionId", server) - .withBoundConfigScope("configScopeId", "connectionId", "projectKey", storage -> storage.withLocalOnlyIssue(aLocalOnlyIssueResolved(issueId))) + .withSonarQubeConnection(CONNECTION_ID, server) + .withBoundConfigScope(CONFIGURATION_SCOPE_ID, CONNECTION_ID, "projectKey", storage -> storage.withLocalOnlyIssue(aLocalOnlyIssueResolved(issueId))) .build(); - var response = backend.getIssueService().reopenIssue(new ReopenIssueParams("configScopeId", invalidIssueId, false)); + var response = backend.getIssueService().reopenIssue(new ReopenIssueParams(CONFIGURATION_SCOPE_ID, invalidIssueId, false)); assertThat(response).succeedsWithin(Duration.ofSeconds(2)); assertThat(response.get().isSuccess()).isFalse(); - var storedIssues = backend.getLocalOnlyIssueStorageService().get().loadForFile("configScopeId", Path.of("file/path")); + var storedIssues = backend.getLocalOnlyIssueStorageService().get().loadForFile(CONFIGURATION_SCOPE_ID, Path.of("file/path")); assertThat(storedIssues).extracting(LocalOnlyIssue::getId).containsOnly(issueId); } @@ -476,15 +523,15 @@ void it_should_return_false_on_reopen_non_existing_issue(SonarLintTestHarness ha var issueId = UUID.randomUUID(); var nonExistingIssueId = UUID.randomUUID(); var backend = harness.newBackend() - .withSonarQubeConnection("connectionId", server) - .withBoundConfigScope("configScopeId", "connectionId", "projectKey", storage -> storage.withLocalOnlyIssue(aLocalOnlyIssueResolved(issueId))) + .withSonarQubeConnection(CONNECTION_ID, server) + .withBoundConfigScope(CONFIGURATION_SCOPE_ID, CONNECTION_ID, "projectKey", storage -> storage.withLocalOnlyIssue(aLocalOnlyIssueResolved(issueId))) .build(); - var response = backend.getIssueService().reopenIssue(new ReopenIssueParams("configScopeId", nonExistingIssueId.toString(), false)); + var response = backend.getIssueService().reopenIssue(new ReopenIssueParams(CONFIGURATION_SCOPE_ID, nonExistingIssueId.toString(), false)); assertThat(response).succeedsWithin(Duration.ofSeconds(2)); assertThat(response.get().isSuccess()).isFalse(); - var storedIssues = backend.getLocalOnlyIssueStorageService().get().loadForFile("configScopeId", Path.of("file/path")); + var storedIssues = backend.getLocalOnlyIssueStorageService().get().loadForFile(CONFIGURATION_SCOPE_ID, Path.of("file/path")); assertThat(storedIssues) .extracting(LocalOnlyIssue::getId) .containsOnly(issueId); @@ -496,15 +543,15 @@ void it_should_return_true_on_reopening_server_issue(SonarLintTestHarness harnes .resolved(); var server = harness.newFakeSonarQubeServer().start(); var backend = harness.newBackend() - .withSonarQubeConnection("connectionId", server.baseUrl(), storage -> storage + .withSonarQubeConnection(CONNECTION_ID, server.baseUrl(), storage -> storage .withProject("projectKey", project -> project.withMainBranch("main", branch -> branch.withIssue(serverIssue))) .withServerVersion("9.8")) - .withBoundConfigScope("configScopeId", "connectionId", "projectKey") + .withBoundConfigScope(CONFIGURATION_SCOPE_ID, CONNECTION_ID, "projectKey") .build(); - var reopen_response = backend.getIssueService().reopenIssue(new ReopenIssueParams("configScopeId", "myIssueKey", false)); + var reopenResponse = backend.getIssueService().reopenIssue(new ReopenIssueParams(CONFIGURATION_SCOPE_ID, "myIssueKey", false)); - assertThat(reopen_response).succeedsWithin(Duration.ofSeconds(2)); - assertThat(reopen_response.get().isSuccess()).isTrue(); + assertThat(reopenResponse).succeedsWithin(Duration.ofSeconds(2)); + assertThat(reopenResponse.get().isSuccess()).isTrue(); } } diff --git a/medium-tests/src/test/java/mediumtest/issues/TrackWithServerIssuesMediumTests.java b/medium-tests/src/test/java/mediumtest/issues/TrackWithServerIssuesMediumTests.java deleted file mode 100644 index b256838419..0000000000 --- a/medium-tests/src/test/java/mediumtest/issues/TrackWithServerIssuesMediumTests.java +++ /dev/null @@ -1,215 +0,0 @@ -/* - * SonarLint Core - Medium Tests - * Copyright (C) 2016-2025 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 mediumtest.issues; - -import java.net.URI; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.time.Duration; -import java.time.Instant; -import java.util.List; -import java.util.Map; -import java.util.concurrent.CompletableFuture; -import java.util.stream.Collectors; -import java.util.stream.IntStream; -import org.sonarsource.sonarlint.core.commons.RuleType; -import org.sonarsource.sonarlint.core.commons.api.TextRange; -import org.sonarsource.sonarlint.core.commons.api.TextRangeWithHash; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.config.binding.BindingConfigurationDto; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.config.scope.ConfigurationScopeDto; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.config.scope.DidAddConfigurationScopesParams; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.tracking.ClientTrackedFindingDto; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.tracking.LineWithHashDto; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.tracking.LocalOnlyIssueDto; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.tracking.ServerMatchedIssueDto; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.tracking.TextRangeWithHashDto; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.tracking.TrackWithServerIssuesParams; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.tracking.TrackWithServerIssuesResponse; -import org.sonarsource.sonarlint.core.rpc.protocol.common.ClientFileDto; -import org.sonarsource.sonarlint.core.rpc.protocol.common.Either; -import org.sonarsource.sonarlint.core.test.utils.SonarLintTestRpcServer; -import org.sonarsource.sonarlint.core.test.utils.junit5.SonarLintTest; -import org.sonarsource.sonarlint.core.test.utils.junit5.SonarLintTestHarness; - -import static com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor; -import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; -import static java.util.concurrent.TimeUnit.SECONDS; -import static org.assertj.core.api.Assertions.assertThat; -import static org.awaitility.Awaitility.waitAtMost; -import static org.sonarsource.sonarlint.core.rpc.protocol.common.RuleType.BUG; -import static org.sonarsource.sonarlint.core.test.utils.storage.ServerIssueFixtures.aServerIssue; - -class TrackWithServerIssuesMediumTests { - - public static final String CONFIG_SCOPE_ID = "configScopeId"; - - @SonarLintTest - void it_should_not_track_server_issues_when_configuration_scope_is_not_bound(SonarLintTestHarness harness) { - var backend = harness.newBackend() - .withUnboundConfigScope(CONFIG_SCOPE_ID) - .build(); - - var response = trackWithServerIssues(backend, - new TrackWithServerIssuesParams(CONFIG_SCOPE_ID, Map.of(Path.of("file/path"), List.of(new ClientTrackedFindingDto(null, null, null, null, "ruleKey", "message"))), false)); - - assertThat(response) - .succeedsWithin(Duration.ofSeconds(2)) - .satisfies(result -> assertThat(result.getIssuesByIdeRelativePath()) - .hasEntrySatisfying(Path.of("file/path"), issues -> assertThat(issues).hasSize(1).allSatisfy(issue -> assertThat(issue.isRight()).isTrue()))); - } - - @SonarLintTest - void it_should_track_local_only_issues(SonarLintTestHarness harness) { - var server = harness.newFakeSonarQubeServer().start(); - var backend = harness.newBackend() - .withSonarQubeConnection("connectionId", server) - .withBoundConfigScope(CONFIG_SCOPE_ID, "connectionId", "projectKey") - .build(); - - var response = trackWithServerIssues(backend, new TrackWithServerIssuesParams(CONFIG_SCOPE_ID, - Map.of(Path.of("file/path"), - List.of(new ClientTrackedFindingDto(null, null, new TextRangeWithHashDto(1, 2, 3, 4, "hash"), new LineWithHashDto(1, "linehash"), "ruleKey", "message"))), - false)); - - assertThat(response) - .succeedsWithin(Duration.ofSeconds(20)) - .satisfies(result -> assertThat(result.getIssuesByIdeRelativePath()) - .hasEntrySatisfying(Path.of("file/path"), issues -> { - assertThat(issues).hasSize(1).allSatisfy(issue -> assertThat(issue.isRight()).isTrue()); - assertThat(issues).usingRecursiveComparison().ignoringFields("lsp4jEither.right.id") - .isEqualTo(List.of(Either.forRight(new LocalOnlyIssueDto(null, null)))); - })); - } - - @SonarLintTest - void it_should_track_issues_for_unknown_branch(SonarLintTestHarness harness) { - var server = harness.newFakeSonarQubeServer().start(); - var backend = harness.newBackend() - .withSonarQubeConnection("connectionId", server) - .withBoundConfigScope(CONFIG_SCOPE_ID, "connectionId", "projectKey") - .build(); - - var response = trackWithServerIssues(backend, new TrackWithServerIssuesParams(CONFIG_SCOPE_ID, - Map.of(Path.of("file/path"), - List.of(new ClientTrackedFindingDto(null, null, new TextRangeWithHashDto(1, 2, 3, 4, "hash"), new LineWithHashDto(1, "linehash"), "ruleKey", "message"))), - false)); - - assertThat(response) - .succeedsWithin(Duration.ofSeconds(2)) - .satisfies(result -> assertThat(result.getIssuesByIdeRelativePath()) - .hasEntrySatisfying(Path.of("file/path"), issues -> { - assertThat(issues).hasSize(1).allSatisfy(issue -> assertThat(issue.isRight()).isTrue()); - assertThat(issues).usingRecursiveComparison().ignoringFields("lsp4jEither.right.id") - .isEqualTo(List.of(Either.forRight(new LocalOnlyIssueDto(null, null)))); - })); - } - - @SonarLintTest - void it_should_track_with_a_known_server_issue_at_the_same_location(SonarLintTestHarness harness) { - var configScopeId = CONFIG_SCOPE_ID; - var connectionId = "connectionId"; - var projectKey = "projectKey"; - var serverIssue = aServerIssue("issueKey").withTextRange(new TextRangeWithHash(1, 2, 3, 4, "hash")).withIntroductionDate(Instant.EPOCH.plusSeconds(1)).withType(RuleType.BUG); - var client = harness.newFakeClient().build(); - var server = harness.newFakeSonarQubeServer().withProject("projectKey").start(); - var backend = harness.newBackend() - .withSonarQubeConnection(connectionId, server, storage -> storage - .withProject(projectKey, project -> project.withMainBranch("main", branch -> branch.withIssue(serverIssue)))) - .withBoundConfigScope(configScopeId, connectionId, projectKey) - .build(client); - backend.getConfigurationService().didAddConfigurationScopes( - new DidAddConfigurationScopesParams(List.of(new ConfigurationScopeDto(configScopeId, null, true, "name", new BindingConfigurationDto(connectionId, projectKey, true))))); - - var response = trackWithServerIssues(backend, new TrackWithServerIssuesParams(configScopeId, - Map.of(Path.of("file/path"), - List.of(new ClientTrackedFindingDto(null, null, new TextRangeWithHashDto(1, 2, 3, 4, "hash"), new LineWithHashDto(1, "linehash"), "ruleKey", "message"))), - false)); - - assertThat(response) - .succeedsWithin(Duration.ofSeconds(20)) - .satisfies(result -> assertThat(result.getIssuesByIdeRelativePath()) - .hasEntrySatisfying(Path.of("file/path"), issues -> assertThat(issues).usingRecursiveComparison().ignoringFields("lsp4jEither.left.id") - .isEqualTo( - List.of((Either.forLeft( - new ServerMatchedIssueDto(null, "issueKey", 1000L, false, null, BUG, true))))))); - } - - @SonarLintTest - void it_should_translate_paths_before_matching(SonarLintTestHarness harness) { - var serverFilePath = "server/file/path"; - var ideFilePath = "ide/file/path"; - var ruleKey = "rule:key"; - var issueKey = "issueKey"; - var server = harness.newFakeSonarQubeServer().withProject("projectKey", project -> project.withFile(serverFilePath)).start(); - var client = harness.newFakeClient() - .withInitialFs(CONFIG_SCOPE_ID, List.of(new ClientFileDto(URI.create("file://foo"), Paths.get(ideFilePath), CONFIG_SCOPE_ID, null, null, null, null, null, true))) - .build(); - var serverIssue = aServerIssue(issueKey) - .withFilePath(serverFilePath) - .withRuleKey(ruleKey) - .withTextRange(new TextRangeWithHash(1, 2, 3, 4, "hash")) - .withIntroductionDate(Instant.ofEpochMilli(123456789L)).withType(RuleType.BUG); - var backend = harness.newBackend() - .withSonarQubeConnection("connectionId", server, storage -> storage - .withProject("projectKey", project -> project.withMainBranch("main", b -> b.withIssue(serverIssue)))) - .withBoundConfigScope(CONFIG_SCOPE_ID, "connectionId", "projectKey") - .build(client); - - var response = trackWithServerIssues(backend, new TrackWithServerIssuesParams(CONFIG_SCOPE_ID, - Map.of(Path.of(ideFilePath), - List.of(new ClientTrackedFindingDto(null, null, new TextRangeWithHashDto(1, 2, 3, 4, "hash"), new LineWithHashDto(1, "linehash"), ruleKey, "message"))), - false)); - - assertThat(response) - .succeedsWithin(Duration.ofSeconds(20)) - .satisfies(result -> assertThat(result.getIssuesByIdeRelativePath()) - .hasEntrySatisfying(Path.of(ideFilePath), issues -> assertThat(issues).usingRecursiveComparison().ignoringFields("lsp4jEither.left.id") - .isEqualTo( - List.of(Either.forLeft(new ServerMatchedIssueDto(null, issueKey, 123456789L, false, null, BUG, true)))))); - } - - @SonarLintTest - void it_should_download_all_issues_at_once_when_tracking_issues_from_more_than_10_files(SonarLintTestHarness harness) { - var server = harness.newFakeSonarQubeServer("9.9").withProject("projectKey", - project -> project.withBranch("main", - branch -> branch.withIssue("issueKey", "rule:key", "message", "author", "file/path", "OPEN", null, Instant.now(), new TextRange(1, 2, 3, 4)))) - .start(); - var client = harness.newFakeClient().build(); - var backend = harness.newBackend() - .withSonarQubeConnection("connectionId", server.baseUrl(), storage -> storage.withServerVersion("9.5") - .withProject("projectKey", project -> project.withMainBranch("main"))) - .withBoundConfigScope(CONFIG_SCOPE_ID, "connectionId", "projectKey") - .build(client); - var issuesByIdeRelativePath = IntStream.rangeClosed(1, 11).boxed().collect(Collectors.>toMap(index -> Path.of("file/path" + index), - i -> List.of(new ClientTrackedFindingDto(null, null, new TextRangeWithHashDto(1, 2, 3, 4, "hash"), new LineWithHashDto(1, "linehash"), "rule:key", "message")))); - - var response = trackWithServerIssues(backend, new TrackWithServerIssuesParams(CONFIG_SCOPE_ID, issuesByIdeRelativePath, true)); - - assertThat(response) - .succeedsWithin(Duration.ofSeconds(4)) - .satisfies(result -> assertThat(result.getIssuesByIdeRelativePath()) - .hasSize(11)); - waitAtMost(2, SECONDS).untilAsserted(() -> server.getMockServer().verify(getRequestedFor(urlEqualTo("/api/issues/pull?projectKey=projectKey&branchName=main")))); - } - - private CompletableFuture trackWithServerIssues(SonarLintTestRpcServer backend, TrackWithServerIssuesParams params) { - return backend.getIssueTrackingService().trackWithServerIssues(params); - } -} diff --git a/medium-tests/src/test/java/mediumtest/promotion/ExtraEnabledLanguagesInConnectedModePromotionMediumTests.java b/medium-tests/src/test/java/mediumtest/promotion/ExtraEnabledLanguagesInConnectedModePromotionMediumTests.java index 941e4eab25..6beffa38b3 100644 --- a/medium-tests/src/test/java/mediumtest/promotion/ExtraEnabledLanguagesInConnectedModePromotionMediumTests.java +++ b/medium-tests/src/test/java/mediumtest/promotion/ExtraEnabledLanguagesInConnectedModePromotionMediumTests.java @@ -29,6 +29,7 @@ import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.api.io.TempDir; import org.sonarsource.sonarlint.core.commons.log.SonarLintLogTester; +import org.sonarsource.sonarlint.core.rpc.protocol.backend.analysis.AnalyzeFilesAndTrackParams; import org.sonarsource.sonarlint.core.rpc.protocol.backend.analysis.AnalyzeFilesParams; import org.sonarsource.sonarlint.core.rpc.protocol.client.log.LogLevel; import org.sonarsource.sonarlint.core.rpc.protocol.common.ClientFileDto; @@ -61,7 +62,9 @@ void it_should_notify_clients_for_a_detected_language_that_is_enabled_only_in_co .withTelemetryEnabled() .build(fakeClient); - backend.getAnalysisService().analyzeFiles(new AnalyzeFilesParams("configScopeId", UUID.randomUUID(), List.of(abapFile.toUri()), Map.of(), 0)).join(); + backend.getAnalysisService() + .analyzeFilesAndTrack(new AnalyzeFilesAndTrackParams("configScopeId", UUID.randomUUID(), + List.of(abapFile.toUri()), Map.of(), false,0)).join(); verify(fakeClient).promoteExtraEnabledLanguagesInConnectedMode("configScopeId", Set.of(Language.ABAP)); } @@ -82,7 +85,9 @@ void it_should_not_notify_clients_when_already_in_connected_mode(SonarLintTestHa .withTelemetryEnabled() .build(fakeClient); - backend.getAnalysisService().analyzeFiles(new AnalyzeFilesParams("configScopeId", UUID.randomUUID(), List.of(abapFile.toUri()), Map.of(), 0)).join(); + backend.getAnalysisService() + .analyzeFilesAndTrack(new AnalyzeFilesAndTrackParams("configScopeId", UUID.randomUUID(), + List.of(abapFile.toUri()), Map.of(), false, 0)).join(); verify(fakeClient, after(200).never()).promoteExtraEnabledLanguagesInConnectedMode("configScopeId", Set.of(Language.ABAP)); } @@ -101,7 +106,9 @@ void it_should_not_notify_clients_when_detected_language_is_not_an_extra_languag .withTelemetryEnabled() .build(fakeClient); - backend.getAnalysisService().analyzeFiles(new AnalyzeFilesParams("configScopeId", UUID.randomUUID(), List.of(abapFile.toUri()), Map.of(), 0)).join(); + backend.getAnalysisService() + .analyzeFilesAndTrack(new AnalyzeFilesAndTrackParams("configScopeId", UUID.randomUUID(), + List.of(abapFile.toUri()), Map.of(), false, 0)).join(); verify(fakeClient, after(200).never()).promoteExtraEnabledLanguagesInConnectedMode("configScopeId", Set.of(Language.ABAP)); } @@ -120,7 +127,9 @@ void it_should_not_notify_clients_when_no_language_was_detected_during_analysis( .withTelemetryEnabled() .build(fakeClient); - backend.getAnalysisService().analyzeFiles(new AnalyzeFilesParams("configScopeId", UUID.randomUUID(), List.of(randomFile.toUri()), Map.of(), 0)).join(); + backend.getAnalysisService() + .analyzeFilesAndTrack(new AnalyzeFilesAndTrackParams("configScopeId", UUID.randomUUID(), + List.of(randomFile.toUri()), Map.of(), false, 0)).join(); verify(fakeClient, after(200).never()).promoteExtraEnabledLanguagesInConnectedMode(eq("configScopeId"), anySet()); verify(fakeClient, never()).log(argThat(logParams -> logParams.getLevel().equals(LogLevel.ERROR))); diff --git a/medium-tests/src/test/java/utils/AnalysisUtils.java b/medium-tests/src/test/java/utils/AnalysisUtils.java index af5a37c0e1..e52b5314fb 100644 --- a/medium-tests/src/test/java/utils/AnalysisUtils.java +++ b/medium-tests/src/test/java/utils/AnalysisUtils.java @@ -100,4 +100,12 @@ public static Map> getPublishedIssues(SonarLintBackend return client.getRaisedIssuesForScopeId(scopeId); } + public static void waitForRaisedIssues(SonarLintBackendFixture.FakeSonarLintRpcClient client, String scopeId) { + await().atMost(5, TimeUnit.SECONDS).untilAsserted(() -> assertThat(client.getRaisedIssuesForScopeIdAsList(scopeId)).isNotEmpty()); + } + + public static void waitForAnalysisReady(SonarLintBackendFixture.FakeSonarLintRpcClient client, String scopeId) { + await().atMost(5, TimeUnit.SECONDS).untilAsserted(() -> assertThat(client.isAnalysisReadyForScope(scopeId)).isTrue()); + } + } diff --git a/pom.xml b/pom.xml index 45b2c30baa..bee1de1df2 100644 --- a/pom.xml +++ b/pom.xml @@ -253,6 +253,7 @@ org.apache.maven.plugins maven-javadoc-plugin + 3.8.0 11 diff --git a/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/SonarLintLauncherBuilder.java b/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/SonarLintLauncherBuilder.java index 7d9f4cca9e..f8a5864c9e 100644 --- a/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/SonarLintLauncherBuilder.java +++ b/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/SonarLintLauncherBuilder.java @@ -32,8 +32,6 @@ import org.eclipse.lsp4j.jsonrpc.json.MessageJsonHandler; import org.eclipse.lsp4j.jsonrpc.json.adapters.MessageTypeAdapter; import org.sonarsource.sonarlint.core.rpc.protocol.adapter.DurationTypeAdapter; -import org.sonarsource.sonarlint.core.rpc.protocol.adapter.EitherServerOrLocalHotspotAdapterFactory; -import org.sonarsource.sonarlint.core.rpc.protocol.adapter.EitherServerOrLocalIssueAdapterFactory; import org.sonarsource.sonarlint.core.rpc.protocol.adapter.EitherStandardOrMQRModeAdapterFactory; import org.sonarsource.sonarlint.core.rpc.protocol.adapter.EitherTypeAdapter; import org.sonarsource.sonarlint.core.rpc.protocol.adapter.InstantTypeAdapter; @@ -57,8 +55,6 @@ public GsonBuilder getDefaultGsonBuilder() { return new GsonBuilder() .registerTypeAdapterFactory(new EitherTypeAdapter.Factory()) // We need to register those adapters globally, because we can't use the @JsonAdapter annotation on generic types - .registerTypeAdapterFactory(new EitherServerOrLocalHotspotAdapterFactory()) - .registerTypeAdapterFactory(new EitherServerOrLocalIssueAdapterFactory()) .registerTypeAdapterFactory(new EitherStandardOrMQRModeAdapterFactory()) .registerTypeAdapterFactory(new MessageTypeAdapter.Factory(this)) diff --git a/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/SonarLintRpcClient.java b/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/SonarLintRpcClient.java index 0df24323ef..b413a8832c 100644 --- a/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/SonarLintRpcClient.java +++ b/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/SonarLintRpcClient.java @@ -28,7 +28,6 @@ import org.sonarsource.sonarlint.core.rpc.protocol.client.OpenUrlInBrowserParams; import org.sonarsource.sonarlint.core.rpc.protocol.client.analysis.DidChangeAnalysisReadinessParams; import org.sonarsource.sonarlint.core.rpc.protocol.client.analysis.DidDetectSecretParams; -import org.sonarsource.sonarlint.core.rpc.protocol.client.analysis.DidRaiseIssueParams; import org.sonarsource.sonarlint.core.rpc.protocol.client.analysis.GetFileExclusionsParams; import org.sonarsource.sonarlint.core.rpc.protocol.client.analysis.GetFileExclusionsResponse; import org.sonarsource.sonarlint.core.rpc.protocol.client.analysis.GetInferredAnalysisPropertiesParams; @@ -254,18 +253,6 @@ default void showFixSuggestion(ShowFixSuggestionParams params) { @JsonNotification void didChangeAnalysisReadiness(DidChangeAnalysisReadinessParams params); - /** - * @deprecated Implement {@link #raiseIssues} instead. - *
- * Called as soon as one of the analyzer discovered an issue. - * A "raw" issue is an issue as it is raised by the analyzer, without any effort to match it to a previously raised issue (this happens at a later stage). - * This is to let clients track the issue with previous local issues, and potentially show them to users via streaming - */ - @Deprecated(since = "10.2") - @JsonNotification - default void didRaiseIssue(DidRaiseIssueParams params) { - } - /** * Called when clients should update the issues list in the UI. This can happen in several situations: *
    diff --git a/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/SonarLintRpcServer.java b/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/SonarLintRpcServer.java index 3a1f90fca4..cd0ca0e719 100644 --- a/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/SonarLintRpcServer.java +++ b/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/SonarLintRpcServer.java @@ -36,8 +36,6 @@ import org.sonarsource.sonarlint.core.rpc.protocol.backend.newcode.NewCodeRpcService; import org.sonarsource.sonarlint.core.rpc.protocol.backend.rules.RulesRpcService; import org.sonarsource.sonarlint.core.rpc.protocol.backend.telemetry.TelemetryRpcService; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.tracking.IssueTrackingRpcService; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.tracking.SecurityHotspotMatchingRpcService; import org.sonarsource.sonarlint.core.rpc.protocol.backend.tracking.TaintVulnerabilityTrackingRpcService; public interface SonarLintRpcServer { @@ -78,12 +76,6 @@ public interface SonarLintRpcServer { @JsonDelegate IssueRpcService getIssueService(); - @JsonDelegate - IssueTrackingRpcService getIssueTrackingService(); - - @JsonDelegate - SecurityHotspotMatchingRpcService getSecurityHotspotMatchingService(); - @JsonDelegate NewCodeRpcService getNewCodeService(); diff --git a/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/adapter/EitherServerOrLocalHotspotAdapterFactory.java b/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/adapter/EitherServerOrLocalHotspotAdapterFactory.java deleted file mode 100644 index a40d9f515d..0000000000 --- a/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/adapter/EitherServerOrLocalHotspotAdapterFactory.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * SonarLint Core - RPC Protocol - * Copyright (C) 2016-2025 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.common.Either; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.tracking.LocalOnlySecurityHotspotDto; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.tracking.ServerMatchedSecurityHotspotDto; - -public class EitherServerOrLocalHotspotAdapterFactory extends CustomEitherAdapterFactory { - - private static final TypeToken> ELEMENT_TYPE = new TypeToken<>() { - }; - - public EitherServerOrLocalHotspotAdapterFactory() { - super(ELEMENT_TYPE, ServerMatchedSecurityHotspotDto.class, LocalOnlySecurityHotspotDto.class, new EitherTypeAdapter.PropertyChecker("serverKey")); - } - -} diff --git a/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/adapter/EitherServerOrLocalIssueAdapterFactory.java b/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/adapter/EitherServerOrLocalIssueAdapterFactory.java deleted file mode 100644 index 6957cb2af0..0000000000 --- a/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/adapter/EitherServerOrLocalIssueAdapterFactory.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * SonarLint Core - RPC Protocol - * Copyright (C) 2016-2025 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.common.Either; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.tracking.LocalOnlyIssueDto; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.tracking.ServerMatchedIssueDto; - -public class EitherServerOrLocalIssueAdapterFactory extends CustomEitherAdapterFactory { - - private static final TypeToken> ELEMENT_TYPE = new TypeToken<>() { - }; - - public EitherServerOrLocalIssueAdapterFactory() { - super(ELEMENT_TYPE, ServerMatchedIssueDto.class, LocalOnlyIssueDto.class, new EitherTypeAdapter.PropertyChecker("serverKey")); - } - -} diff --git a/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/backend/analysis/AnalysisRpcService.java b/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/backend/analysis/AnalysisRpcService.java index fab19cd49b..bf789608c7 100644 --- a/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/backend/analysis/AnalysisRpcService.java +++ b/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/backend/analysis/AnalysisRpcService.java @@ -69,14 +69,6 @@ public interface AnalysisRpcService { @JsonRequest CompletableFuture getAutoDetectedNodeJs(); - /** - * @deprecated use {@link #analyzeFilesAndTrack} instead. - * Analyze the provided files. - */ - @Deprecated(since = "10.2") - @JsonRequest - CompletableFuture analyzeFiles(AnalyzeFilesParams params); - /** * Analyze and track issues in the provided files. * Issues will be reported to the client via diff --git a/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/backend/hotspot/HotspotStatus.java b/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/backend/hotspot/HotspotStatus.java index 1f7f833bb4..8b80d184e0 100644 --- a/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/backend/hotspot/HotspotStatus.java +++ b/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/backend/hotspot/HotspotStatus.java @@ -23,6 +23,5 @@ public enum HotspotStatus { TO_REVIEW, ACKNOWLEDGED, FIXED, - SAFE; - + SAFE } diff --git a/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/backend/tracking/ClientTrackedFindingDto.java b/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/backend/tracking/ClientTrackedFindingDto.java deleted file mode 100644 index e5f25a2af4..0000000000 --- a/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/backend/tracking/ClientTrackedFindingDto.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * SonarLint Core - RPC Protocol - * Copyright (C) 2016-2025 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.backend.tracking; - -import java.util.UUID; -import javax.annotation.CheckForNull; -import javax.annotation.Nullable; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.analysis.AnalysisRpcService; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.analysis.AnalyzeFilesAndTrackParams; - -/** - * @deprecated Use {@link AnalysisRpcService#analyzeFilesAndTrack(AnalyzeFilesAndTrackParams)} instead. - */ -@Deprecated(since = "10.2") -public class ClientTrackedFindingDto { - private final UUID id; - private final String serverKey; - private final TextRangeWithHashDto textRangeWithHash; - private final LineWithHashDto lineWithHash; - private final String ruleKey; - private final String message; - - /** - * @param id null when it's a first-time detected issue, else comes from a previous tracking - * @param serverKey null when it's a first-time detected issue or if the issue was never matched to a server issue previously, else the key coming from the server issue - * @param textRangeWithHash null when it's a file-level issue - * @param lineWithHash null when it's a file-level issue - */ - public ClientTrackedFindingDto(@Nullable UUID id, @Nullable String serverKey, @Nullable TextRangeWithHashDto textRangeWithHash, @Nullable LineWithHashDto lineWithHash, - String ruleKey, String message) { - this.id = id; - this.serverKey = serverKey; - this.textRangeWithHash = textRangeWithHash; - this.lineWithHash = lineWithHash; - this.ruleKey = ruleKey; - this.message = message; - } - - public UUID getId() { - return id; - } - - @CheckForNull - public String getServerKey() { - return serverKey; - } - - @CheckForNull - public TextRangeWithHashDto getTextRangeWithHash() { - return textRangeWithHash; - } - - @CheckForNull - public LineWithHashDto getLineWithHash() { - return lineWithHash; - } - - public String getRuleKey() { - return ruleKey; - } - - public String getMessage() { - return message; - } -} diff --git a/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/backend/tracking/IssueTrackingRpcService.java b/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/backend/tracking/IssueTrackingRpcService.java deleted file mode 100644 index 4d193bb604..0000000000 --- a/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/backend/tracking/IssueTrackingRpcService.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * SonarLint Core - RPC Protocol - * Copyright (C) 2016-2025 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.backend.tracking; - -import java.util.concurrent.CompletableFuture; -import org.eclipse.lsp4j.jsonrpc.services.JsonRequest; -import org.eclipse.lsp4j.jsonrpc.services.JsonSegment; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.analysis.AnalysisRpcService; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.analysis.AnalyzeFilesAndTrackParams; - -/** - * @deprecated Use {@link AnalysisRpcService#analyzeFilesAndTrack(AnalyzeFilesAndTrackParams)} instead. - */ -@JsonSegment("issueTracking") -@Deprecated(since = "10.2") -public interface IssueTrackingRpcService { - /** - * @deprecated - *

    This method accepts a list of raw issues grouped by the ide relative file path in which they were detected. - * This method returns a list of tracked issues grouped by the ide relative file path in which they were detected. - * It is guaranteed that the size and order of the tracked issues list in the response will be the same as the locally tracked issues list in the parameters. - * If the provided configuration scope is not bound, the issues are still tracked and assigned a unique identifier if they don't have one. - *

    - */ - @Deprecated(since = "10.2") - @JsonRequest - CompletableFuture trackWithServerIssues(TrackWithServerIssuesParams params); -} diff --git a/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/backend/tracking/LocalOnlyIssueDto.java b/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/backend/tracking/LocalOnlyIssueDto.java deleted file mode 100644 index 8b1a48628f..0000000000 --- a/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/backend/tracking/LocalOnlyIssueDto.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * SonarLint Core - RPC Protocol - * Copyright (C) 2016-2025 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.backend.tracking; - -import java.util.UUID; -import javax.annotation.CheckForNull; -import javax.annotation.Nullable; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.analysis.AnalysisRpcService; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.analysis.AnalyzeFilesAndTrackParams; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.issue.ResolutionStatus; - -/** - * @deprecated Use {@link AnalysisRpcService#analyzeFilesAndTrack(AnalyzeFilesAndTrackParams)} instead. - */ -@Deprecated(since = "10.2") -public class LocalOnlyIssueDto { - private final UUID id; - private final ResolutionStatus resolutionStatus; - - public LocalOnlyIssueDto(UUID id, @Nullable ResolutionStatus resolutionStatus) { - this.id = id; - this.resolutionStatus = resolutionStatus; - } - - public UUID getId() { - return id; - } - - @CheckForNull - public ResolutionStatus getResolutionStatus() { - return resolutionStatus; - } -} diff --git a/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/backend/tracking/LocalOnlySecurityHotspotDto.java b/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/backend/tracking/LocalOnlySecurityHotspotDto.java deleted file mode 100644 index 6ae7a769c2..0000000000 --- a/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/backend/tracking/LocalOnlySecurityHotspotDto.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * SonarLint Core - RPC Protocol - * Copyright (C) 2016-2025 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.backend.tracking; - -import java.util.UUID; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.analysis.AnalysisRpcService; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.analysis.AnalyzeFilesAndTrackParams; - -/** - * @deprecated Use {@link AnalysisRpcService#analyzeFilesAndTrack(AnalyzeFilesAndTrackParams)} instead. - */ -@Deprecated(since = "10.2") -public class LocalOnlySecurityHotspotDto { - private final UUID id; - - public LocalOnlySecurityHotspotDto(UUID id) { - this.id = id; - } - - public UUID getId() { - return id; - } -} diff --git a/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/backend/tracking/MatchWithServerSecurityHotspotsParams.java b/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/backend/tracking/MatchWithServerSecurityHotspotsParams.java deleted file mode 100644 index 701aa7db13..0000000000 --- a/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/backend/tracking/MatchWithServerSecurityHotspotsParams.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * SonarLint Core - RPC Protocol - * Copyright (C) 2016-2025 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.backend.tracking; - -import java.nio.file.Path; -import java.util.List; -import java.util.Map; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.analysis.AnalysisRpcService; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.analysis.AnalyzeFilesAndTrackParams; - -/** - * @deprecated Use {@link AnalysisRpcService#analyzeFilesAndTrack(AnalyzeFilesAndTrackParams)} instead. - */ -@Deprecated(since = "10.2") -public class MatchWithServerSecurityHotspotsParams { - private final String configurationScopeId; - private final Map> clientTrackedHotspotsByIdeRelativePath; - private final boolean shouldFetchHotspotsFromServer; - - public MatchWithServerSecurityHotspotsParams(String configurationScopeId, Map> clientTrackedHotspotsByIdeRelativePath, - boolean shouldFetchHotspotsFromServer) { - this.configurationScopeId = configurationScopeId; - this.clientTrackedHotspotsByIdeRelativePath = clientTrackedHotspotsByIdeRelativePath; - this.shouldFetchHotspotsFromServer = shouldFetchHotspotsFromServer; - } - - public String getConfigurationScopeId() { - return configurationScopeId; - } - - public Map> getClientTrackedHotspotsByIdeRelativePath() { - return clientTrackedHotspotsByIdeRelativePath; - } - - public boolean shouldFetchHotspotsFromServer() { - return shouldFetchHotspotsFromServer; - } -} diff --git a/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/backend/tracking/MatchWithServerSecurityHotspotsResponse.java b/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/backend/tracking/MatchWithServerSecurityHotspotsResponse.java deleted file mode 100644 index f82edbe89f..0000000000 --- a/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/backend/tracking/MatchWithServerSecurityHotspotsResponse.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * SonarLint Core - RPC Protocol - * Copyright (C) 2016-2025 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.backend.tracking; - -import java.nio.file.Path; -import java.util.List; -import java.util.Map; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.analysis.AnalysisRpcService; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.analysis.AnalyzeFilesAndTrackParams; -import org.sonarsource.sonarlint.core.rpc.protocol.common.Either; - -/** - * @deprecated Use {@link AnalysisRpcService#analyzeFilesAndTrack(AnalyzeFilesAndTrackParams)} instead. - */ -@Deprecated(since = "10.2") -public class MatchWithServerSecurityHotspotsResponse { - - private final Map>> securityHotspotsByIdeRelativePath; - - public MatchWithServerSecurityHotspotsResponse(Map>> hotspotsByServerRelativePath) { - this.securityHotspotsByIdeRelativePath = hotspotsByServerRelativePath; - } - - public Map>> getSecurityHotspotsByIdeRelativePath() { - return securityHotspotsByIdeRelativePath; - } - -} diff --git a/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/backend/tracking/SecurityHotspotMatchingRpcService.java b/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/backend/tracking/SecurityHotspotMatchingRpcService.java deleted file mode 100644 index fbec59f6b0..0000000000 --- a/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/backend/tracking/SecurityHotspotMatchingRpcService.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * SonarLint Core - RPC Protocol - * Copyright (C) 2016-2025 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.backend.tracking; - -import java.util.concurrent.CompletableFuture; -import org.eclipse.lsp4j.jsonrpc.services.JsonRequest; -import org.eclipse.lsp4j.jsonrpc.services.JsonSegment; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.analysis.AnalysisRpcService; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.analysis.AnalyzeFilesAndTrackParams; - -/** - * @deprecated Use {@link AnalysisRpcService#analyzeFilesAndTrack(AnalyzeFilesAndTrackParams)} instead. - */ -@Deprecated(since = "10.2") -@JsonSegment("hotspotMatching") -public interface SecurityHotspotMatchingRpcService { - /** - * Warning: this method will eventually become internal to the backend. It is exposed as an intermediate step during migration. - * - *

    This method accepts a list of raw security hotspots grouped by the server relative file path in which they were detected. - * This method returns a list of matched security hotspots grouped by the server relative file path in which they were detected. - * It is guaranteed that the size and order of the matched security hotspots list in the response will be the same as the locally - * tracked security hotspots list in the parameters. - * If the provided configuration scope is not bound, the security hotspots are considered local-only and assigned a unique identifier if they don't have one. - *

    - */ - @JsonRequest - CompletableFuture matchWithServerSecurityHotspots(MatchWithServerSecurityHotspotsParams params); -} diff --git a/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/backend/tracking/ServerMatchedIssueDto.java b/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/backend/tracking/ServerMatchedIssueDto.java deleted file mode 100644 index cd781fd358..0000000000 --- a/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/backend/tracking/ServerMatchedIssueDto.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * SonarLint Core - RPC Protocol - * Copyright (C) 2016-2025 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.backend.tracking; - -import java.util.UUID; -import javax.annotation.CheckForNull; -import javax.annotation.Nullable; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.analysis.AnalysisRpcService; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.analysis.AnalyzeFilesAndTrackParams; -import org.sonarsource.sonarlint.core.rpc.protocol.common.IssueSeverity; -import org.sonarsource.sonarlint.core.rpc.protocol.common.RuleType; - -/** - * @deprecated Use {@link AnalysisRpcService#analyzeFilesAndTrack(AnalyzeFilesAndTrackParams)} instead. - */ -@Deprecated(since = "10.2") -public class ServerMatchedIssueDto { - private final UUID id; - private final String serverKey; - private final long introductionDate; - private final boolean resolved; - private final IssueSeverity overriddenSeverity; - private final RuleType type; - private final boolean isOnNewCode; - - public ServerMatchedIssueDto(UUID id, String serverKey, long introductionDate, - boolean resolved, @Nullable IssueSeverity overriddenSeverity, RuleType type, boolean isOnNewCode) { - this.id = id; - this.serverKey = serverKey; - this.introductionDate = introductionDate; - this.resolved = resolved; - this.overriddenSeverity = overriddenSeverity; - this.type = type; - this.isOnNewCode = isOnNewCode; - } - - public UUID getId() { - return id; - } - - public String getServerKey() { - return serverKey; - } - - public long getIntroductionDate() { - return introductionDate; - } - - public boolean isResolved() { - return resolved; - } - - @CheckForNull - public IssueSeverity getOverriddenSeverity() { - return overriddenSeverity; - } - - public RuleType getType() { - return type; - } - - public boolean isOnNewCode() { - return isOnNewCode; - } - -} diff --git a/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/backend/tracking/ServerMatchedSecurityHotspotDto.java b/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/backend/tracking/ServerMatchedSecurityHotspotDto.java deleted file mode 100644 index 7ec12f7406..0000000000 --- a/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/backend/tracking/ServerMatchedSecurityHotspotDto.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * SonarLint Core - RPC Protocol - * Copyright (C) 2016-2025 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.backend.tracking; - -import java.util.UUID; -import org.eclipse.lsp4j.jsonrpc.services.JsonSegment; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.analysis.AnalysisRpcService; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.analysis.AnalyzeFilesAndTrackParams; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.hotspot.HotspotStatus; - -/** - * @deprecated Use {@link AnalysisRpcService#analyzeFilesAndTrack(AnalyzeFilesAndTrackParams)} instead. - */ -@JsonSegment("issueTracking") -@Deprecated(since = "10.2") -public class ServerMatchedSecurityHotspotDto { - private final UUID id; - private final String serverKey; - private final long introductionDate; - private final HotspotStatus status; - private final boolean isOnNewCode; - - public ServerMatchedSecurityHotspotDto(UUID id, String serverKey, long introductionDate, HotspotStatus status, boolean isOnNewCode) { - this.id = id; - this.serverKey = serverKey; - this.introductionDate = introductionDate; - this.status = status; - this.isOnNewCode = isOnNewCode; - } - - public UUID getId() { - return id; - } - - public String getServerKey() { - return serverKey; - } - - public long getIntroductionDate() { - return introductionDate; - } - - public HotspotStatus getStatus() { - return status; - } - - public boolean isOnNewCode() { - return isOnNewCode; - } - -} diff --git a/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/backend/tracking/TrackWithServerIssuesParams.java b/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/backend/tracking/TrackWithServerIssuesParams.java deleted file mode 100644 index bd2345c97e..0000000000 --- a/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/backend/tracking/TrackWithServerIssuesParams.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * SonarLint Core - RPC Protocol - * Copyright (C) 2016-2025 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.backend.tracking; - -import java.nio.file.Path; -import java.util.List; -import java.util.Map; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.analysis.AnalysisRpcService; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.analysis.AnalyzeFilesAndTrackParams; - -/** - * @deprecated Use {@link AnalysisRpcService#analyzeFilesAndTrack(AnalyzeFilesAndTrackParams)} instead. - */ -@Deprecated(since = "10.2") -public class TrackWithServerIssuesParams { - private final String configurationScopeId; - private final Map> clientTrackedIssuesByIdeRelativePath; - private final boolean shouldFetchIssuesFromServer; - - public TrackWithServerIssuesParams(String configurationScopeId, Map> clientTrackedIssuesByIdeRelativePath, - boolean shouldFetchIssuesFromServer) { - this.configurationScopeId = configurationScopeId; - this.clientTrackedIssuesByIdeRelativePath = clientTrackedIssuesByIdeRelativePath; - this.shouldFetchIssuesFromServer = shouldFetchIssuesFromServer; - } - - public String getConfigurationScopeId() { - return configurationScopeId; - } - - public Map> getClientTrackedIssuesByIdeRelativePath() { - return clientTrackedIssuesByIdeRelativePath; - } - - public boolean shouldFetchIssuesFromServer() { - return shouldFetchIssuesFromServer; - } -} diff --git a/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/backend/tracking/TrackWithServerIssuesResponse.java b/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/backend/tracking/TrackWithServerIssuesResponse.java deleted file mode 100644 index 5791a2f754..0000000000 --- a/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/backend/tracking/TrackWithServerIssuesResponse.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * SonarLint Core - RPC Protocol - * Copyright (C) 2016-2025 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.backend.tracking; - -import java.nio.file.Path; -import java.util.List; -import java.util.Map; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.analysis.AnalysisRpcService; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.analysis.AnalyzeFilesAndTrackParams; -import org.sonarsource.sonarlint.core.rpc.protocol.common.Either; - -/** - * @deprecated Use {@link AnalysisRpcService#analyzeFilesAndTrack(AnalyzeFilesAndTrackParams)} instead. - */ -@Deprecated(since = "10.2") -public class TrackWithServerIssuesResponse { - - private final Map>> issuesByIdeRelativePath; - - public TrackWithServerIssuesResponse(Map>> issuesByIdeRelativePath) { - this.issuesByIdeRelativePath = issuesByIdeRelativePath; - } - - public Map>> getIssuesByIdeRelativePath() { - return issuesByIdeRelativePath; - } - -} diff --git a/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/client/analysis/DidRaiseIssueParams.java b/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/client/analysis/DidRaiseIssueParams.java deleted file mode 100644 index bbbda358ee..0000000000 --- a/rpc-protocol/src/main/java/org/sonarsource/sonarlint/core/rpc/protocol/client/analysis/DidRaiseIssueParams.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * SonarLint Core - RPC Protocol - * Copyright (C) 2016-2025 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.analysis; - -import java.util.UUID; - - -@Deprecated(since = "10.2") -public class DidRaiseIssueParams { - private final String configurationScopeId; - // the ID that was provided when the analysis was triggered - private final UUID analysisId; - private final RawIssueDto rawIssue; - - public DidRaiseIssueParams(String configurationScopeId, UUID analysisId, RawIssueDto rawIssue) { - this.configurationScopeId = configurationScopeId; - this.analysisId = analysisId; - this.rawIssue = rawIssue; - } - - public String getConfigurationScopeId() { - return configurationScopeId; - } - - public UUID getAnalysisId() { - return analysisId; - } - - public RawIssueDto getRawIssue() { - return rawIssue; - } -} 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 10300e99d0..350f726494 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 @@ -43,14 +43,6 @@ public Either getConnecti return connectionParams; } - /** - * @deprecated Use {@link #getConnectionParams()}.getLeft().getServerUrl() instead. - */ - @Deprecated(since = "10.3", forRemoval = true) - public String getServerUrl() { - return connectionParams.isLeft() ? connectionParams.getLeft().getServerUrl() : null; - } - public String getTokenName() { return connectionParams.isLeft() ? connectionParams.getLeft().getTokenName() diff --git a/test-utils/src/main/java/org/sonarsource/sonarlint/core/test/utils/SonarLintTestRpcServer.java b/test-utils/src/main/java/org/sonarsource/sonarlint/core/test/utils/SonarLintTestRpcServer.java index 4a31572f3f..da1ccd4633 100644 --- a/test-utils/src/main/java/org/sonarsource/sonarlint/core/test/utils/SonarLintTestRpcServer.java +++ b/test-utils/src/main/java/org/sonarsource/sonarlint/core/test/utils/SonarLintTestRpcServer.java @@ -41,8 +41,6 @@ import org.sonarsource.sonarlint.core.rpc.protocol.backend.newcode.NewCodeRpcService; import org.sonarsource.sonarlint.core.rpc.protocol.backend.rules.RulesRpcService; import org.sonarsource.sonarlint.core.rpc.protocol.backend.telemetry.TelemetryRpcService; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.tracking.IssueTrackingRpcService; -import org.sonarsource.sonarlint.core.rpc.protocol.backend.tracking.SecurityHotspotMatchingRpcService; import org.sonarsource.sonarlint.core.rpc.protocol.backend.tracking.TaintVulnerabilityTrackingRpcService; import org.sonarsource.sonarlint.core.storage.StorageService; @@ -125,16 +123,6 @@ public IssueRpcService getIssueService() { return serverUsingRpc.getIssueService(); } - @Override - public IssueTrackingRpcService getIssueTrackingService() { - return serverUsingRpc.getIssueTrackingService(); - } - - @Override - public SecurityHotspotMatchingRpcService getSecurityHotspotMatchingService() { - return serverUsingRpc.getSecurityHotspotMatchingService(); - } - @Override public NewCodeRpcService getNewCodeService() { return serverUsingRpc.getNewCodeService();