]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-23250 handle impacts on active rules in scanner engine
authorLéo Geoffroy <leo.geoffroy@sonarsource.com>
Thu, 10 Oct 2024 16:14:38 +0000 (18:14 +0200)
committersonartech <sonartech@sonarsource.com>
Wed, 16 Oct 2024 20:03:01 +0000 (20:03 +0000)
19 files changed:
gradle.properties
server/sonar-webserver-webapi/src/it/java/org/sonar/server/issue/ws/PullTaintActionIT.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ImpactFormatter.java [deleted file]
server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SearchResponseFormat.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/pull/PullTaintActionProtobufObjectGenerator.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/rule/ws/RuleMapper.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/rule/ws/RulesResponseFormatter.java
server/sonar-webserver-webapi/src/test/java/org/sonar/server/issue/ImpactFormatterTest.java [deleted file]
sonar-core/src/main/java/org/sonar/core/rule/ImpactFormatter.java [new file with mode: 0644]
sonar-core/src/test/java/org/sonar/core/rule/ImpactFormatterTest.java [new file with mode: 0644]
sonar-plugin-api-impl/src/main/java/org/sonar/api/batch/rule/internal/DefaultActiveRule.java
sonar-plugin-api-impl/src/main/java/org/sonar/api/batch/rule/internal/NewActiveRule.java
sonar-plugin-api-impl/src/test/java/org/sonar/api/batch/rule/internal/NewActiveRuleTest.java
sonar-scanner-engine/src/main/java/org/sonar/scanner/issue/IssuePublisher.java
sonar-scanner-engine/src/main/java/org/sonar/scanner/rule/ActiveRulesProvider.java
sonar-scanner-engine/src/main/java/org/sonar/scanner/rule/DefaultActiveRulesLoader.java
sonar-scanner-engine/src/test/java/org/sonar/scanner/issue/IssuePublisherTest.java
sonar-scanner-engine/src/test/java/org/sonar/scanner/rule/ActiveRulesProviderTest.java
sonar-scanner-engine/src/test/java/org/sonar/scanner/rule/DefaultActiveRulesLoaderTest.java

index ed397c125b2cd19a7beb722399151534b674af6f..4167263696ff26fb94b0fee935e37d1618998350 100644 (file)
@@ -1,12 +1,11 @@
 group=org.sonarsource.sonarqube
 version=10.8
-
 # End Of Life date for the version. MMF-3763. format is yyyy-MM-dd
 # 6 months from the release date for non LTA versions
 # 30 months from the release date for LTA versions
 # No change required for patch versions
 versionEOL=2025-05-27
-pluginApiVersion=10.11.0.2468
+pluginApiVersion=10.12.0.2507
 description=Open source platform for continuous inspection of code quality
 projectTitle=SonarQube
 org.gradle.jvmargs=-Xmx2048m
index 17cebf289afdd472fbda3bff13d0265b33246446..8d1155f71081ef767d7ffa1e9312699464cf2c3e 100644 (file)
@@ -32,6 +32,7 @@ import org.sonar.api.issue.impact.Severity;
 import org.sonar.api.issue.impact.SoftwareQuality;
 import org.sonar.api.resources.Qualifiers;
 import org.sonar.api.utils.System2;
+import org.sonar.core.rule.ImpactFormatter;
 import org.sonar.db.DbTester;
 import org.sonar.db.component.ComponentDto;
 import org.sonar.db.component.ProjectData;
@@ -48,7 +49,6 @@ import org.sonar.db.user.UserDto;
 import org.sonar.server.component.ComponentFinder;
 import org.sonar.server.exceptions.ForbiddenException;
 import org.sonar.server.exceptions.NotFoundException;
