Skip to content

Commit 9b107a8

Browse files
SLCORE-1528 Allow clients to fetch Dependency Risk description fields
1 parent 7dd8e36 commit 9b107a8

File tree

10 files changed

+866
-12
lines changed

10 files changed

+866
-12
lines changed

API_CHANGES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
* Allow changing status of SCA issues via `org.sonarsource.sonarlint.core.rpc.protocol.backend.sca.ScaRpcService.changeStatus`.
66
* Required parameters are `configScopeId`, `issueId` and `transition`.
77
* If transition is `ACCEPT`, `FIXED`, or `SAFE`, a `comment` field is mandatory
8+
* Add a new `org.sonarsource.sonarlint.core.rpc.protocol.backend.sca.ScaRpcService.getDependencyRiskDetails`.
89

910
# 10.26
1011

backend/core/src/main/java/org/sonarsource/sonarlint/core/sca/ScaService.java

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,19 @@
2121

2222
import java.util.UUID;
2323
import javax.annotation.CheckForNull;
24+
import org.eclipse.lsp4j.jsonrpc.ResponseErrorException;
25+
import org.eclipse.lsp4j.jsonrpc.messages.ResponseError;
2426
import org.sonarsource.sonarlint.core.SonarQubeClientManager;
2527
import org.sonarsource.sonarlint.core.branch.SonarProjectBranchTrackingService;
2628
import org.sonarsource.sonarlint.core.commons.log.SonarLintLogger;
2729
import org.sonarsource.sonarlint.core.commons.progress.SonarLintCancelMonitor;
2830
import org.sonarsource.sonarlint.core.repository.config.ConfigurationRepository;
31+
import org.sonarsource.sonarlint.core.rpc.protocol.SonarLintRpcErrorCode;
2932
import org.sonarsource.sonarlint.core.rpc.protocol.backend.sca.DependencyRiskTransition;
33+
import org.sonarsource.sonarlint.core.rpc.protocol.backend.tracking.AffectedPackageDto;
34+
import org.sonarsource.sonarlint.core.rpc.protocol.backend.sca.GetDependencyRiskDetailsResponse;
35+
import org.sonarsource.sonarlint.core.rpc.protocol.backend.tracking.ScaIssueDto;
36+
import org.sonarsource.sonarlint.core.serverapi.sca.GetIssueReleaseResponse;
3037
import org.sonarsource.sonarlint.core.serverconnection.issues.ServerScaIssue;
3138
import org.sonarsource.sonarlint.core.storage.StorageService;
3239

@@ -80,6 +87,23 @@ public void changeStatus(String configurationScopeId, UUID issueReleaseKey, Depe
8087
serverConnection.withClientApi(serverApi -> serverApi.sca().changeStatus(issueReleaseKey, transition.name(), comment, cancelMonitor));
8188
}
8289

