Skip to content

Commit

Permalink
SLCORE-1158 Expose a way to request a fix suggestion
Browse files Browse the repository at this point in the history
  • Loading branch information
damien-urruty-sonarsource committed Feb 16, 2025
1 parent 286cc07 commit a6b571e
Show file tree
Hide file tree
Showing 42 changed files with 1,007 additions and 116 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ private void checkIfSoonUnsupportedOncePerConnection(Set<String> configScopeIds)
configScopeIds.forEach(configScopeId -> {
var effectiveBinding = configRepository.getEffectiveBinding(configScopeId);
if (effectiveBinding.isPresent()) {
var connectionId = effectiveBinding.get().getConnectionId();
var connectionId = effectiveBinding.get().connectionId();
oneConfigScopeIdPerConnection.putIfAbsent(connectionId, configScopeId);
}
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,13 +82,13 @@ public AnalysisEngineCache(ConfigurationRepository configurationRepository, Node
@CheckForNull
public AnalysisEngine getAnalysisEngineIfStarted(String configurationScopeId) {
return configurationRepository.getEffectiveBinding(configurationScopeId)
.map(binding -> getConnectedEngineIfStarted(binding.getConnectionId()))
.map(binding -> getConnectedEngineIfStarted(binding.connectionId()))
.orElseGet(this::getStandaloneEngineIfStarted);
}

public AnalysisEngine getOrCreateAnalysisEngine(String configurationScopeId) {
return configurationRepository.getEffectiveBinding(configurationScopeId)
.map(binding -> getOrCreateConnectedEngine(binding.getConnectionId()))
.map(binding -> getOrCreateConnectedEngine(binding.connectionId()))
.orElseGet(this::getOrCreateStandaloneEngine);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ public GetAnalysisConfigResponse getAnalysisConfig(String configScopeId, boolean
var analysisProperties = new HashMap<>(serverProperties);
analysisProperties.putAll(userAnalysisProperties);
return new GetAnalysisConfigResponse(buildConnectedActiveRules(binding, hotspotsOnly), analysisProperties, nodeJsDetailsDto,
Set.copyOf(pluginsService.getConnectedPluginPaths(binding.getConnectionId())));
Set.copyOf(pluginsService.getConnectedPluginPaths(binding.connectionId())));
})
.orElseGet(() -> new GetAnalysisConfigResponse(buildStandaloneActiveRules(), userAnalysisProperties, nodeJsDetailsDto,
Set.copyOf(pluginsService.getEmbeddedPluginPaths())));
Expand Down Expand Up @@ -328,23 +328,23 @@ private List<ActiveRuleDto> buildConnectedActiveRules(Binding binding, boolean h

LOG.debug(" * {}: {} active rules", languageKey, ruleSet.getRules().size());
for (ServerActiveRule possiblyDeprecatedActiveRuleFromStorage : ruleSet.getRules()) {
var activeRuleFromStorage = tryConvertDeprecatedKeys(binding.getConnectionId(), possiblyDeprecatedActiveRuleFromStorage);
var activeRuleFromStorage = tryConvertDeprecatedKeys(binding.connectionId(), possiblyDeprecatedActiveRuleFromStorage);
SonarLintRuleDefinition ruleOrTemplateDefinition;
if (StringUtils.isNotBlank(activeRuleFromStorage.getTemplateKey())) {
ruleOrTemplateDefinition = rulesRepository.getRule(binding.getConnectionId(), activeRuleFromStorage.getTemplateKey()).orElse(null);
ruleOrTemplateDefinition = rulesRepository.getRule(binding.connectionId(), activeRuleFromStorage.getTemplateKey()).orElse(null);
if (ruleOrTemplateDefinition == null) {
LOG.debug("Rule {} is enabled on the server, but its template {} is not available in SonarLint", activeRuleFromStorage.getRuleKey(),
activeRuleFromStorage.getTemplateKey());
continue;
}
} else {
ruleOrTemplateDefinition = rulesRepository.getRule(binding.getConnectionId(), activeRuleFromStorage.getRuleKey()).orElse(null);
ruleOrTemplateDefinition = rulesRepository.getRule(binding.connectionId(), activeRuleFromStorage.getRuleKey()).orElse(null);
if (ruleOrTemplateDefinition == null) {
LOG.debug("Rule {} is enabled on the server, but not available in SonarLint", activeRuleFromStorage.getRuleKey());
continue;
}
}
if (shouldIncludeRuleForAnalysis(binding.getConnectionId(), ruleOrTemplateDefinition, hotspotsOnly)) {
if (shouldIncludeRuleForAnalysis(binding.connectionId(), ruleOrTemplateDefinition, hotspotsOnly)) {
result.add(buildActiveRuleDto(ruleOrTemplateDefinition, activeRuleFromStorage));
}
}
Expand Down Expand Up @@ -618,7 +618,7 @@ private boolean isReadyForAnalysis(String configScopeId) {
}

private boolean isReadyForAnalysis(Binding binding) {
var pluginsValid = storageService.connection(binding.getConnectionId()).plugins().isValid();
var pluginsValid = storageService.connection(binding.connectionId()).plugins().isValid();
var bindingStorage = storageService.binding(binding);
var analyzerConfigValid = bindingStorage.analyzerConfiguration().isValid();
var findingsStorageValid = bindingStorage.findings().wasEverUpdated();
Expand All @@ -627,7 +627,7 @@ private boolean isReadyForAnalysis(Binding binding) {
// this is not strictly for analysis but for tracking
&& findingsStorageValid;
LOG.debug("isReadyForAnalysis(connectionId: {}, sonarProjectKey: {}, plugins: {}, analyzer config: {}, findings: {}) => {}",
binding.getConnectionId(), binding.getSonarProjectKey(), pluginsValid, analyzerConfigValid, findingsStorageValid, isReady);
binding.connectionId(), binding.sonarProjectKey(), pluginsValid, analyzerConfigValid, findingsStorageValid, isReady);
return isReady;
}

Expand All @@ -636,7 +636,7 @@ public boolean shouldUseEnterpriseCSharpAnalyzer(String configurationScopeId) {
if (binding.isEmpty()) {
return false;
} else {
var connectionId = binding.get().getConnectionId();
var connectionId = binding.get().connectionId();
return pluginsService.shouldUseEnterpriseCSharpAnalyzer(connectionId);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,48 +19,5 @@
*/
package org.sonarsource.sonarlint.core.commons;

import java.util.Objects;

public class Binding {

private final String connectionId;
private final String sonarProjectKey;

public Binding(String connectionId, String sonarProjectKey) {
this.connectionId = connectionId;
this.sonarProjectKey = sonarProjectKey;
}

public String getConnectionId() {
return connectionId;
}

public String getSonarProjectKey() {
return sonarProjectKey;
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
var binding = (Binding) o;
return Objects.equals(connectionId, binding.connectionId) && Objects.equals(sonarProjectKey, binding.sonarProjectKey);
}

@Override
public int hashCode() {
return Objects.hash(connectionId, sonarProjectKey);
}

@Override
public String toString() {
return "Binding{" +
"connectionId='" + connectionId + '\'' +
", sonarProjectKey='" + sonarProjectKey + '\'' +
'}';
}
public record Binding(String connectionId, String sonarProjectKey) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -98,21 +98,21 @@ private Optional<List<Path>> getPathsFromFileCache(Binding binding) {
}

private Optional<List<Path>> fetchPathsFromServer(Binding binding, SonarLintCancelMonitor cancelMonitor) {
var connectionOpt = connectionManager.tryGetConnection(binding.getConnectionId());
var connectionOpt = connectionManager.tryGetConnection(binding.connectionId());
if (connectionOpt.isEmpty()) {
LOG.debug("Connection '{}' does not exist", binding.getConnectionId());
LOG.debug("Connection '{}' does not exist", binding.connectionId());
return Optional.empty();
}
try {
return connectionManager.withValidConnectionFlatMapOptionalAndReturn(binding.getConnectionId(), serverApi -> {
List<Path> paths = fetchPathsFromServer(serverApi, binding.getSonarProjectKey(), cancelMonitor);
return connectionManager.withValidConnectionFlatMapOptionalAndReturn(binding.connectionId(), serverApi -> {
List<Path> paths = fetchPathsFromServer(serverApi, binding.sonarProjectKey(), cancelMonitor);
cacheServerPaths(binding, paths);
return Optional.of(paths);
});
} catch (CancellationException e) {
throw e;
} catch (Exception e) {
LOG.debug("Error while getting server file paths for project '{}'", binding.getSonarProjectKey(), e);
LOG.debug("Error while getting server file paths for project '{}'", binding.sonarProjectKey(), e);
return Optional.empty();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,10 +101,10 @@ public boolean computeIfExcluded(URI fileUri, SonarLintCancelMonitor cancelMonit
if (effectiveBindingOpt.isEmpty()) {
return false;
}
var storage = storageService.connection(effectiveBindingOpt.get().getConnectionId());
var storage = storageService.connection(effectiveBindingOpt.get().connectionId());
AnalyzerConfiguration analyzerConfig;
try {
analyzerConfig = storage.project(effectiveBindingOpt.get().getSonarProjectKey()).analyzerConfiguration().read();
analyzerConfig = storage.project(effectiveBindingOpt.get().sonarProjectKey()).analyzerConfiguration().read();
} catch (StorageException e) {
LOG.debug("Unable to read settings in local storage", e);
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ public HotspotService(SonarLintRpcClient client, StorageService storageService,

public void openHotspotInBrowser(String configScopeId, String hotspotKey) {
var effectiveBinding = configurationRepository.getEffectiveBinding(configScopeId);
var endpointParams = effectiveBinding.flatMap(binding -> connectionRepository.getEndpointParams(binding.getConnectionId()));
var endpointParams = effectiveBinding.flatMap(binding -> connectionRepository.getEndpointParams(binding.connectionId()));
if (effectiveBinding.isEmpty() || endpointParams.isEmpty()) {
LOG.warn("Configuration scope {} is not bound properly, unable to open hotspot", configScopeId);
return;
Expand All @@ -93,7 +93,7 @@ public void openHotspotInBrowser(String configScopeId, String hotspotKey) {
return;
}

var url = buildHotspotUrl(effectiveBinding.get().getSonarProjectKey(), branchName.get(), hotspotKey, endpointParams.get());
var url = buildHotspotUrl(effectiveBinding.get().sonarProjectKey(), branchName.get(), hotspotKey, endpointParams.get());

client.openUrlInBrowser(new OpenUrlInBrowserParams(url));

Expand All @@ -110,7 +110,7 @@ public CheckLocalDetectionSupportedResponse checkLocalDetectionSupported(String
if (effectiveBinding.isEmpty()) {
return new CheckLocalDetectionSupportedResponse(false, NO_BINDING_REASON);
}
var connectionId = effectiveBinding.get().getConnectionId();
var connectionId = effectiveBinding.get().connectionId();
if (connectionRepository.getConnectionById(connectionId) == null) {
var error = new ResponseError(SonarLintRpcErrorCode.CONNECTION_NOT_FOUND, "The provided configuration scope is bound to an unknown connection: " + connectionId,
connectionId);
Expand Down Expand Up @@ -146,7 +146,7 @@ public void changeStatus(String configurationScopeId, String hotspotKey, Hotspot
LOG.debug("No binding for config scope {}", configurationScopeId);
return;
}
connectionManager.withValidConnection(effectiveBindingOpt.get().getConnectionId(), serverApi -> {
connectionManager.withValidConnection(effectiveBindingOpt.get().connectionId(), serverApi -> {
serverApi.hotspot().changeStatus(hotspotKey, newStatus, cancelMonitor);
saveStatusInStorage(effectiveBindingOpt.get(), hotspotKey, newStatus);
telemetryService.hotspotStatusChanged();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,14 +126,14 @@ public IssueService(ConfigurationRepository configurationRepository, ConnectionM

public void changeStatus(String configurationScopeId, String issueKey, ResolutionStatus newStatus, boolean isTaintIssue, SonarLintCancelMonitor cancelMonitor) {
var binding = configurationRepository.getEffectiveBindingOrThrow(configurationScopeId);
var serverConnection = connectionManager.getConnectionOrThrow(binding.getConnectionId());
var serverConnection = connectionManager.getConnectionOrThrow(binding.connectionId());
var reviewStatus = transitionByResolutionStatus.get(newStatus);
var projectServerIssueStore = storageService.binding(binding).findings();
boolean isServerIssue = projectServerIssueStore.containsIssue(issueKey);
if (isServerIssue) {
serverConnection.withClientApi(serverApi -> serverApi.issue().changeStatus(issueKey, reviewStatus, cancelMonitor));
projectServerIssueStore.updateIssueResolutionStatus(issueKey, isTaintIssue, true)
.ifPresent(issue -> eventPublisher.publishEvent(new ServerIssueStatusChangedEvent(binding.getConnectionId(), binding.getSonarProjectKey(), issue)));
.ifPresent(issue -> eventPublisher.publishEvent(new ServerIssueStatusChangedEvent(binding.connectionId(), binding.sonarProjectKey(), issue)));
} else {
var localIssueOpt = asUUID(issueKey)
.flatMap(localOnlyIssueRepository::findByKey);
Expand All @@ -145,7 +145,7 @@ public void changeStatus(String configurationScopeId, String issueKey, Resolutio
issue.resolve(coreStatus);
var localOnlyIssueStore = localOnlyIssueStorageService.get();
serverConnection.withClientApi(serverApi -> serverApi.issue()
.anticipatedTransitions(binding.getSonarProjectKey(), concat(localOnlyIssueStore.loadAll(configurationScopeId), issue), cancelMonitor));
.anticipatedTransitions(binding.sonarProjectKey(), concat(localOnlyIssueStore.loadAll(configurationScopeId), issue), cancelMonitor));
localOnlyIssueStore.storeLocalOnlyIssue(configurationScopeId, issue);
eventPublisher.publishEvent(new LocalOnlyIssueStatusChangedEvent(issue));
}
Expand All @@ -163,8 +163,8 @@ private static List<LocalOnlyIssue> subtract(List<LocalOnlyIssue> allIssues, Lis

public boolean checkAnticipatedStatusChangeSupported(String configScopeId) {
var binding = configurationRepository.getEffectiveBindingOrThrow(configScopeId);
var connectionId = binding.getConnectionId();
return connectionManager.getConnectionOrThrow(binding.getConnectionId())
var connectionId = binding.connectionId();
return connectionManager.getConnectionOrThrow(binding.connectionId())
.withClientApiAndReturn(serverApi -> checkAnticipatedStatusChangeSupported(serverApi, connectionId));
}

Expand Down Expand Up @@ -260,7 +260,7 @@ public boolean reopenIssue(String configurationScopeId, String issueId, boolean
var projectServerIssueStore = storageService.binding(binding).findings();
boolean isServerIssue = projectServerIssueStore.containsIssue(issueId);
if (isServerIssue) {
return connectionManager.getConnectionOrThrow(binding.getConnectionId())
return connectionManager.getConnectionOrThrow(binding.connectionId())
.withClientApiAndReturn(serverApi -> reopenServerIssue(serverApi, binding, issueId, projectServerIssueStore, isTaintIssue, cancelMonitor));
} else {
return reopenLocalIssue(issueId, configurationScopeId, cancelMonitor);
Expand All @@ -281,17 +281,17 @@ private void removeAllIssuesForFile(XodusLocalOnlyIssueStore localOnlyIssueStore
var issuesForFile = localOnlyIssueStore.loadForFile(configurationScopeId, filePath);
var issuesToSync = subtract(allIssues, issuesForFile);
var binding = configurationRepository.getEffectiveBindingOrThrow(configurationScopeId);
connectionManager.getConnectionOrThrow(binding.getConnectionId())
.withClientApi(serverApi -> serverApi.issue().anticipatedTransitions(binding.getSonarProjectKey(), issuesToSync, cancelMonitor));
connectionManager.getConnectionOrThrow(binding.connectionId())
.withClientApi(serverApi -> serverApi.issue().anticipatedTransitions(binding.sonarProjectKey(), issuesToSync, cancelMonitor));
}

private void removeIssueOnServer(XodusLocalOnlyIssueStore localOnlyIssueStore,
String configurationScopeId, UUID issueId, SonarLintCancelMonitor cancelMonitor) {
var allIssues = localOnlyIssueStore.loadAll(configurationScopeId);
var issuesToSync = allIssues.stream().filter(it -> !it.getId().equals(issueId)).toList();
var binding = configurationRepository.getEffectiveBindingOrThrow(configurationScopeId);
connectionManager.getConnectionOrThrow(binding.getConnectionId())
.withClientApi(serverApi -> serverApi.issue().anticipatedTransitions(binding.getSonarProjectKey(), issuesToSync, cancelMonitor));
connectionManager.getConnectionOrThrow(binding.connectionId())
.withClientApi(serverApi -> serverApi.issue().anticipatedTransitions(binding.sonarProjectKey(), issuesToSync, cancelMonitor));
}

private void setCommentOnLocalOnlyIssue(String configurationScopeId, UUID issueId, String comment, SonarLintCancelMonitor cancelMonitor) {
Expand All @@ -305,8 +305,8 @@ private void setCommentOnLocalOnlyIssue(String configurationScopeId, UUID issueI
var issuesToSync = localOnlyIssueStore.loadAll(configurationScopeId);
issuesToSync.replaceAll(issue -> issue.getId().equals(issueId) ? commentedIssue : issue);
var binding = configurationRepository.getEffectiveBindingOrThrow(configurationScopeId);
connectionManager.getConnectionOrThrow(binding.getConnectionId())
.withClientApi(serverApi -> serverApi.issue().anticipatedTransitions(binding.getSonarProjectKey(), issuesToSync, cancelMonitor));
connectionManager.getConnectionOrThrow(binding.connectionId())
.withClientApi(serverApi -> serverApi.issue().anticipatedTransitions(binding.sonarProjectKey(), issuesToSync, cancelMonitor));
localOnlyIssueStore.storeLocalOnlyIssue(configurationScopeId, commentedIssue);
}
} else {
Expand All @@ -321,15 +321,15 @@ private static ResponseErrorException issueNotFoundException(String issueId) {

private void addCommentOnServerIssue(String configurationScopeId, String issueKey, String comment, SonarLintCancelMonitor cancelMonitor) {
var binding = configurationRepository.getEffectiveBindingOrThrow(configurationScopeId);
connectionManager.getConnectionOrThrow(binding.getConnectionId())
connectionManager.getConnectionOrThrow(binding.connectionId())
.withClientApi(serverApi -> serverApi.issue().addComment(issueKey, comment, cancelMonitor));
}

private boolean reopenServerIssue(ServerApi connection, Binding binding, String issueId, ProjectServerIssueStore projectServerIssueStore, boolean isTaintIssue,
SonarLintCancelMonitor cancelMonitor) {
connection.issue().changeStatus(issueId, Transition.REOPEN, cancelMonitor);
var serverIssue = projectServerIssueStore.updateIssueResolutionStatus(issueId, isTaintIssue, false);
serverIssue.ifPresent(issue -> eventPublisher.publishEvent(new ServerIssueStatusChangedEvent(binding.getConnectionId(), binding.getSonarProjectKey(), issue)));
serverIssue.ifPresent(issue -> eventPublisher.publishEvent(new ServerIssueStatusChangedEvent(binding.connectionId(), binding.sonarProjectKey(), issue)));
return true;
}

Expand All @@ -349,7 +349,7 @@ public EffectiveIssueDetailsDto getEffectiveIssueDetails(String configurationSco
var effectiveBinding = configurationRepository.getEffectiveBinding(configurationScopeId);
String connectionId = null;
if (effectiveBinding.isPresent()) {
connectionId = effectiveBinding.get().getConnectionId();
connectionId = effectiveBinding.get().connectionId();
}
var isMQRMode = severityModeService.isMQRModeForConnection(connectionId);
var newCodeDefinition = newCodeService.getFullNewCodeDefinition(configurationScopeId).orElseGet(NewCodeDefinition::withAlwaysNew);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ private List<SkippedPlugin> getSkippedPluginsToNotify(String configurationScopeI
@CheckForNull
private List<SkippedPlugin> getSkippedPlugins(String configurationScopeId) {
return configurationRepository.getEffectiveBinding(configurationScopeId)
.map(binding -> skippedPluginsRepository.getSkippedPlugins(binding.getConnectionId()))
.map(binding -> skippedPluginsRepository.getSkippedPlugins(binding.connectionId()))
.orElseGet(skippedPluginsRepository::getSkippedEmbeddedPlugins);
}
}
Loading

0 comments on commit a6b571e

Please sign in to comment.