diff options
author | Pierre Guillot <pierre.guillot@sonarsource.com> | 2024-12-10 15:29:09 +0100 |
---|---|---|
committer | Steve Marion <steve.marion@sonarsource.com> | 2024-12-18 11:13:21 +0100 |
commit | d639a965bce7acafb004906cd07a8f0b5f7af993 (patch) | |
tree | 647cd646abddb12dfeeef7e637aa33b4658f1049 /sonar-scanner-engine/src/main | |
parent | 451c1c2e4856ec3df87f86189fcdb25b31794027 (diff) | |
download | sonarqube-d639a965bce7acafb004906cd07a8f0b5f7af993.tar.gz sonarqube-d639a965bce7acafb004906cd07a8f0b5f7af993.zip |
SONAR-22998 fetch active rules with a dedicated endpoint
Co-authored-by: Julien HENRY <julien.henry@sonarsource.com>
Diffstat (limited to 'sonar-scanner-engine/src/main')
5 files changed, 97 insertions, 145 deletions
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/language/DefaultLanguagesLoader.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/language/DefaultLanguagesLoader.java index 58aeb9695f8..4a34d21d016 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/language/DefaultLanguagesLoader.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/language/DefaultLanguagesLoader.java @@ -39,8 +39,7 @@ public class DefaultLanguagesLoader implements LanguagesLoader { "js", "javascript", "ts", "typescript", "py", "python", - "web", "html" - ); + "web", "html"); private final DefaultScannerWsClient wsClient; @@ -76,7 +75,6 @@ public class DefaultLanguagesLoader implements LanguagesLoader { return new Language(lang); } - private String[] getFileSuffixes(String languageKey) { return getPropertyForLanguage("sonar.%s.file.suffixes", languageKey); } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/rule/ActiveRulesLoader.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/rule/ActiveRulesLoader.java index 1bd49f3ec27..06358376c69 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/rule/ActiveRulesLoader.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/rule/ActiveRulesLoader.java @@ -22,5 +22,5 @@ package org.sonar.scanner.rule; import java.util.List; public interface ActiveRulesLoader { - List<LoadedActiveRule> load(String qualityProfileKey); + List<LoadedActiveRule> load(String projectKey); } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/rule/ActiveRulesProvider.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/rule/ActiveRulesProvider.java index eb703522dc3..a6d7f7666aa 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/rule/ActiveRulesProvider.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/rule/ActiveRulesProvider.java @@ -19,21 +19,16 @@ */ package org.sonar.scanner.rule; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashSet; -import java.util.List; import java.util.Map; -import java.util.Set; import org.sonar.api.batch.rule.internal.ActiveRulesBuilder; import org.sonar.api.batch.rule.internal.DefaultActiveRules; import org.sonar.api.batch.rule.internal.NewActiveRule; import org.sonar.api.issue.impact.Severity; import org.sonar.api.issue.impact.SoftwareQuality; -import org.sonar.api.rule.RuleKey; import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; import org.sonar.api.utils.log.Profiler; +import org.sonar.scanner.bootstrap.ScannerProperties; import org.springframework.context.annotation.Bean; /** @@ -45,34 +40,22 @@ public class ActiveRulesProvider { private static final String LOG_MSG = "Load active rules"; @Bean("ActiveRules") - public DefaultActiveRules provide(ActiveRulesLoader loader, QualityProfiles qProfiles) { + public DefaultActiveRules provide(ActiveRulesLoader loader, ScannerProperties props) { Profiler profiler = Profiler.create(LOG).startInfo(LOG_MSG); - DefaultActiveRules activeRules = load(loader, qProfiles); + DefaultActiveRules activeRules = load(loader, props.getProjectKey()); profiler.stopInfo(); return activeRules; } - private static DefaultActiveRules load(ActiveRulesLoader loader, QualityProfiles qProfiles) { - - Collection<String> qProfileKeys = getKeys(qProfiles); - Set<RuleKey> loadedRulesKey = new HashSet<>(); + private static DefaultActiveRules load(ActiveRulesLoader loader, String projectKey) { ActiveRulesBuilder builder = new ActiveRulesBuilder(); - - for (String qProfileKey : qProfileKeys) { - Collection<LoadedActiveRule> qProfileRules = load(loader, qProfileKey); - - for (LoadedActiveRule r : qProfileRules) { - if (!loadedRulesKey.contains(r.getRuleKey())) { - loadedRulesKey.add(r.getRuleKey()); - builder.addRule(transform(r, qProfileKey, r.getDeprecatedKeys())); - } - } - } - + loader.load(projectKey).stream() + .map(ActiveRulesProvider::transform) + .forEach(builder::addRule); return builder.build(); } - private static NewActiveRule transform(LoadedActiveRule activeRule, String qProfileKey, Set<RuleKey> deprecatedKeys) { + private static NewActiveRule transform(LoadedActiveRule activeRule) { NewActiveRule.Builder builder = new NewActiveRule.Builder(); builder .setRuleKey(activeRule.getRuleKey()) @@ -83,8 +66,8 @@ public class ActiveRulesProvider { .setLanguage(activeRule.getLanguage()) .setInternalKey(activeRule.getInternalKey()) .setTemplateRuleKey(activeRule.getTemplateRuleKey()) - .setQProfileKey(qProfileKey) - .setDeprecatedKeys(deprecatedKeys); + .setQProfileKey(activeRule.getQProfileKey()) + .setDeprecatedKeys(activeRule.getDeprecatedKeys()); // load parameters if (activeRule.getParams() != null) { for (Map.Entry<String, String> params : activeRule.getParams().entrySet()) { @@ -100,18 +83,4 @@ public class ActiveRulesProvider { return builder.build(); } - - private static List<LoadedActiveRule> load(ActiveRulesLoader loader, String qProfileKey) { - return loader.load(qProfileKey); - } - - private static Collection<String> getKeys(QualityProfiles qProfiles) { - List<String> keys = new ArrayList<>(qProfiles.findAll().size()); - - for (QProfile qp : qProfiles.findAll()) { - keys.add(qp.getKey()); - } - - return keys; - } } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/rule/DefaultActiveRulesLoader.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/rule/DefaultActiveRulesLoader.java index 78c047d5903..dad400fb11b 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/rule/DefaultActiveRulesLoader.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/rule/DefaultActiveRulesLoader.java @@ -19,34 +19,27 @@ */ package org.sonar.scanner.rule; -import java.io.IOException; -import java.io.InputStream; -import java.util.EnumMap; -import java.util.HashMap; -import java.util.LinkedList; +import com.google.gson.Gson; +import com.google.gson.annotations.SerializedName; +import com.google.gson.reflect.TypeToken; +import java.io.Reader; +import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.stream.Collectors; -import org.apache.commons.io.IOUtils; -import org.sonar.api.impl.utils.ScannerUtils; +import javax.annotation.Nullable; import org.sonar.api.issue.impact.Severity; import org.sonar.api.issue.impact.SoftwareQuality; import org.sonar.api.rule.RuleKey; import org.sonar.api.utils.DateUtils; -import org.sonar.core.rule.ImpactFormatter; import org.sonar.scanner.http.ScannerWsClient; -import org.sonarqube.ws.Common; -import org.sonarqube.ws.Common.Paging; -import org.sonarqube.ws.Rules; -import org.sonarqube.ws.Rules.Active; -import org.sonarqube.ws.Rules.Active.Param; -import org.sonarqube.ws.Rules.ActiveList; -import org.sonarqube.ws.Rules.ListResponse; -import org.sonarqube.ws.Rules.Rule; import org.sonarqube.ws.client.GetRequest; +import static java.util.Optional.ofNullable; + public class DefaultActiveRulesLoader implements ActiveRulesLoader { - private static final String RULES_SEARCH_URL = "/api/rules/list.protobuf?"; + private static final String RULES_ACTIVE_URL = "/api/v2/analysis/active_rules?"; private final ScannerWsClient wsClient; @@ -55,101 +48,83 @@ public class DefaultActiveRulesLoader implements ActiveRulesLoader { } @Override - public List<LoadedActiveRule> load(String qualityProfileKey) { - List<LoadedActiveRule> ruleList = new LinkedList<>(); - int page = 1; - int pageSize = 500; - long loaded = 0; - - while (true) { - GetRequest getRequest = new GetRequest(getUrl(qualityProfileKey, page, pageSize)); - ListResponse response = loadFromStream(wsClient.call(getRequest).contentStream()); - List<LoadedActiveRule> pageRules = readPage(response); - ruleList.addAll(pageRules); - - Paging paging = response.getPaging(); - loaded += paging.getPageSize(); - - if (paging.getTotal() <= loaded) { - break; - } - page++; + public List<LoadedActiveRule> load(String projectKey) { + GetRequest getRequest = new GetRequest(getUrl(projectKey)); + List<ActiveRuleGson> jsonResponse; + try (Reader reader = wsClient.call(getRequest).contentReader()) { + jsonResponse = new Gson().fromJson(reader, new TypeToken<ArrayList<ActiveRuleGson>>() { + }.getType()); + } catch (Exception e) { + throw new IllegalStateException("Unable to load active rules", e); } - - return ruleList; + return convert(jsonResponse); } - private static String getUrl(String qualityProfileKey, int page, int pageSize) { - StringBuilder builder = new StringBuilder(1024); - builder.append(RULES_SEARCH_URL); - builder.append("qprofile=").append(ScannerUtils.encodeForUrl(qualityProfileKey)); - builder.append("&ps=").append(pageSize); - builder.append("&p=").append(page); - return builder.toString(); + private static String getUrl(String projectKey) { + return RULES_ACTIVE_URL + "projectKey=" + projectKey; } - private static ListResponse loadFromStream(InputStream is) { - try { - return ListResponse.parseFrom(is); - } catch (IOException e) { - throw new IllegalStateException("Failed to load quality profiles", e); - } finally { - IOUtils.closeQuietly(is); - } + private static List<LoadedActiveRule> convert(List<ActiveRuleGson> activeRuleGsonList) { + return activeRuleGsonList.stream() + .map(DefaultActiveRulesLoader::convertActiveRule) + .toList(); } - private static List<LoadedActiveRule> readPage(ListResponse response) { - List<LoadedActiveRule> loadedRules = new LinkedList<>(); - - List<Rule> rulesList = response.getRulesList(); - Map<String, ActiveList> actives = response.getActives().getActivesMap(); - - for (Rule r : rulesList) { - ActiveList activeList = actives.get(r.getKey()); - Active active = activeList.getActiveList(0); - - LoadedActiveRule loadedRule = new LoadedActiveRule(); - - loadedRule.setRuleKey(RuleKey.parse(r.getKey())); - loadedRule.setName(r.getName()); - loadedRule.setSeverity(active.getSeverity()); - - loadedRule.setCreatedAt(DateUtils.dateToLong(DateUtils.parseDateTime(active.getCreatedAt()))); - loadedRule.setUpdatedAt(DateUtils.dateToLong(DateUtils.parseDateTime(active.getUpdatedAt()))); - loadedRule.setLanguage(r.getLang()); - loadedRule.setInternalKey(r.getInternalKey()); - if (r.hasTemplateKey()) { - RuleKey templateRuleKey = RuleKey.parse(r.getTemplateKey()); - loadedRule.setTemplateRuleKey(templateRuleKey.rule()); - } - - Map<String, String> params = new HashMap<>(); + private static LoadedActiveRule convertActiveRule(ActiveRuleGson activeRule) { + LoadedActiveRule loadedRule = new LoadedActiveRule(); + loadedRule.setRuleKey(convertRuleKey(activeRule.ruleKey())); + loadedRule.setName(activeRule.name()); + loadedRule.setSeverity(activeRule.severity()); + loadedRule.setCreatedAt(DateUtils.dateToLong(DateUtils.parseDateTime(activeRule.createdAt()))); + loadedRule.setUpdatedAt(DateUtils.dateToLong(DateUtils.parseDateTime(activeRule.updatedAt()))); + loadedRule.setLanguage(activeRule.language()); + loadedRule.setInternalKey(activeRule.internalKey()); + loadedRule.setQProfileKey(activeRule.qProfileKey()); + ofNullable(activeRule.templateRuleKey()) + .map(RuleKey::parse) + .map(RuleKey::rule) + .ifPresent(loadedRule::setTemplateRuleKey); + loadedRule.setParams(activeRule.params() != null ? convertParams(activeRule.params()) : Map.of()); + loadedRule.setImpacts(activeRule.impacts() != null ? activeRule.impacts() : Map.of()); + loadedRule.setDeprecatedKeys(convertDeprecatedKeys(activeRule.deprecatedKeys())); + return loadedRule; + } - for (Rules.Rule.Param param : r.getParams().getParamsList()) { - params.put(param.getKey(), param.getDefaultValue()); - } + private static Map<String, String> convertParams(List<ParamGson> params) { + return params.stream().collect(Collectors.toMap(ParamGson::key, ParamGson::value)); + } - // overrides defaultValue if the key is the same - for (Param param : active.getParamsList()) { - params.put(param.getKey(), param.getValue()); - } + private static Set<RuleKey> convertDeprecatedKeys(@Nullable List<RuleKeyGson> deprecatedKeysList) { + return ofNullable(deprecatedKeysList) + .orElse(List.of()) + .stream() + .map(value -> RuleKey.of(value.repository(), value.rule())) + .collect(Collectors.toSet()); + } - loadedRule.setParams(params); + private static RuleKey convertRuleKey(RuleKeyGson ruleKey) { + return RuleKey.of(ruleKey.repository(), ruleKey.rule()); + } - Map<SoftwareQuality, Severity> impacts = new EnumMap<>(SoftwareQuality.class); - for (Common.Impact impact : active.getImpacts().getImpactsList()) { - impacts.put(SoftwareQuality.valueOf(impact.getSoftwareQuality().name()), ImpactFormatter.mapImpactSeverity(impact.getSeverity())); - } - loadedRule.setImpacts(impacts); + record ActiveRuleGson( + @SerializedName("ruleKey") RuleKeyGson ruleKey, + @SerializedName("name") String name, + @SerializedName("severity") String severity, + @SerializedName("createdAt") String createdAt, + @SerializedName("updatedAt") String updatedAt, + @SerializedName("internalKey") @Nullable String internalKey, + @SerializedName("language") String language, + @SerializedName("templateRuleKey") @Nullable String templateRuleKey, + @SerializedName("qProfileKey") String qProfileKey, + @SerializedName("deprecatedKeys") @Nullable List<RuleKeyGson> deprecatedKeys, + @SerializedName("params") @Nullable List<ParamGson> params, + @SerializedName("impacts") @Nullable Map<SoftwareQuality, Severity> impacts) { + } - loadedRule.setDeprecatedKeys(r.getDeprecatedKeys().getDeprecatedKeyList() - .stream() - .map(RuleKey::parse) - .collect(Collectors.toSet())); - loadedRules.add(loadedRule); - } + record RuleKeyGson(@SerializedName("repository") String repository, @SerializedName("rule") String rule) { + } - return loadedRules; + record ParamGson(@SerializedName("key") String key, @SerializedName("value") String value) { } } diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/rule/LoadedActiveRule.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/rule/LoadedActiveRule.java index 8f2c3643237..b0d1ee6d3b8 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/rule/LoadedActiveRule.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/rule/LoadedActiveRule.java @@ -39,6 +39,7 @@ public class LoadedActiveRule { private String templateRuleKey; private String internalKey; private Set<RuleKey> deprecatedKeys; + private String qProfileKey; public LoadedActiveRule() { // nothing to do here @@ -117,11 +118,12 @@ public class LoadedActiveRule { this.templateRuleKey = templateRuleKey; } + @CheckForNull public String getInternalKey() { return internalKey; } - public void setInternalKey(String internalKey) { + public void setInternalKey(@Nullable String internalKey) { this.internalKey = internalKey; } @@ -132,4 +134,12 @@ public class LoadedActiveRule { public void setDeprecatedKeys(Set<RuleKey> deprecatedKeys) { this.deprecatedKeys = deprecatedKeys; } + + public String getQProfileKey() { + return qProfileKey; + } + + public void setQProfileKey(String qProfileKey) { + this.qProfileKey = qProfileKey; + } } |