90+
public GetDependencyRiskDetailsResponse getDependencyRiskDetails(String configurationScopeId, String dependencyRiskKey, SonarLintCancelMonitor cancelMonitor) {
91+
var configScope = configurationRepository.getConfigurationScope(configurationScopeId);
92+
if (configScope == null) {
93+
var error = new ResponseError(SonarLintRpcErrorCode.CONFIG_SCOPE_NOT_FOUND, "The provided configuration scope does not exist: " + configurationScopeId, configurationScopeId);
94+
throw new ResponseErrorException(error);
95+
}
96+
var effectiveBinding = configurationRepository.getEffectiveBinding(configurationScopeId);
97+
if (effectiveBinding.isEmpty()) {
98+
var error = new ResponseError(SonarLintRpcErrorCode.CONFIG_SCOPE_NOT_BOUND,
99+
"The provided configuration scope is not bound to a SonarQube/SonarCloud project: " + configurationScopeId, configurationScopeId);
100+
throw new ResponseErrorException(error);
101+
}
102+
var apiClient = sonarQubeClientManager.getClientOrThrow(effectiveBinding.get().connectionId());
103+
var serverResponse = apiClient.withClientApiAndReturn(serverApi -> serverApi.sca().getIssueRelease(dependencyRiskKey, cancelMonitor));
104+
return convertToRpcResponse(serverResponse);
105+
}
106+
83107
private static ServerScaIssue.Transition adaptTransition(DependencyRiskTransition transition) {
84108
return switch (transition) {
85109
case REOPEN -> ServerScaIssue.Transition.REOPEN;
@@ -90,6 +114,31 @@ private static ServerScaIssue.Transition adaptTransition(DependencyRiskTransitio
90114
};
91115
}
92116

117+
private static GetDependencyRiskDetailsResponse convertToRpcResponse(GetIssueReleaseResponse serverResponse) {
118+
var affectedPackages = serverResponse.affectedPackages().stream()
119+
.map(pkg -> AffectedPackageDto.builder()
120+
.purl(pkg.purl())
121+
.recommendation(pkg.recommendation())
122+
.impactScore(pkg.recommendationDetails().impactScore())
123+
.impactDescription(pkg.recommendationDetails().impactDescription())
124+
.realIssue(pkg.recommendationDetails().realIssue())
125+
.falsePositiveReason(pkg.recommendationDetails().falsePositiveReason())
126+
.includesDev(pkg.recommendationDetails().includesDev())
127+
.specificMethodsAffected(pkg.recommendationDetails().specificMethodsAffected())
128+
.specificMethodsDescription(pkg.recommendationDetails().specificMethodsDescription())
129+
.otherConditions(pkg.recommendationDetails().otherConditions())
130+
.otherConditionsDescription(pkg.recommendationDetails().otherConditionsDescription())
131+
.workaroundAvailable(pkg.recommendationDetails().workaroundAvailable())
132+
.workaroundDescription(pkg.recommendationDetails().workaroundDescription())
133+
.visibility(pkg.recommendationDetails().visibility())
134+
.build())
135+
.toList();
136+
137+
return new GetDependencyRiskDetailsResponse(serverResponse.key(), ScaIssueDto.Severity.valueOf(serverResponse.severity().name()), serverResponse.release().packageName(),
138+
serverResponse.release().version(), ScaIssueDto.Type.valueOf(serverResponse.type().name()), serverResponse.vulnerability().vulnerabilityId(),
139+
serverResponse.vulnerability().description(), affectedPackages);
140+
}
141+
93142
public static class ScaIssueNotFoundException extends RuntimeException {
94143
private final String issueKey;
95144

backend/rpc-impl/src/main/java/org/sonarsource/sonarlint/core/rpc/impl/ScaRpcServiceDelegate.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
import org.eclipse.lsp4j.jsonrpc.messages.ResponseError;
2525
import org.sonarsource.sonarlint.core.rpc.protocol.SonarLintRpcErrorCode;
2626
import org.sonarsource.sonarlint.core.rpc.protocol.backend.sca.ChangeScaIssueStatusParams;
27+
import org.sonarsource.sonarlint.core.rpc.protocol.backend.sca.GetDependencyRiskDetailsParams;
28+
import org.sonarsource.sonarlint.core.rpc.protocol.backend.sca.GetDependencyRiskDetailsResponse;
2729
import org.sonarsource.sonarlint.core.rpc.protocol.backend.sca.ScaRpcService;
2830
import org.sonarsource.sonarlint.core.sca.ScaService;
2931

@@ -44,7 +46,7 @@ public CompletableFuture<Void> changeStatus(ChangeScaIssueStatusParams params) {
4446
params.getComment(),
4547
cancelMonitor);
4648
} catch (ScaService.ScaIssueNotFoundException e) {
47-
var error = new ResponseError(SonarLintRpcErrorCode.ISSUE_NOT_FOUND,
49+
var error = new ResponseError(SonarLintRpcErrorCode.ISSUE_NOT_FOUND,
4850
"Dependency Risk with key " + e.getIssueKey() + " was not found", e.getIssueKey());
4951
throw new ResponseErrorException(error);
5052
} catch (IllegalArgumentException e) {
@@ -53,4 +55,10 @@ public CompletableFuture<Void> changeStatus(ChangeScaIssueStatusParams params) {
5355
}
5456
}, params.getConfigurationScopeId());
5557
}
58+
59+
@Override
60+
public CompletableFuture<GetDependencyRiskDetailsResponse> getDependencyRiskDetails(GetDependencyRiskDetailsParams params) {
61+
return requestAsync(cancelMonitor -> getBean(ScaService.class)
62+
.getDependencyRiskDetails(params.getConfigurationScopeId(), params.getDependencyRiskKey(), cancelMonitor), params.getConfigurationScopeId());
63+
}
5664
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*
2+
* SonarLint Core - Server API
3+
* Copyright (C) 2016-2025 SonarSource SA
4+
* mailto:info AT sonarsource DOT com
5+
*
6+
* This program is free software; you can redistribute it and/or
7+
* modify it under the terms of the GNU Lesser General Public
8+
* License as published by the Free Software Foundation; either
9+
* version 3 of the License, or (at your option) any later version.
10+
*
11+
* This program is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14+
* Lesser General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU Lesser General Public License
17+
* along with this program; if not, write to the Free Software Foundation,
18+
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19+
*/
20+
package org.sonarsource.sonarlint.core.serverapi.sca;
21+
22+
import java.util.List;
23+
24+
public record GetIssueReleaseResponse(String key, Severity severity, Release release, Type type, Vulnerability vulnerability, List<AffectedPackage> affectedPackages) {
25+
public enum Severity {
26+
INFO, LOW, MEDIUM, HIGH, BLOCKER
27+
}
28+
29+
public record Release(String packageName, String version) {
30+
}
31+
32+
public enum Type {
33+
VULNERABILITY, PROHIBITED_LICENSE
34+
}
35+
36+
public record Vulnerability(String vulnerabilityId, String description) {
37+
}
38+
39+
public record AffectedPackage(
40+
String purl,
41+
String recommendation,
42+
RecommendationDetails recommendationDetails) {
43+
}
44+
45+
public record RecommendationDetails(
46+
int impactScore,
47+
String impactDescription,
48+
boolean realIssue,
49+
String falsePositiveReason,
50+
boolean includesDev,
51+
boolean specificMethodsAffected,
52+
String specificMethodsDescription,
53+
boolean otherConditions,
54+
String otherConditionsDescription,
55+
boolean workaroundAvailable,
56+
String workaroundDescription,
57+
String visibility) {
58+
}
59+
}

backend/server-api/src/main/java/org/sonarsource/sonarlint/core/serverapi/sca/ScaApi.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,13 @@ public GetIssuesReleasesResponse getIssuesReleases(String projectKey, String bra
5959
return new GetIssuesReleasesResponse(allIssuesReleases, new GetIssuesReleasesResponse.Page(allIssuesReleases.size()));
6060
}
6161

62+
public GetIssueReleaseResponse getIssueRelease(String key, SonarLintCancelMonitor cancelMonitor) {
63+
var url = "/api/v2/sca/issues-releases/" + UrlUtils.urlEncode(key);
64+
try (var response = serverApiHelper.get(url, cancelMonitor)) {
65+
return new Gson().fromJson(new InputStreamReader(response.bodyAsStream(), StandardCharsets.UTF_8), GetIssueReleaseResponse.class);
66+
}
67+
}
68+
6269
public void changeStatus(UUID issueReleaseKey, String transitionKey, String comment, SonarLintCancelMonitor cancelMonitor) {
6370
var body = new ChangeStatusRequestBody(issueReleaseKey.toString(), transitionKey, comment);
6471
var url = "/api/v2/sca/issues-releases/change-status";

0 commit comments

Comments
 (0)