aboutsummaryrefslogtreecommitdiffstats
path: root/sonar-scanner-engine
diff options
context:
space:
mode:
authorPierre Guillot <pierre.guillot@sonarsource.com>2024-12-10 15:29:09 +0100
committerSteve Marion <steve.marion@sonarsource.com>2024-12-18 11:13:21 +0100
commitd639a965bce7acafb004906cd07a8f0b5f7af993 (patch)
tree647cd646abddb12dfeeef7e637aa33b4658f1049 /sonar-scanner-engine
parent451c1c2e4856ec3df87f86189fcdb25b31794027 (diff)
downloadsonarqube-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')
-rw-r--r--sonar-scanner-engine/src/it/java/org/sonar/scanner/mediumtest/bootstrap/BootstrapMediumIT.java9
-rw-r--r--sonar-scanner-engine/src/it/java/org/sonar/scanner/mediumtest/issues/ChecksMediumIT.java3
-rw-r--r--sonar-scanner-engine/src/it/java/org/sonar/scanner/mediumtest/issues/IssuesMediumIT.java6
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/language/DefaultLanguagesLoader.java4
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/rule/ActiveRulesLoader.java2
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/rule/ActiveRulesProvider.java51
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/rule/DefaultActiveRulesLoader.java173
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/rule/LoadedActiveRule.java12
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/scanner/rule/ActiveRulesProviderTest.java69
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/scanner/rule/DefaultActiveRulesLoaderTest.java208
-rw-r--r--sonar-scanner-engine/src/testFixtures/java/org/sonar/scanner/mediumtest/ScannerMediumTester.java4
11 files changed, 270 insertions, 271 deletions
diff --git a/sonar-scanner-engine/src/it/java/org/sonar/scanner/mediumtest/bootstrap/BootstrapMediumIT.java b/sonar-scanner-engine/src/it/java/org/sonar/scanner/mediumtest/bootstrap/BootstrapMediumIT.java
index 4af42659e6e..5df0d03b111 100644
--- a/sonar-scanner-engine/src/it/java/org/sonar/scanner/mediumtest/bootstrap/BootstrapMediumIT.java
+++ b/sonar-scanner-engine/src/it/java/org/sonar/scanner/mediumtest/bootstrap/BootstrapMediumIT.java
@@ -35,7 +35,6 @@ import org.sonar.api.testfixtures.log.LogTesterJUnit5;
import org.sonar.scanner.bootstrap.ScannerMain;
import org.sonarqube.ws.Ce;
import org.sonarqube.ws.Qualityprofiles;
-import org.sonarqube.ws.Rules;
import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
import static com.github.tomakehurst.wiremock.client.WireMock.get;
@@ -81,10 +80,10 @@ class BootstrapMediumIT {
.build())
.build()))));
- sonarqube.stubFor(get("/api/rules/list.protobuf?qprofile=" + QPROFILE_KEY + "&ps=500&p=1")
- .willReturn(aResponse()
- .withResponseBody(protobufBody(Rules.ListResponse.newBuilder()
- .build()))));
+ sonarqube.stubFor(get("/api/v2/analysis/active_rules?projectKey=" + PROJECT_KEY)
+ .willReturn(okJson("""
+ []
+ """)));
sonarqube.stubFor(get("/api/languages/list")
.willReturn(okJson("""
diff --git a/sonar-scanner-engine/src/it/java/org/sonar/scanner/mediumtest/issues/ChecksMediumIT.java b/sonar-scanner-engine/src/it/java/org/sonar/scanner/mediumtest/issues/ChecksMediumIT.java
index 7883b4c064f..035267f43fe 100644
--- a/sonar-scanner-engine/src/it/java/org/sonar/scanner/mediumtest/issues/ChecksMediumIT.java
+++ b/sonar-scanner-engine/src/it/java/org/sonar/scanner/mediumtest/issues/ChecksMediumIT.java
@@ -30,11 +30,11 @@ import org.apache.commons.io.FileUtils;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
-import org.sonar.scanner.rule.LoadedActiveRule;
import org.sonar.api.rule.RuleKey;
import org.sonar.scanner.mediumtest.AnalysisResult;
import org.sonar.scanner.mediumtest.ScannerMediumTester;
import org.sonar.scanner.protocol.output.ScannerReport.Issue;
+import org.sonar.scanner.rule.LoadedActiveRule;
import org.sonar.xoo.XooPlugin;
import org.sonar.xoo.rule.XooRulesDefinition;
@@ -100,6 +100,7 @@ public class ChecksMediumIT {
r.setLanguage(languag);
r.setSeverity(severity);
r.setDeprecatedKeys(emptySet());
+ r.setQProfileKey("whatever");
Map<String, String> params = new HashMap<>();
params.put(paramKey, paramValue);
diff --git a/sonar-scanner-engine/src/it/java/org/sonar/scanner/mediumtest/issues/IssuesMediumIT.java b/sonar-scanner-engine/src/it/java/org/sonar/scanner/mediumtest/issues/IssuesMediumIT.java
index 39c65da4861..541a690a63c 100644
--- a/sonar-scanner-engine/src/it/java/org/sonar/scanner/mediumtest/issues/IssuesMediumIT.java
+++ b/sonar-scanner-engine/src/it/java/org/sonar/scanner/mediumtest/issues/IssuesMediumIT.java
@@ -30,13 +30,13 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.slf4j.event.Level;
-import org.sonar.scanner.rule.LoadedActiveRule;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.testfixtures.log.LogTester;
import org.sonar.scanner.mediumtest.AnalysisResult;
import org.sonar.scanner.mediumtest.ScannerMediumTester;
import org.sonar.scanner.protocol.output.ScannerReport.ExternalIssue;
import org.sonar.scanner.protocol.output.ScannerReport.Issue;
+import org.sonar.scanner.rule.LoadedActiveRule;
import org.sonar.xoo.XooPlugin;
import org.sonar.xoo.rule.HasTagSensor;
import org.sonar.xoo.rule.OneExternalIssueOnProjectSensor;
@@ -414,9 +414,9 @@ public class IssuesMediumIT {
r.setName("TODO");
r.setLanguage("xoo");
r.setSeverity("MAJOR");
- r.setDeprecatedKeys(emptySet()
- );
+ r.setDeprecatedKeys(emptySet());
r.setParams(ImmutableMap.of("tag", "TODO"));
+ r.setQProfileKey("whatever");
tester.activateRule(r);
}
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;
+ }
}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/rule/ActiveRulesProviderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/rule/ActiveRulesProviderTest.java
index 4072d462f92..c5f3fceedbc 100644
--- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/rule/ActiveRulesProviderTest.java
+++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/rule/ActiveRulesProviderTest.java
@@ -19,13 +19,12 @@
*/
package org.sonar.scanner.rule;
-import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
-import java.util.Date;
-import java.util.LinkedList;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.function.Consumer;
import java.util.stream.Collectors;
import org.assertj.core.groups.Tuple;
import org.junit.Test;
@@ -36,16 +35,17 @@ import org.sonar.api.batch.rule.internal.DefaultActiveRules;
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.sonarqube.ws.Qualityprofiles.SearchWsResponse.QualityProfile;
+import org.sonar.scanner.bootstrap.ScannerProperties;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
+import static org.sonar.api.CoreProperties.PROJECT_KEY_PROPERTY;
public class ActiveRulesProviderTest {
+ public static final String PROJECT_KEY = "my-awesome-project";
private final ActiveRulesProvider provider = new ActiveRulesProvider();
private final DefaultActiveRulesLoader loader = mock(DefaultActiveRulesLoader.class);
@@ -57,21 +57,19 @@ public class ActiveRulesProviderTest {
r1.setImpacts(Map.of(SoftwareQuality.MAINTAINABILITY, Severity.HIGH));
- List<LoadedActiveRule> qp1Rules = ImmutableList.of(r1, r2);
- List<LoadedActiveRule> qp2Rules = ImmutableList.of(r2, r3);
- List<LoadedActiveRule> qp3Rules = ImmutableList.of(r1, r3);
+ when(loader.load(PROJECT_KEY)).thenReturn(List.of(r1, r2, r3));
- when(loader.load("qp1")).thenReturn(qp1Rules);
- when(loader.load("qp2")).thenReturn(qp2Rules);
- when(loader.load("qp3")).thenReturn(qp3Rules);
-
- QualityProfiles profiles = mockProfiles("qp1", "qp2", "qp3");
- DefaultActiveRules activeRules = provider.provide(loader, profiles);
+ HashMap<String, String> propertiesMap = new HashMap<>();
+ propertiesMap.put(PROJECT_KEY_PROPERTY, PROJECT_KEY);
+ ScannerProperties scannerProperties = new ScannerProperties(propertiesMap);
+ DefaultActiveRules activeRules = provider.provide(loader, scannerProperties);
assertThat(activeRules.findAll()).hasSize(3);
assertThat(activeRules.findAll()).extracting("ruleKey").containsOnly(
RuleKey.of("rule1", "rule1"), RuleKey.of("rule2", "rule2"), RuleKey.of("rule3", "rule3"));
+ verify(loader).load(PROJECT_KEY);
+
Map<String, ActiveRule> activeRuleByKey = activeRules.findAll().stream().collect(Collectors.toMap(e -> e.ruleKey().rule(), e -> e));
assertThat(((DefaultActiveRule) activeRuleByKey.get("rule1")).impacts())
.containsExactlyInAnyOrderEntriesOf(Map.of(SoftwareQuality.MAINTAINABILITY, Severity.HIGH));
@@ -79,10 +77,6 @@ public class ActiveRulesProviderTest {
assertThat(((DefaultActiveRule) activeRuleByKey.get("rule2")).impacts()).isEmpty();
assertThat(((DefaultActiveRule) activeRuleByKey.get("rule3")).impacts()).isEmpty();
- verify(loader).load("qp1");
- verify(loader).load("qp2");
- verify(loader).load("qp3");
-
assertThat(activeRules.getDeprecatedRuleKeys(RuleKey.of("rule1", "rule1"))).containsOnly("rule1old:rule1old");
verifyNoMoreInteractions(loader);
}
@@ -90,40 +84,33 @@ public class ActiveRulesProviderTest {
@Test
public void testParamsAreTransformed() {
LoadedActiveRule r1 = mockRule("rule1");
- LoadedActiveRule r2 = mockRule("rule2");
- r2.setParams(ImmutableMap.of("foo1", "bar1", "foo2", "bar2"));
+ LoadedActiveRule r2 = mockRule("rule2", b -> b.setParams(Map.of("foo1", "bar1", "foo2", "bar2")));
- List<LoadedActiveRule> qpRules = ImmutableList.of(r1, r2);
- when(loader.load("qp")).thenReturn(qpRules);
+ when(loader.load(PROJECT_KEY)).thenReturn(List.of(r1, r2));
- QualityProfiles profiles = mockProfiles("qp");
- ActiveRules activeRules = provider.provide(loader, profiles);
+ HashMap<String, String> propertiesMap = new HashMap<>();
+ propertiesMap.put(PROJECT_KEY_PROPERTY, PROJECT_KEY);
+ ScannerProperties scannerProperties = new ScannerProperties(propertiesMap);
+ ActiveRules activeRules = provider.provide(loader, scannerProperties);
assertThat(activeRules.findAll()).hasSize(2);
assertThat(activeRules.findAll()).extracting("ruleKey", "params").containsOnly(
Tuple.tuple(RuleKey.of("rule1", "rule1"), ImmutableMap.of()),
Tuple.tuple(RuleKey.of("rule2", "rule2"), ImmutableMap.of("foo1", "bar1", "foo2", "bar2")));
- verify(loader).load("qp");
+ verify(loader).load(PROJECT_KEY);
verifyNoMoreInteractions(loader);
}
- private static QualityProfiles mockProfiles(String... keys) {
- List<QualityProfile> profiles = new LinkedList<>();
-
- for (String k : keys) {
- QualityProfile p = QualityProfile.newBuilder().setKey(k).setLanguage(k).setRulesUpdatedAt(DateUtils.formatDateTime(new Date())).build();
- profiles.add(p);
+ @SafeVarargs
+ private static LoadedActiveRule mockRule(String name, Consumer<LoadedActiveRule>... consumers) {
+ LoadedActiveRule rule = new LoadedActiveRule();
+ rule.setName(name);
+ rule.setRuleKey(RuleKey.of(name, name));
+ rule.setDeprecatedKeys(ImmutableSet.of(RuleKey.of(name + "old", name + "old")));
+ for (Consumer<LoadedActiveRule> consumer : consumers) {
+ consumer.accept(rule);
}
-
- return new QualityProfiles(profiles);
- }
-
- private static LoadedActiveRule mockRule(String name) {
- LoadedActiveRule r = new LoadedActiveRule();
- r.setName(name);
- r.setRuleKey(RuleKey.of(name, name));
- r.setDeprecatedKeys(ImmutableSet.of(RuleKey.of(name + "old", name + "old")));
- return r;
+ return rule;
}
}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/rule/DefaultActiveRulesLoaderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/rule/DefaultActiveRulesLoaderTest.java
index 59a49084742..cf66faeb655 100644
--- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/rule/DefaultActiveRulesLoaderTest.java
+++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/rule/DefaultActiveRulesLoaderTest.java
@@ -19,46 +19,49 @@
*/
package org.sonar.scanner.rule;
-import java.io.ByteArrayInputStream;
-import java.io.InputStream;
-import java.util.Collection;
+import com.google.gson.Gson;
+import java.io.Reader;
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.EnumMap;
+import java.util.List;
import java.util.Map;
+import java.util.stream.Collectors;
import java.util.stream.IntStream;
-import org.junit.Before;
-import org.junit.Test;
-import org.sonar.scanner.rule.LoadedActiveRule;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
import org.sonar.api.issue.impact.SoftwareQuality;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.rule.Severity;
import org.sonar.scanner.WsTestUtil;
import org.sonar.scanner.http.DefaultScannerWsClient;
import org.sonar.scanner.scan.branch.BranchConfiguration;
-import org.sonarqube.ws.Common;
-import org.sonarqube.ws.Rules;
-import org.sonarqube.ws.Rules.Active;
-import org.sonarqube.ws.Rules.ActiveList;
-import org.sonarqube.ws.Rules.Actives;
-import org.sonarqube.ws.Rules.Rule;
+import static java.util.Map.entry;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
+import static org.sonar.api.issue.impact.Severity.HIGH;
+import static org.sonar.scanner.rule.DefaultActiveRulesLoader.ActiveRuleGson;
+import static org.sonar.scanner.rule.DefaultActiveRulesLoader.ParamGson;
+import static org.sonar.scanner.rule.DefaultActiveRulesLoader.RuleKeyGson;
-public class DefaultActiveRulesLoaderTest {
+class DefaultActiveRulesLoaderTest {
- private static final int PAGE_SIZE_1 = 150;
- private static final int PAGE_SIZE_2 = 76;
+ private static final int NUMBER_OF_RULES = 150;
private static final RuleKey EXAMPLE_KEY = RuleKey.of("java", "S108");
+ private static final RuleKey CUSTOM_RULE_KEY = RuleKey.of("java", "my-custom-rule");
private static final String FORMAT_KEY = "format";
private static final String FORMAT_VALUE = "^[a-z][a-zA-Z0-9]*$";
+ private static final String PROJECT_KEY = "myProjectKey";
private static final String SEVERITY_VALUE = Severity.MINOR;
private DefaultActiveRulesLoader loader;
private DefaultScannerWsClient wsClient;
- @Before
- public void setUp() {
+ @BeforeEach
+ void setUp() {
wsClient = mock(DefaultScannerWsClient.class);
BranchConfiguration branchConfig = mock(BranchConfiguration.class);
when(branchConfig.isPullRequest()).thenReturn(false);
@@ -66,78 +69,131 @@ public class DefaultActiveRulesLoaderTest {
}
@Test
- public void load_shouldRequestRulesAndParseResponse() {
- int total = PAGE_SIZE_1 + PAGE_SIZE_2;
-
- WsTestUtil.mockStream(wsClient, urlOfPage(1), responseOfSize(1, PAGE_SIZE_1, total));
- WsTestUtil.mockStream(wsClient, urlOfPage(2), responseOfSize(2, PAGE_SIZE_2, total));
-
- Collection<LoadedActiveRule> activeRules = loader.load("c+-test_c+-values-17445");
- assertThat(activeRules).hasSize(total);
- assertThat(activeRules)
- .filteredOn(r -> r.getRuleKey().equals(EXAMPLE_KEY))
- .extracting(LoadedActiveRule::getParams)
- .extracting(p -> p.get(FORMAT_KEY))
- .containsExactly(FORMAT_VALUE);
- assertThat(activeRules)
- .filteredOn(r -> r.getRuleKey().equals(EXAMPLE_KEY))
- .extracting(LoadedActiveRule::getSeverity)
- .containsExactly(SEVERITY_VALUE);
- assertThat(activeRules)
- .filteredOn(r -> r.getRuleKey().equals(EXAMPLE_KEY))
- .extracting(LoadedActiveRule::getImpacts)
- .containsExactlyInAnyOrder(Map.of(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.HIGH));
-
- WsTestUtil.verifyCall(wsClient, urlOfPage(1));
- WsTestUtil.verifyCall(wsClient, urlOfPage(2));
+ void load_shouldRequestRulesAndParseResponse() {
+ WsTestUtil.mockReader(wsClient, getUrl(), response());
+
+ Map<RuleKey, LoadedActiveRule> activeRulesByKey = loader.load(PROJECT_KEY).stream().collect(Collectors.toMap(LoadedActiveRule::getRuleKey, r -> r));
+ assertThat(activeRulesByKey).hasSize(NUMBER_OF_RULES);
+
+ var exampleRule = activeRulesByKey.get(EXAMPLE_KEY);
+ assertThat(exampleRule.getParams()).containsEntry(FORMAT_KEY, FORMAT_VALUE);
+ assertThat(exampleRule.getSeverity()).isEqualTo(SEVERITY_VALUE);
+ assertThat(exampleRule.getImpacts()).containsExactly(entry(SoftwareQuality.MAINTAINABILITY, HIGH));
+
+ var customRule = activeRulesByKey.get(CUSTOM_RULE_KEY);
+ assertThat(customRule.getTemplateRuleKey()).isEqualTo("ruleTemplate");
+
+ WsTestUtil.verifyCall(wsClient, getUrl());
verifyNoMoreInteractions(wsClient);
}
- private String urlOfPage(int page) {
- return "/api/rules/list.protobuf?qprofile=c%2B-test_c%2B-values-17445&ps=500&p=" + page + "";
+ private String getUrl() {
+ return "/api/v2/analysis/active_rules?projectKey=" + PROJECT_KEY;
}
- /**
- * Generates an imaginary protobuf result.
- *
- * @param pageIndex page index, that the response should contain
- * @param numberOfRules the number of rules, that the response should contain
- * @param total the number of results on all pages
- * @return the binary stream
- */
- private InputStream responseOfSize(int pageIndex, int numberOfRules, int total) {
- Rules.ListResponse.Builder rules = Rules.ListResponse.newBuilder();
- Actives.Builder actives = Actives.newBuilder();
-
- IntStream.rangeClosed(1, numberOfRules)
+ private Reader response() {
+ List<ActiveRuleGson> activeRules = new ArrayList<>();
+
+ IntStream.rangeClosed(1, NUMBER_OF_RULES - 1)
.mapToObj(i -> RuleKey.of("java", "S" + i))
.forEach(key -> {
+ ActiveRuleGsonBuilder builder = new ActiveRuleGsonBuilder();
- Rule.Builder ruleBuilder = Rule.newBuilder();
- ruleBuilder.setKey(key.toString());
- rules.addRules(ruleBuilder);
+ builder.setRuleKey(new RuleKeyGson(key.repository(), key.rule()));
- Active.Builder activeBuilder = Active.newBuilder();
- activeBuilder.setCreatedAt("2014-05-27T15:50:45+0100");
- activeBuilder.setUpdatedAt("2014-05-27T15:50:45+0100");
+ builder.setCreatedAt("2014-05-27T15:50:45+0100");
+ builder.setUpdatedAt("2014-05-27T15:50:45+0100");
if (EXAMPLE_KEY.equals(key)) {
- activeBuilder.addParams(Rules.Active.Param.newBuilder().setKey(FORMAT_KEY).setValue(FORMAT_VALUE));
- activeBuilder.setSeverity(SEVERITY_VALUE);
- activeBuilder.setImpacts(Rules.Impacts.newBuilder().addImpacts(Common.Impact.newBuilder()
- .setSoftwareQuality(Common.SoftwareQuality.MAINTAINABILITY)
- .setSeverity(Common.ImpactSeverity.HIGH).build()).build());
+ builder.setParams(List.of(new ParamGson(FORMAT_KEY, FORMAT_VALUE)));
+ builder.setSeverity(SEVERITY_VALUE);
+ builder.setImpacts(Map.of(SoftwareQuality.MAINTAINABILITY, HIGH));
}
- ActiveList activeList = Rules.ActiveList.newBuilder().addActiveList(activeBuilder).build();
- actives.putAllActives(Map.of(key.toString(), activeList));
+
+ activeRules.add(builder.build());
});
- rules.setActives(actives);
- rules.setPaging(Common.Paging.newBuilder()
- .setTotal(total)
- .setPageIndex(pageIndex)
- .setPageSize(numberOfRules)
- .build());
- return new ByteArrayInputStream(rules.build().toByteArray());
+ ActiveRuleGsonBuilder builder = new ActiveRuleGsonBuilder();
+ builder.setRuleKey(new RuleKeyGson(CUSTOM_RULE_KEY.repository(), CUSTOM_RULE_KEY.rule()));
+ builder.setCreatedAt("2014-05-27T15:50:45+0100");
+ builder.setUpdatedAt("2014-05-27T15:50:45+0100");
+ builder.setTemplateRuleKey("java:ruleTemplate");
+ activeRules.add(builder.build());
+
+ return toReader(activeRules);
+ }
+
+ private static Reader toReader(List<ActiveRuleGson> activeRules) {
+ String json = new Gson().toJson(activeRules);
+ return new StringReader(json);
+ }
+
+ private static class ActiveRuleGsonBuilder {
+ private RuleKeyGson ruleKey;
+ private String name;
+ private String severity;
+ private String createdAt;
+ private String updatedAt;
+ private String internalKey;
+ private String language;
+ private String templateRuleKey;
+ private String qProfilKey;
+ private final List<RuleKeyGson> deprecatedKeys = new ArrayList<>();
+ private final List<ParamGson> params = new ArrayList<>();
+ private final Map<SoftwareQuality, org.sonar.api.issue.impact.Severity> impacts = new EnumMap<>(SoftwareQuality.class);
+
+ public void setRuleKey(RuleKeyGson ruleKey) {
+ this.ruleKey = ruleKey;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public void setSeverity(String severity) {
+ this.severity = severity;
+ }
+
+ public void setCreatedAt(String createdAt) {
+ this.createdAt = createdAt;
+ }
+
+ public void setUpdatedAt(String updatedAt) {
+ this.updatedAt = updatedAt;
+ }
+
+ public void setInternalKey(String internalKey) {
+ this.internalKey = internalKey;
+ }
+
+ public void setLanguage(String language) {
+ this.language = language;
+ }
+
+ public void setTemplateRuleKey(String templateRuleKey) {
+ this.templateRuleKey = templateRuleKey;
+ }
+
+ public void setQProfilKey(String qProfilKey) {
+ this.qProfilKey = qProfilKey;
+ }
+
+ public void setParams(List<ParamGson> params) {
+ this.params.clear();
+ this.params.addAll(params);
+ }
+
+ public void addAllDeprecatedKeys(List<RuleKeyGson> deprecatedKeys) {
+ this.deprecatedKeys.addAll(deprecatedKeys);
+ }
+
+ public void setImpacts(Map<SoftwareQuality, org.sonar.api.issue.impact.Severity> impacts) {
+ this.impacts.clear();
+ this.impacts.putAll(impacts);
+ }
+
+ public ActiveRuleGson build() {
+ return new ActiveRuleGson(ruleKey, name, severity, createdAt, updatedAt, internalKey, language, templateRuleKey, qProfilKey, deprecatedKeys, params, impacts);
+ }
}
}
diff --git a/sonar-scanner-engine/src/testFixtures/java/org/sonar/scanner/mediumtest/ScannerMediumTester.java b/sonar-scanner-engine/src/testFixtures/java/org/sonar/scanner/mediumtest/ScannerMediumTester.java
index 89f54283c45..0dc09835a06 100644
--- a/sonar-scanner-engine/src/testFixtures/java/org/sonar/scanner/mediumtest/ScannerMediumTester.java
+++ b/sonar-scanner-engine/src/testFixtures/java/org/sonar/scanner/mediumtest/ScannerMediumTester.java
@@ -30,6 +30,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
@@ -49,6 +50,7 @@ import org.sonar.api.SonarEdition;
import org.sonar.api.SonarProduct;
import org.sonar.api.SonarQubeSide;
import org.sonar.api.SonarRuntime;
+import org.sonar.api.batch.rule.internal.NewActiveRule;
import org.sonar.api.impl.server.RulesDefinitionContext;
import org.sonar.api.measures.CoreMetrics;
import org.sonar.api.measures.Metric;
@@ -223,6 +225,7 @@ public class ScannerMediumTester extends ExternalResource implements BeforeTestE
r.setLanguage(language);
r.setSeverity(severity);
r.setDeprecatedKeys(emptySet());
+ r.setQProfileKey("whatever");
activeRules.addActiveRule(r);
return this;
@@ -280,6 +283,7 @@ public class ScannerMediumTester extends ExternalResource implements BeforeTestE
}
}
+
public AnalysisBuilder newAnalysis() {
return new AnalysisBuilder(this);
}