-import org.sonar.server.issue.ImpactFormatter;
 import org.sonar.server.issue.TaintChecker;
 import org.sonar.server.issue.ws.pull.PullTaintActionProtobufObjectGenerator;
 import org.sonar.server.tester.UserSessionRule;
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ImpactFormatter.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ImpactFormatter.java
deleted file mode 100644 (file)
index 3d01812..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.server.issue;
-
-import org.sonar.api.issue.impact.Severity;
-import org.sonarqube.ws.Common;
-
-public class ImpactFormatter {
-  private ImpactFormatter() {
-  }
-
-  public static Common.ImpactSeverity mapImpactSeverity(Severity severity) {
-    return switch (severity) {
-      case BLOCKER -> Common.ImpactSeverity.ImpactSeverity_BLOCKER;
-      case HIGH -> Common.ImpactSeverity.HIGH;
-      case MEDIUM -> Common.ImpactSeverity.MEDIUM;
-      case LOW -> Common.ImpactSeverity.LOW;
-      case INFO -> Common.ImpactSeverity.ImpactSeverity_INFO;
-    };
-  }
-}
index b04eeee8b6601479e1e9add63432eb5cef24ba77..19309c3f288f79efb4a87db742d2136144a14314 100644 (file)
@@ -38,6 +38,7 @@ import org.sonar.api.utils.DateUtils;
 import org.sonar.api.utils.Duration;
 import org.sonar.api.utils.Durations;
 import org.sonar.api.utils.Paging;
+import org.sonar.core.rule.ImpactFormatter;
 import org.sonar.db.component.BranchDto;
 import org.sonar.db.component.BranchType;
 import org.sonar.db.component.ComponentDto;
@@ -49,7 +50,6 @@ import org.sonar.db.rule.RuleDto;
 import org.sonar.db.user.UserDto;
 import org.sonar.markdown.Markdown;
 import org.sonar.server.es.Facets;
-import org.sonar.server.issue.ImpactFormatter;
 import org.sonar.server.issue.TextRangeResponseFormatter;
 import org.sonar.server.issue.index.IssueScope;
 import org.sonar.server.issue.workflow.Transition;
index a31082b669336516e69ed8283449cdb9d5851121..ac19caea2862fbb61db2a200353a041ba56da16a 100644 (file)
@@ -28,13 +28,13 @@ import java.util.Set;
 import java.util.stream.Collectors;
 import org.sonar.api.rules.CleanCodeAttribute;
 import org.sonar.api.server.ServerSide;
+import org.sonar.core.rule.ImpactFormatter;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
 import org.sonar.db.component.ComponentDto;
 import org.sonar.db.issue.IssueDto;
 import org.sonar.db.protobuf.DbIssues;
 import org.sonar.db.rule.RuleDto;
-import org.sonar.server.issue.ImpactFormatter;
 import org.sonar.server.user.UserSession;
 import org.sonar.server.ws.MessageFormattingUtils;
 import org.sonarqube.ws.Common;
index 5f695661ae895b0419c918fc66e72e9cdcfbe50b..a90216e1b55f1cf4a9392b5ef12a9b4c7f2a75bc 100644 (file)
@@ -35,6 +35,7 @@ import org.sonar.api.rule.RuleKey;
 import org.sonar.api.rules.CleanCodeAttribute;
 import org.sonar.api.server.debt.DebtRemediationFunction;
 import org.sonar.api.server.debt.internal.DefaultDebtRemediationFunction;
+import org.sonar.core.rule.ImpactFormatter;
 import org.sonar.db.issue.ImpactDto;
 import org.sonar.db.rule.DeprecatedRuleKeyDto;
 import org.sonar.db.rule.RuleDescriptionSectionContextDto;
@@ -45,7 +46,6 @@ import org.sonar.db.rule.RuleParamDto;
 import org.sonar.db.user.UserDto;
 import org.sonar.markdown.Markdown;
 import org.sonar.server.common.text.MacroInterpreter;
-import org.sonar.server.issue.ImpactFormatter;
 import org.sonar.server.rule.RuleDescriptionFormatter;
 import org.sonar.server.rule.ws.RulesResponseFormatter.SearchResult;
 import org.sonarqube.ws.Common;
index 4da000f5943b657070615c1f9d23d5deffabbc8c..7c20a8cf6169ac3db7a6a9672805706c3c07c74a 100644 (file)
@@ -42,6 +42,7 @@ import org.sonar.api.resources.Languages;
 import org.sonar.api.rule.RuleKey;
 import org.sonar.api.server.ServerSide;
 import org.sonar.api.utils.DateUtils;
+import org.sonar.core.rule.ImpactFormatter;
 import org.sonar.core.util.stream.MoreCollectors;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
@@ -55,7 +56,6 @@ import org.sonar.db.rule.RuleDto;
 import org.sonar.db.rule.RuleParamDto;
 import org.sonar.db.user.UserDto;
 import org.sonar.server.es.Facets;
