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 19, 2025
1 parent d366685 commit 4dfa6d2
Show file tree
Hide file tree
Showing 63 changed files with 1,091 additions and 162 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ private boolean checkIfBearerIsSupported(EndpointParams params) {
public ServerApi getServerApi(String baseUrl, @Nullable String organization, String token) {
var isSonarCloud = sonarCloudActiveEnvironment.isSonarQubeCloud(baseUrl);

var params = new EndpointParams(baseUrl, isSonarCloud, organization);
var params = new EndpointParams(baseUrl, baseUrl, isSonarCloud, organization);
var isBearerSupported = checkIfBearerIsSupported(params);
return new ServerApi(params, httpClientProvider.getHttpClientWithPreemptiveAuth(token, isBearerSupported));
}
Expand All @@ -115,15 +115,18 @@ private ServerApi getServerApiOrThrow(String connectionId) {
* Used to do SonarCloud requests before knowing the organization
*/
public ServerApi getForSonarCloudNoOrg(Either<TokenDto, UsernamePasswordDto> credentials, SonarCloudRegion region) {
var endpointParams = new EndpointParams(sonarCloudActiveEnvironment.getUri(region).toString(), true, null);
var endpointParams = new EndpointParams(sonarCloudActiveEnvironment.getUri(region).toString(), sonarCloudActiveEnvironment.getApiUri(region).toString(), true, null);
var httpClient = getClientFor(endpointParams, credentials);
return new ServerApi(new ServerApiHelper(endpointParams, httpClient));
}

public ServerApi getForTransientConnection(Either<TransientSonarQubeConnectionDto, TransientSonarCloudConnectionDto> transientConnection) {
var endpointParams = transientConnection.map(
sq -> new EndpointParams(sq.getServerUrl(), false, null),
sc -> new EndpointParams(sonarCloudActiveEnvironment.getUri(SonarCloudRegion.valueOf(sc.getRegion().toString())).toString(), true, sc.getOrganization()));
sq -> new EndpointParams(sq.getServerUrl(), null, false, null),
sc -> {
var region = SonarCloudRegion.valueOf(sc.getRegion().toString());
return new EndpointParams(sonarCloudActiveEnvironment.getUri(region).toString(), sonarCloudActiveEnvironment.getApiUri(region).toString(), true, sc.getOrganization());
});
var httpClient = getClientFor(endpointParams, transientConnection
.map(TransientSonarQubeConnectionDto::getCredentials, TransientSonarCloudConnectionDto::getCredentials));
return new ServerApi(new ServerApiHelper(endpointParams, httpClient));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,9 @@ private static SonarQubeConnectionConfiguration adapt(SonarQubeConnectionConfigu
}

private SonarCloudConnectionConfiguration adapt(SonarCloudConnectionConfigurationDto scDto) {
return new SonarCloudConnectionConfiguration(sonarCloudActiveEnvironment.getUri(SonarCloudRegion.valueOf(scDto.getRegion().toString())), scDto.getConnectionId(),
scDto.getOrganization(), SonarCloudRegion.valueOf(scDto.getRegion().toString()), scDto.isDisableNotifications());
var region = SonarCloudRegion.valueOf(scDto.getRegion().toString());
return new SonarCloudConnectionConfiguration(sonarCloudActiveEnvironment.getUri(region), sonarCloudActiveEnvironment.getApiUri(region), scDto.getConnectionId(),
scDto.getOrganization(), region, scDto.isDisableNotifications());
}

private static void putAndLogIfDuplicateId(Map<String, AbstractConnectionConfiguration> map, AbstractConnectionConfiguration config) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ public URI getUri(SonarCloudRegion region) {
return alternativeUris != null ? alternativeUris.productionUri : region.getProductionUri();
}

public URI getApiUri(SonarCloudRegion region) {
return alternativeUris != null ? alternativeUris.productionUri : region.getApiProductionUri();
}

public URI getWebSocketsEndpointUri(SonarCloudRegion region) {
return alternativeUris != null ? alternativeUris.wsUri : region.getWebSocketUri();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,27 @@
import java.net.URI;

public enum SonarCloudRegion {
EU("https://sonarcloud.io", "wss://events-api.sonarcloud.io/"),
US("https://us.sonarcloud.io", "wss://events-api.us.sonarcloud.io/");
EU("https://sonarcloud.io", "https://api.sonarcloud.io", "wss://events-api.sonarcloud.io/"),
US("https://us.sonarcloud.io", "https://api.us.sonarcloud.io", "wss://events-api.us.sonarcloud.io/");

private final URI productionUri;
private final URI apiProductionUri;
private final URI webSocketUri;

SonarCloudRegion(String productionUri, String webSocketUri) {
SonarCloudRegion(String productionUri, String apiProductionUri, String webSocketUri) {
this.productionUri = URI.create(productionUri);
this.apiProductionUri = URI.create(apiProductionUri);
this.webSocketUri = URI.create(webSocketUri);
}

public URI getProductionUri() {
return productionUri;
}

public URI getApiProductionUri() {
return apiProductionUri;
}

public URI getWebSocketUri() {
return webSocketUri;
}
Expand Down
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 @@ -83,13 +83,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
Loading

0 comments on commit 4dfa6d2

Please sign in to comment.