-import org.sonar.server.issue.ImpactFormatter;
 import org.sonar.server.qualityprofile.ActiveRuleInheritance;
 import org.sonarqube.ws.Common;
 import org.sonarqube.ws.Rules;
@@ -256,10 +256,9 @@ public class RulesResponseFormatter {
 
   public static Rules.Impacts mapImpacts(Map<SoftwareQuality, Severity> impacts) {
     Rules.Impacts.Builder impactsBuilder = Rules.Impacts.newBuilder();
-    impacts.forEach((quality, severity) ->
-      impactsBuilder.addImpacts(Common.Impact.newBuilder()
-        .setSoftwareQuality(Common.SoftwareQuality.valueOf(quality.name()))
-        .setSeverity(ImpactFormatter.mapImpactSeverity(severity))));
+    impacts.forEach((quality, severity) -> impactsBuilder.addImpacts(Common.Impact.newBuilder()
+      .setSoftwareQuality(Common.SoftwareQuality.valueOf(quality.name()))
+      .setSeverity(ImpactFormatter.mapImpactSeverity(severity))));
     return impactsBuilder.build();
   }
 
diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/issue/ImpactFormatterTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/issue/ImpactFormatterTest.java
deleted file mode 100644 (file)
index 3a801c4..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.server.issue;
-
-import org.junit.jupiter.api.Test;
-import org.sonar.api.issue.impact.Severity;
-import org.sonarqube.ws.Common;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-class ImpactFormatterTest {
-
-  @Test
-  void mapImpactSeverity_shouldReturnExpectedValue() {
-    assertEquals(Common.ImpactSeverity.ImpactSeverity_BLOCKER, ImpactFormatter.mapImpactSeverity(Severity.BLOCKER));
-    assertEquals(Common.ImpactSeverity.HIGH, ImpactFormatter.mapImpactSeverity(Severity.HIGH));
-    assertEquals(Common.ImpactSeverity.MEDIUM, ImpactFormatter.mapImpactSeverity(Severity.MEDIUM));
-    assertEquals(Common.ImpactSeverity.LOW, ImpactFormatter.mapImpactSeverity(Severity.LOW));
-    assertEquals(Common.ImpactSeverity.ImpactSeverity_INFO, ImpactFormatter.mapImpactSeverity(Severity.INFO));
-  }
-}
diff --git a/sonar-core/src/main/java/org/sonar/core/rule/ImpactFormatter.java b/sonar-core/src/main/java/org/sonar/core/rule/ImpactFormatter.java
new file mode 100644 (file)
index 0000000..7312007
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2024 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.core.rule;
+
+import org.sonar.api.issue.impact.Severity;
+import org.sonarqube.ws.Common;
+
+import static org.sonar.api.issue.impact.Severity.BLOCKER;
+import static org.sonar.api.issue.impact.Severity.HIGH;
+import static org.sonar.api.issue.impact.Severity.INFO;
+import static org.sonar.api.issue.impact.Severity.LOW;
+import static org.sonar.api.issue.impact.Severity.MEDIUM;
+
+public class ImpactFormatter {
+  private ImpactFormatter() {
+  }
+
+  public static Common.ImpactSeverity mapImpactSeverity(Severity severity) {
+    return switch (severity) {
+      case BLOCKER -> Common.ImpactSeverity.ImpactSeverity_BLOCKER;
+      case HIGH -> Common.ImpactSeverity.HIGH;
+      case MEDIUM -> Common.ImpactSeverity.MEDIUM;
+      case LOW -> Common.ImpactSeverity.LOW;
+      case INFO -> Common.ImpactSeverity.ImpactSeverity_INFO;
+    };
+  }
+
+  public static Severity mapImpactSeverity(Common.ImpactSeverity severity) {
+    return switch (severity) {
+      case ImpactSeverity_BLOCKER -> BLOCKER;
+      case HIGH -> HIGH;
+      case MEDIUM -> MEDIUM;
+      case LOW -> LOW;
+      case ImpactSeverity_INFO -> INFO;
+      case UNKNOWN_IMPACT_SEVERITY -> throw new UnsupportedOperationException("Impact severity not supported");
+    };
+  }
+}
diff --git a/sonar-core/src/test/java/org/sonar/core/rule/ImpactFormatterTest.java b/sonar-core/src/test/java/org/sonar/core/rule/ImpactFormatterTest.java
new file mode 100644 (file)
index 0000000..6883ef1
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2024 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.core.rule;
+
+import org.junit.jupiter.api.Test;
+import org.sonar.api.issue.impact.Severity;
+import org.sonarqube.ws.Common;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+class ImpactFormatterTest {
+
+  @Test
+  void mapImpactSeverity_whenMappingStandardSeverity_shouldReturnExpectedValue() {
+    assertThat(ImpactFormatter.mapImpactSeverity(Severity.BLOCKER)).isEqualTo(Common.ImpactSeverity.ImpactSeverity_BLOCKER);
+    assertThat(ImpactFormatter.mapImpactSeverity(Severity.HIGH)).isEqualTo(Common.ImpactSeverity.HIGH);
+    assertThat(ImpactFormatter.mapImpactSeverity(Severity.MEDIUM)).isEqualTo(Common.ImpactSeverity.MEDIUM);
+    assertThat(ImpactFormatter.mapImpactSeverity(Severity.LOW)).isEqualTo(Common.ImpactSeverity.LOW);
+    assertThat(ImpactFormatter.mapImpactSeverity(Severity.INFO)).isEqualTo(Common.ImpactSeverity.ImpactSeverity_INFO);
+  }
+
+  @Test
+  void mapImpactSeverity_whenMappingProtobufSeverity_shouldReturnExpectedValue() {
+    assertThat(ImpactFormatter.mapImpactSeverity(Common.ImpactSeverity.ImpactSeverity_BLOCKER)).isEqualTo(Severity.BLOCKER);
+    assertThat(ImpactFormatter.mapImpactSeverity(Common.ImpactSeverity.HIGH)).isEqualTo(Severity.HIGH);
+    assertThat(ImpactFormatter.mapImpactSeverity(Common.ImpactSeverity.MEDIUM)).isEqualTo(Severity.MEDIUM);
+    assertThat(ImpactFormatter.mapImpactSeverity(Common.ImpactSeverity.LOW)).isEqualTo(Severity.LOW);
+    assertThat(ImpactFormatter.mapImpactSeverity(Common.ImpactSeverity.ImpactSeverity_INFO)).isEqualTo(Severity.INFO);
+    assertThatThrownBy(() -> ImpactFormatter.mapImpactSeverity(Common.ImpactSeverity.UNKNOWN_IMPACT_SEVERITY))
+      .isInstanceOf(UnsupportedOperationException.class);
+  }
+}
index e0af1b32898f0d692d120239f1ec68e400ea3c64..e893c7842c781c435aa5988906298b27d804f93b 100644 (file)
  */
 package org.sonar.api.batch.rule.internal;
 
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
 import javax.annotation.concurrent.Immutable;
 import org.sonar.api.batch.rule.ActiveRule;
+import org.sonar.api.issue.impact.Severity;
+import org.sonar.api.issue.impact.SoftwareQuality;
 import org.sonar.api.rule.RuleKey;
 
 @Immutable
@@ -36,6 +35,7 @@ public class DefaultActiveRule implements ActiveRule {
   private final String language;
   private final String templateRuleKey;
   private final Map<String, String> params;
+  private final Map<SoftwareQuality, Severity> impacts;
   private final long createdAt;
   private final long updatedAt;
   private final String qProfileKey;
@@ -46,12 +46,13 @@ public class DefaultActiveRule implements ActiveRule {
     this.internalKey = newActiveRule.internalKey;
     this.templateRuleKey = newActiveRule.templateRuleKey;
     this.ruleKey = newActiveRule.ruleKey;
-    this.params = Collections.unmodifiableMap(new HashMap<>(newActiveRule.params));
+    this.params = Map.copyOf(newActiveRule.params);
+    this.impacts = Map.copyOf(newActiveRule.impacts);
     this.language = newActiveRule.language;
     this.createdAt = newActiveRule.createdAt;
     this.updatedAt = newActiveRule.updatedAt;
     this.qProfileKey = newActiveRule.qProfileKey;
-    this.deprecatedKeys = Collections.unmodifiableSet(new HashSet<>(newActiveRule.deprecatedKeys));
+    this.deprecatedKeys = Set.copyOf(newActiveRule.deprecatedKeys);
   }
 
   @Override
@@ -64,6 +65,11 @@ public class DefaultActiveRule implements ActiveRule {
     return severity;
   }
 
+  @Override
+  public Map<SoftwareQuality, Severity> impacts() {
+    return impacts;
+  }
+
   @Override
   public String language() {
     return language;
index 9afdf89f9c97d6359adda623c1597c8c22b6ae3d..cf21a1b39cdb3dd6934c4b39aeda8bfc5729f57b 100644 (file)
@@ -19,6 +19,7 @@
  */
 package org.sonar.api.batch.rule.internal;
 
+import java.util.EnumMap;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
@@ -26,6 +27,7 @@ import java.util.Set;
 import javax.annotation.Nullable;
 import javax.annotation.concurrent.Immutable;
 import org.apache.commons.lang3.StringUtils;
+import org.sonar.api.issue.impact.SoftwareQuality;
 import org.sonar.api.rule.RuleKey;
 import org.sonar.api.rule.Severity;
 
@@ -37,6 +39,7 @@ public class NewActiveRule {
   final RuleKey ruleKey;
   final String name;
   final String severity;
+  final Map<SoftwareQuality, org.sonar.api.issue.impact.Severity> impacts;
   final Map<String, String> params;
   final long createdAt;
   final long updatedAt;
@@ -50,6 +53,7 @@ public class NewActiveRule {
     this.ruleKey = builder.ruleKey;
     this.name = builder.name;
     this.severity = builder.severity;
+    this.impacts = builder.impacts;
     this.params = builder.params;
     this.createdAt = builder.createdAt;
     this.updatedAt = builder.updatedAt;
@@ -68,6 +72,7 @@ public class NewActiveRule {
     private RuleKey ruleKey;
     private String name;
     private String severity = Severity.defaultSeverity();
+    private final Map<SoftwareQuality, org.sonar.api.issue.impact.Severity> impacts = new EnumMap<>(SoftwareQuality.class);
     private Map<String, String> params = new HashMap<>();
     private long createdAt;
     private long updatedAt;
@@ -92,6 +97,11 @@ public class NewActiveRule {
       return this;
     }
 
+    public Builder setImpact(SoftwareQuality softwareQuality, org.sonar.api.issue.impact.Severity severity) {
+      impacts.put(softwareQuality, severity);
+      return this;
+    }
+
     public Builder setParam(String key, @Nullable String value) {
       // possible improvement : check that the param key exists in rule definition
       if (value == null) {
index 51d6facef11462a5d0c1f0cc7a4cf183e0a156e9..449b7a35b40d8f7d9529a110768d1810032d45b5 100644 (file)
 package org.sonar.api.batch.rule.internal;
 
 import com.google.common.collect.ImmutableMap;
+import java.util.Map;
 import org.junit.Before;
 import org.junit.Test;
-import org.sonar.api.batch.rule.internal.NewActiveRule;
+import org.sonar.api.issue.impact.SoftwareQuality;
 import org.sonar.api.rule.RuleKey;
 import org.sonar.api.rule.Severity;
 
@@ -43,6 +44,7 @@ public class NewActiveRuleTest {
       .setRuleKey(RuleKey.of("foo", "bar"))
       .setName("name")
       .setSeverity(org.sonar.api.rule.Severity.CRITICAL)
+      .setImpact(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.MEDIUM)
       .setParam("key", "value")
       .setCreatedAt(1_000L)
       .setUpdatedAt(1_000L)
@@ -55,6 +57,7 @@ public class NewActiveRuleTest {
     assertThat(rule.ruleKey).isEqualTo(RuleKey.of("foo", "bar"));
     assertThat(rule.name).isEqualTo("name");
     assertThat(rule.severity).isEqualTo(org.sonar.api.rule.Severity.CRITICAL);
+    assertThat(rule.impacts).containsExactlyInAnyOrderEntriesOf(Map.of(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.MEDIUM));
     assertThat(rule.params).isEqualTo(ImmutableMap.of("key", "value"));
     assertThat(rule.createdAt).isEqualTo(1_000L);
     assertThat(rule.updatedAt).isEqualTo(1_000L);
index d09209bb2c76f6c8ca680a66ce3a6f13b93f9669..53d04e5faa5391cf8d0006426292259762054a98 100644 (file)
@@ -21,6 +21,7 @@ package org.sonar.scanner.issue;
 
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.EnumMap;
 import java.util.List;
 import java.util.Map;
 import java.util.function.Consumer;
@@ -77,7 +78,7 @@ public class IssuePublisher {
       return false;
     }
 
-    ScannerReport.Issue rawIssue = createReportIssue(issue, inputComponent.scannerId(), activeRule.severity());
+    ScannerReport.Issue rawIssue = createReportIssue(issue, inputComponent.scannerId(), activeRule.severity(), activeRule.impacts());
 
     if (filters.accept(inputComponent, rawIssue)) {
       write(inputComponent.scannerId(), rawIssue);
@@ -107,7 +108,8 @@ public class IssuePublisher {
     return str;
   }
 
-  private static ScannerReport.Issue createReportIssue(Issue issue, int componentRef, String activeRuleSeverity) {
+  private static ScannerReport.Issue createReportIssue(Issue issue, int componentRef, String activeRuleSeverity,
+    Map<SoftwareQuality, org.sonar.api.issue.impact.Severity> activeRuleImpacts) {
     String primaryMessage = nullToEmpty(issue.primaryLocation().message());
     org.sonar.api.batch.rule.Severity overriddenSeverity = issue.overriddenSeverity();
     Severity severity = overriddenSeverity != null ? Severity.valueOf(overriddenSeverity.name()) : Severity.valueOf(activeRuleSeverity);
@@ -121,7 +123,10 @@ public class IssuePublisher {
     builder.setRuleKey(issue.ruleKey().rule());
     builder.setMsg(primaryMessage);
     builder.addAllMsgFormatting(toProtobufMessageFormattings(issue.primaryLocation().messageFormattings()));
-    builder.addAllOverridenImpacts(toProtobufImpacts(issue.overridenImpacts()));
+    Map<SoftwareQuality, org.sonar.api.issue.impact.Severity> overriddenImpacts = new EnumMap<>(issue.overridenImpacts());
+    activeRuleImpacts.entrySet().forEach(e -> overriddenImpacts.putIfAbsent(e.getKey(), e.getValue()));
+    builder.addAllOverridenImpacts(toProtobufImpacts(overriddenImpacts));
+
     locationBuilder.setMsg(primaryMessage);
     locationBuilder.addAllMsgFormatting(toProtobufMessageFormattings(issue.primaryLocation().messageFormattings()));
 
index 4028fbbcd9a89bc2a589774a010f85182d891e5d..8e0691a069e005f6ac70423b73e9bb5552135344 100644 (file)
@@ -29,6 +29,8 @@ import org.sonar.api.batch.rule.LoadedActiveRule;
 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;
@@ -91,6 +93,12 @@ public class ActiveRulesProvider {
       }
     }
 
+    if (activeRule.getImpacts() != null) {
+      for (Map.Entry<SoftwareQuality, Severity> impact : activeRule.getImpacts().entrySet()) {
+        builder.setImpact(impact.getKey(), impact.getValue());
+      }
+    }
+
     return builder.build();
   }
 
index 1bc34c0198c34ac57477fd8a74710dc86c115cf0..9c2bb5d86d1f3522824f7dbb1921811d752a832b 100644 (file)
@@ -21,6 +21,7 @@ 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 java.util.List;
@@ -29,9 +30,13 @@ import java.util.stream.Collectors;
 import org.apache.commons.io.IOUtils;
 import org.sonar.api.batch.rule.LoadedActiveRule;
 import org.sonar.api.impl.utils.ScannerUtils;
+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;
@@ -109,6 +114,7 @@ public class DefaultActiveRulesLoader implements ActiveRulesLoader {
       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());
@@ -128,7 +134,15 @@ public class DefaultActiveRulesLoader implements ActiveRulesLoader {
       for (Param param : active.getParamsList()) {
         params.put(param.getKey(), param.getValue());
       }
+
       loadedRule.setParams(params);
+
+      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);
+
       loadedRule.setDeprecatedKeys(r.getDeprecatedKeys().getDeprecatedKeyList()
         .stream()
         .map(RuleKey::parse)
@@ -138,4 +152,5 @@ public class DefaultActiveRulesLoader implements ActiveRulesLoader {
 
     return loadedRules;
   }
+
 }
index 5a58ff47540b94efa65be53647f6216d578c0b3d..0c4b12443ecf32ab87f6a0b782958273b123fe2c 100644 (file)
@@ -62,6 +62,7 @@ import static org.mockito.Mockito.when;
 import static org.sonar.api.batch.sensor.issue.MessageFormatting.Type.CODE;
 import static org.sonar.api.issue.impact.SoftwareQuality.MAINTAINABILITY;
 import static org.sonar.api.issue.impact.SoftwareQuality.RELIABILITY;
+import static org.sonar.api.issue.impact.SoftwareQuality.SECURITY;
 
 @RunWith(MockitoJUnitRunner.class)
 public class IssuePublisherTest {
@@ -149,7 +150,7 @@ public class IssuePublisherTest {
 
     ScannerReport.Impact impact1 = ScannerReport.Impact.newBuilder().setSoftwareQuality(MAINTAINABILITY.name()).setSeverity("HIGH").build();
     ScannerReport.Impact impact2 = ScannerReport.Impact.newBuilder().setSoftwareQuality(RELIABILITY.name()).setSeverity("LOW").build();
-    assertThat(argument.getValue().getOverridenImpactsList()).containsExactly(impact1, impact2);
+    assertThat(argument.getValue().getOverridenImpactsList()).containsExactlyInAnyOrder(impact1, impact2);
   }
 
   @Test
@@ -243,6 +244,58 @@ public class IssuePublisherTest {
     ArgumentCaptor<ScannerReport.Issue> argument = ArgumentCaptor.forClass(ScannerReport.Issue.class);
     verify(reportPublisher.getWriter()).appendComponentIssue(eq(file.scannerId()), argument.capture());
     assertThat(argument.getValue().getSeverity()).isEqualTo(org.sonar.scanner.protocol.Constants.Severity.INFO);
+    assertThat(argument.getValue().getOverridenImpactsList()).isEmpty();
+  }
+
+  @Test
+  public void initAndAddIssue_whenImpactsOverriddenOnActiveRule_shouldOverrideIssue() {
+    activeRulesBuilder.addRule(new NewActiveRule.Builder()
+      .setRuleKey(NOSONAR_RULE_KEY)
+      .setSeverity(Severity.INFO)
+      .setImpact(MAINTAINABILITY, org.sonar.api.issue.impact.Severity.HIGH)
+      .setQProfileKey("qp-1")
+      .build());
+    initModuleIssues();
+
+    DefaultIssue issue = new DefaultIssue(project)
+      .at(new DefaultIssueLocation().on(file).at(file.selectLine(3)).message("Foo"))
+      .forRule(NOSONAR_RULE_KEY);
+    when(filters.accept(any(InputComponent.class), any(ScannerReport.Issue.class))).thenReturn(true);
+    moduleIssues.initAndAddIssue(issue);
+
+    ArgumentCaptor<ScannerReport.Issue> argument = ArgumentCaptor.forClass(ScannerReport.Issue.class);
+    verify(reportPublisher.getWriter()).appendComponentIssue(eq(file.scannerId()), argument.capture());
+    assertThat(argument.getValue().getSeverity()).isEqualTo(org.sonar.scanner.protocol.Constants.Severity.INFO);
+    assertThat(argument.getValue().getOverridenImpactsList()).extracting(ScannerReport.Impact::getSoftwareQuality, ScannerReport.Impact::getSeverity)
+      .containsExactly(tuple(MAINTAINABILITY.name(), org.sonar.api.issue.impact.Severity.HIGH.name()));
+  }
+
+  @Test
+  public void initAndAddIssue_whenImpactsOverriddenOnActiveRuleAndInIssue_shouldCombineOverriddenImpacts() {
+    activeRulesBuilder.addRule(new NewActiveRule.Builder()
+      .setRuleKey(NOSONAR_RULE_KEY)
+      .setSeverity(Severity.INFO)
+      .setImpact(MAINTAINABILITY, org.sonar.api.issue.impact.Severity.HIGH)
+      .setImpact(SECURITY, org.sonar.api.issue.impact.Severity.INFO)
+      .setQProfileKey("qp-1")
+      .build());
+    initModuleIssues();
+
+    DefaultIssue issue = new DefaultIssue(project)
+      .at(new DefaultIssueLocation().on(file).at(file.selectLine(3)).message("Foo"))
+      .overrideImpact(RELIABILITY, org.sonar.api.issue.impact.Severity.MEDIUM)
+      .overrideImpact(MAINTAINABILITY, org.sonar.api.issue.impact.Severity.LOW)
+      .forRule(NOSONAR_RULE_KEY);
+    when(filters.accept(any(InputComponent.class), any(ScannerReport.Issue.class))).thenReturn(true);
+    moduleIssues.initAndAddIssue(issue);
+
+    ArgumentCaptor<ScannerReport.Issue> argument = ArgumentCaptor.forClass(ScannerReport.Issue.class);
+    verify(reportPublisher.getWriter()).appendComponentIssue(eq(file.scannerId()), argument.capture());
+    assertThat(argument.getValue().getSeverity()).isEqualTo(org.sonar.scanner.protocol.Constants.Severity.INFO);
+    assertThat(argument.getValue().getOverridenImpactsList()).extracting(ScannerReport.Impact::getSoftwareQuality, ScannerReport.Impact::getSeverity)
+      .containsExactlyInAnyOrder(tuple(MAINTAINABILITY.name(), org.sonar.api.issue.impact.Severity.LOW.name()),
+        tuple(RELIABILITY.name(), org.sonar.api.issue.impact.Severity.MEDIUM.name()),
+        tuple(SECURITY.name(), org.sonar.api.issue.impact.Severity.INFO.name()));
   }
 
   @Test
index 32611fb0cf20fe721f53eac59ca29f7e7c5eb880..14a5036e254c4670949d2ea4027a6fe41a8682a0 100644 (file)
@@ -25,11 +25,16 @@ import com.google.common.collect.ImmutableSet;
 import java.util.Date;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
 import org.assertj.core.groups.Tuple;
 import org.junit.Test;
+import org.sonar.api.batch.rule.ActiveRule;
 import org.sonar.api.batch.rule.ActiveRules;
 import org.sonar.api.batch.rule.LoadedActiveRule;
 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;
@@ -50,6 +55,8 @@ public class ActiveRulesProviderTest {
     LoadedActiveRule r2 = mockRule("rule2");
     LoadedActiveRule r3 = mockRule("rule3");
 
+    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);
@@ -65,6 +72,13 @@ public class ActiveRulesProviderTest {
     assertThat(activeRules.findAll()).extracting("ruleKey").containsOnly(
       RuleKey.of("rule1", "rule1"), RuleKey.of("rule2", "rule2"), RuleKey.of("rule3", "rule3"));
 
+    Map<String, ActiveRule> activeRuleByKey = activeRules.findAll().stream().collect(Collectors.toMap(e -> e.ruleKey().rule(), e -> e));
+    assertThat(activeRuleByKey.get("rule1").impacts())
+      .containsExactlyInAnyOrderEntriesOf(Map.of(SoftwareQuality.MAINTAINABILITY, Severity.HIGH));
+
+    assertThat(activeRuleByKey.get("rule2").impacts()).isEmpty();
+    assertThat(activeRuleByKey.get("rule3").impacts()).isEmpty();
+
     verify(loader).load("qp1");
     verify(loader).load("qp2");
     verify(loader).load("qp3");
index bae17ee866ddf798d3355e4a0a40ff17c947037f..ad70dd74d471cf13edc52b9c32d05e65275efa63 100644 (file)
@@ -27,6 +27,7 @@ import java.util.stream.IntStream;
 import org.junit.Before;
 import org.junit.Test;
 import org.sonar.api.batch.rule.LoadedActiveRule;
+import org.sonar.api.issue.impact.SoftwareQuality;
 import org.sonar.api.rule.RuleKey;
 import org.sonar.api.rule.Severity;
 import org.sonar.scanner.WsTestUtil;
@@ -82,6 +83,10 @@ public class DefaultActiveRulesLoaderTest {
       .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));
@@ -119,6 +124,9 @@ public class DefaultActiveRulesLoaderTest {
         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());
         }
         ActiveList activeList = Rules.ActiveList.newBuilder().addActiveList(activeBuilder).build();
         actives.putAllActives(Map.of(key.toString(), activeList));