]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-23250 Update rule activator to support impact severity overrides
authorLéo Geoffroy <leo.geoffroy@sonarsource.com>
Wed, 9 Oct 2024 12:33:03 +0000 (14:33 +0200)
committersonartech <sonartech@sonarsource.com>
Wed, 16 Oct 2024 20:03:01 +0000 (20:03 +0000)
server/sonar-db-dao/src/main/java/org/sonar/db/rule/RuleDto.java
server/sonar-db-dao/src/test/java/org/sonar/db/rule/RuleDtoTest.java
server/sonar-server-common/src/main/java/org/sonar/server/qualityprofile/ActiveRuleChange.java
server/sonar-webserver-api/src/main/java/org/sonar/server/qualityprofile/RuleActivation.java
server/sonar-webserver-webapi/src/it/java/org/sonar/server/qualityprofile/QProfileRuleImplIT.java
server/sonar-webserver-webapi/src/it/java/org/sonar/server/qualityprofile/RegisterQualityProfilesNotificationIT.java
server/sonar-webserver-webapi/src/it/java/org/sonar/server/qualityprofile/builtin/BuiltInQProfileUpdateImplIT.java
server/sonar-webserver-webapi/src/it/java/org/sonar/server/qualityprofile/builtin/RuleActivatorIT.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/QProfileImpactSeverityMapper.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/builtin/RuleActivator.java
server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/QProfileImpactSeverityMapperTest.java

index ea65b61c9076e5b1444ca4dba52474cf4a3ae2f8..6d1f544e0765bcf69d64b16404c4ec8de25fdece 100644 (file)
@@ -23,6 +23,7 @@ import com.google.common.base.Splitter;
 import com.google.common.collect.ImmutableSet;
 import java.util.Collection;
 import java.util.HashSet;
+import java.util.Map;
 import java.util.Objects;
 import java.util.Optional;
 import java.util.Set;
@@ -30,6 +31,7 @@ import java.util.stream.Collectors;
 import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
 import org.apache.commons.lang3.builder.HashCodeBuilder;
+import org.sonar.api.issue.impact.Severity;
 import org.sonar.api.issue.impact.SoftwareQuality;
 import org.sonar.api.rule.RuleKey;
 import org.sonar.api.rule.RuleStatus;
@@ -275,6 +277,11 @@ public class RuleDto {
     return defaultImpacts;
   }
 
+  public Map<SoftwareQuality, Severity> getDefaultImpactsMap() {
+    return defaultImpacts.stream()
+      .collect(Collectors.toMap(ImpactDto::getSoftwareQuality, ImpactDto::getSeverity));
+  }
+
   public RuleDto addDefaultImpact(ImpactDto defaultImpactDto) {
     defaultImpacts.stream().filter(impactDto -> impactDto.getSoftwareQuality() == defaultImpactDto.getSoftwareQuality()).findFirst()
       .ifPresent(impactDto -> {
index c141ab39f9e0e306cb4c6069bfd298bd85637799..4b03c0944effee0d9a845ba2b5df785c4e768c80 100644 (file)
@@ -21,6 +21,7 @@ package org.sonar.db.rule;
 
 import com.google.common.collect.ImmutableSet;
 import java.util.Collections;
+import java.util.Map;
 import java.util.Set;
 import java.util.TreeSet;
 import org.jetbrains.annotations.NotNull;
@@ -214,6 +215,25 @@ class RuleDtoTest {
         tuple(SoftwareQuality.SECURITY, Severity.LOW));
   }
 
+  @Test
+  void getDefaultImpactsMap_shouldReturnExpectedResult() {
+    RuleDto dto = new RuleDto();
+    dto.addDefaultImpact(newImpactDto(SoftwareQuality.MAINTAINABILITY, Severity.MEDIUM));
+    dto.addDefaultImpact(newImpactDto(SoftwareQuality.SECURITY, Severity.HIGH));
+    dto.addDefaultImpact(newImpactDto(SoftwareQuality.RELIABILITY, Severity.LOW));
+
+    assertThat(dto.getDefaultImpactsMap())
+      .containsExactlyInAnyOrderEntriesOf(Map.of(SoftwareQuality.MAINTAINABILITY, Severity.MEDIUM,
+        SoftwareQuality.SECURITY, Severity.HIGH,
+        SoftwareQuality.RELIABILITY, Severity.LOW));
+  }
+
+  @Test
+  void getDefaultImpactsMap_whenIsEmpty_shouldReturnEmptyMap() {
+    RuleDto dto = new RuleDto();
+    assertThat(dto.getDefaultImpactsMap()).isEmpty();
+  }
+
   @Test
   void getEnumType_shouldReturnCorrectValue() {
     RuleDto ruleDto = new RuleDto();
index 65412f21a663ddf40604f55a4116139e21bcd784..eefcb5b8bd28e7550dfe2085f8a2604ac5f13311 100644 (file)
 package org.sonar.server.qualityprofile;
 
 import com.google.common.base.MoreObjects;
+import java.util.EnumMap;
 import java.util.HashMap;
 import java.util.Map;
 import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
 import org.apache.commons.lang3.StringUtils;
+import org.sonar.api.issue.impact.Severity;
+import org.sonar.api.issue.impact.SoftwareQuality;
 import org.sonar.db.qualityprofile.ActiveRuleDto;
 import org.sonar.db.qualityprofile.ActiveRuleKey;
 import org.sonar.db.qualityprofile.QProfileChangeDto;
@@ -42,6 +45,7 @@ public class ActiveRuleChange {
   private final ActiveRuleKey key;
   private final String ruleUuid;
   private String severity = null;
+  private final Map<SoftwareQuality, Severity> impactSeverities = new EnumMap<>(SoftwareQuality.class);
   private Boolean prioritizedRule = null;
   private ActiveRuleInheritance inheritance = null;
   private final Map<String, String> parameters = new HashMap<>();
@@ -81,13 +85,23 @@ public class ActiveRuleChange {
     return this;
   }
 
-  public ActiveRuleChange setPrioritizedRule(@Nullable Boolean prioritizedRule){
+  public Map<SoftwareQuality, Severity> getImpactSeverities() {
+    return impactSeverities;
+  }
+
+  public ActiveRuleChange setImpactSeverities(Map<SoftwareQuality, Severity> impactSeverities) {
+    this.impactSeverities.clear();
+    this.impactSeverities.putAll(impactSeverities);
+    return this;
+  }
+
+  public ActiveRuleChange setPrioritizedRule(@Nullable Boolean prioritizedRule) {
     this.prioritizedRule = prioritizedRule;
     return this;
   }
 
   @CheckForNull
-  public Boolean isPrioritizedRule(){
+  public Boolean isPrioritizedRule() {
     return prioritizedRule;
   }
 
@@ -159,6 +173,7 @@ public class ActiveRuleChange {
       .add("inheritance", inheritance)
       .add("parameters", parameters)
       .add("prioritizedRule", prioritizedRule)
+      .add("impactSeverities", impactSeverities)
       .toString();
   }
 }
index 46abd860e6aab1fcb5c6446b942051de218cd8ed..5cf81f7a54729c8ac478a2d98a722eb3e1674c91 100644 (file)
 package org.sonar.server.qualityprofile;
 
 import com.google.common.base.Strings;
+import java.util.EnumMap;
 import java.util.HashMap;
 import java.util.Map;
 import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
 import javax.annotation.concurrent.Immutable;
+import org.sonar.api.issue.impact.SoftwareQuality;
 import org.sonar.api.rule.Severity;
 
 /**
@@ -36,15 +38,17 @@ public class RuleActivation {
   private final String ruleUuid;
   private final boolean reset;
   private final String severity;
+  private final Map<SoftwareQuality, org.sonar.api.issue.impact.Severity> impactSeverities;
   private final Boolean prioritizedRule;
   private final Map<String, String> parameters = new HashMap<>();
 
-  private RuleActivation(String ruleUuid, boolean reset, @Nullable String severity, @Nullable Boolean prioritizedRule, @Nullable Map<String,
-    String> parameters) {
+  private RuleActivation(String ruleUuid, boolean reset, @Nullable String severity, @Nullable Boolean prioritizedRule, @Nullable Map<String, String> parameters,
+    Map<SoftwareQuality, org.sonar.api.issue.impact.Severity> impactSeverities) {
     this.ruleUuid = ruleUuid;
     this.reset = reset;
     this.severity = severity;
     this.prioritizedRule = prioritizedRule;
+    this.impactSeverities = impactSeverities.isEmpty() ? Map.of() : new EnumMap<>(impactSeverities);
     if (severity != null && !Severity.ALL.contains(severity)) {
       throw new IllegalArgumentException("Unknown severity: " + severity);
     }
@@ -56,18 +60,23 @@ public class RuleActivation {
   }
 
   public static RuleActivation createReset(String ruleUuid) {
-    return new RuleActivation(ruleUuid, true, null, null, null);
+    return new RuleActivation(ruleUuid, true, null, null, null, Map.of());
   }
 
   public static RuleActivation create(String ruleUuid, @Nullable String severity, @Nullable Boolean prioritizedRule,
     @Nullable Map<String, String> parameters) {
-    return new RuleActivation(ruleUuid, false, severity, prioritizedRule, parameters);
+    return new RuleActivation(ruleUuid, false, severity, prioritizedRule, parameters, Map.of());
   }
 
   public static RuleActivation create(String ruleUuid, @Nullable String severity, @Nullable Map<String, String> parameters) {
     return create(ruleUuid, severity, null, parameters);
   }
 
+  public static RuleActivation createOverrideImpacts(String ruleUuid, Map<SoftwareQuality, org.sonar.api.issue.impact.Severity> impactSeverities,
+    @Nullable Map<String, String> parameters) {
+    return new RuleActivation(ruleUuid, false, null, null, parameters, impactSeverities);
+  }
+
   public static RuleActivation create(String ruleUuid) {
     return create(ruleUuid, null, null, null);
   }
@@ -80,6 +89,10 @@ public class RuleActivation {
     return severity;
   }
 
+  public Map<SoftwareQuality, org.sonar.api.issue.impact.Severity> getImpactSeverities() {
+    return impactSeverities;
+  }
+
   public String getRuleUuid() {
     return ruleUuid;
   }
index 309292a3f828ad53bc86e319e98feccded9e4c70..ed5407003cff60638db1a48cbba421b98a8322e3 100644 (file)
@@ -33,6 +33,7 @@ import org.mockito.Mockito;
 import org.sonar.api.PropertyType;
 import org.sonar.api.config.Configuration;
 import org.sonar.api.impl.utils.AlwaysIncreasingSystem2;
+import org.sonar.api.issue.impact.SoftwareQuality;
 import org.sonar.api.rule.RuleStatus;
 import org.sonar.api.rule.Severity;
 import org.sonar.api.utils.System2;
@@ -40,6 +41,7 @@ import org.sonar.api.utils.Version;
 import org.sonar.core.config.CorePropertyDefinitions;
 import org.sonar.core.platform.SonarQubeVersion;
 import org.sonar.db.DbTester;
+import org.sonar.db.issue.ImpactDto;
 import org.sonar.db.qualityprofile.ActiveRuleParamDto;
 import org.sonar.db.qualityprofile.OrgActiveRuleDto;
 import org.sonar.db.qualityprofile.QProfileDto;
@@ -112,7 +114,7 @@ class QProfileRuleImplIT {
     RuleActivation activation = RuleActivation.create(rule.getUuid(), BLOCKER, null);
     List<ActiveRuleChange> changes = activate(profile, activation);
 
-    assertThatRuleIsActivated(profile, rule, changes, BLOCKER, null, emptyMap());
+    assertThatRuleIsActivated(profile, rule, changes, BLOCKER, Map.of(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.BLOCKER), null, emptyMap());
     assertThatProfileIsUpdatedBySystem(profile);
     verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), any(), eq(profile.getLanguage()));
   }
@@ -125,7 +127,7 @@ class QProfileRuleImplIT {
     RuleActivation activation = RuleActivation.create(rule.getUuid(), BLOCKER, null);
     List<ActiveRuleChange> changes = activate(profile, activation);
 
-    assertThatRuleIsActivated(profile, rule, changes, BLOCKER, null, emptyMap());
+    assertThatRuleIsActivated(profile, rule, changes, BLOCKER, Map.of(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.BLOCKER), null, emptyMap());
     assertThatProfileIsUpdatedByUser(profile);
     verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), any(), eq(profile.getLanguage()));
   }
@@ -139,7 +141,7 @@ class QProfileRuleImplIT {
     RuleActivation activation = RuleActivation.create(rule.getUuid());
     List<ActiveRuleChange> changes = activate(profile, activation);
 
-    assertThatRuleIsActivated(profile, rule, changes, rule.getSeverityString(), null, ofEntries(entry("min", "10")));
+    assertThatRuleIsActivated(profile, rule, changes, rule.getSeverityString(), rule.getDefaultImpactsMap(), null, ofEntries(entry("min", "10")));
     assertThatProfileIsUpdatedBySystem(profile);
     verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), any(), eq(profile.getLanguage()));
   }
@@ -153,7 +155,7 @@ class QProfileRuleImplIT {
     RuleActivation activation = RuleActivation.create(rule.getUuid(), null, of(ruleParam.getName(), "15"));
     List<ActiveRuleChange> changes = activate(profile, activation);
 
-    assertThatRuleIsActivated(profile, rule, changes, rule.getSeverityString(), null, of("min", "15"));
+    assertThatRuleIsActivated(profile, rule, changes, rule.getSeverityString(), rule.getDefaultImpactsMap(), null, of("min", "15"));
     assertThatProfileIsUpdatedBySystem(profile);
     verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), any(), eq(profile.getLanguage()));
   }
@@ -166,7 +168,7 @@ class QProfileRuleImplIT {
     RuleActivation activation = RuleActivation.create(rule.getUuid());
     List<ActiveRuleChange> changes = activate(profile, activation);
 
-    assertThatRuleIsActivated(profile, rule, changes, rule.getSeverityString(), null, emptyMap());
+    assertThatRuleIsActivated(profile, rule, changes, rule.getSeverityString(), rule.getDefaultImpactsMap(), null, emptyMap());
     assertThatProfileIsUpdatedBySystem(profile);
     verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), any(), eq(profile.getLanguage()));
   }
@@ -183,7 +185,7 @@ class QProfileRuleImplIT {
     RuleActivation activation = RuleActivation.create(rule.getUuid(), null, of("min", ""));
     List<ActiveRuleChange> changes = activate(profile, activation);
 
-    assertThatRuleIsActivated(profile, rule, changes, rule.getSeverityString(), null, of("min", "10"));
+    assertThatRuleIsActivated(profile, rule, changes, rule.getSeverityString(), rule.getDefaultImpactsMap(), null, of("min", "10"));
     assertThatProfileIsUpdatedBySystem(profile);
     verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), any(), eq(profile.getLanguage()));
   }
@@ -202,7 +204,7 @@ class QProfileRuleImplIT {
     RuleActivation activation = RuleActivation.create(rule.getUuid(), null, of(paramWithoutDefault.getName(), "-10"));
     List<ActiveRuleChange> changes = activate(profile, activation);
 
-    assertThatRuleIsActivated(profile, rule, changes, rule.getSeverityString(), null,
+    assertThatRuleIsActivated(profile, rule, changes, rule.getSeverityString(), rule.getDefaultImpactsMap(), null,
       of(paramWithoutDefault.getName(), "-10", paramWithDefault.getName(), paramWithDefault.getDefaultValue()));
     assertThatProfileIsUpdatedBySystem(profile);
     verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), any(), eq(profile.getLanguage()));
@@ -217,7 +219,7 @@ class QProfileRuleImplIT {
     RuleActivation activation = RuleActivation.create(rule.getUuid(), null, of("xxx", "yyy"));
     List<ActiveRuleChange> changes = activate(profile, activation);
 
-    assertThatRuleIsActivated(profile, rule, changes, rule.getSeverityString(), null, of(param.getName(), param.getDefaultValue()));
+    assertThatRuleIsActivated(profile, rule, changes, rule.getSeverityString(), rule.getDefaultImpactsMap(), null, of(param.getName(), param.getDefaultValue()));
     assertThatProfileIsUpdatedBySystem(profile);
     verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), any(), eq(profile.getLanguage()));
   }
@@ -237,7 +239,7 @@ class QProfileRuleImplIT {
     RuleActivation updateActivation = RuleActivation.create(rule.getUuid(), CRITICAL, of(param.getName(), "20"));
     changes = activate(profile, updateActivation);
 
-    assertThatRuleIsUpdated(profile, rule, CRITICAL, null, of(param.getName(), "20"));
+    assertThatRuleIsUpdated(profile, rule, CRITICAL, Map.of(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.HIGH), null, of(param.getName(), "20"));
     assertThatProfileIsUpdatedBySystem(profile);
     verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(profile.getLanguage()));
   }
@@ -258,7 +260,8 @@ class QProfileRuleImplIT {
     changes = activate(profile, updateActivation);
     verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(profile.getLanguage()));
 
-    assertThatRuleIsUpdated(profile, rule, MAJOR, null, of(paramWithDefault.getName(), "10", paramWithoutDefault.getName(), "3"));
+    assertThatRuleIsUpdated(profile, rule, MAJOR, Map.of(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.MEDIUM), null,
+      of(paramWithDefault.getName(), "10", paramWithoutDefault.getName(), "3"));
     assertThatProfileIsUpdatedBySystem(profile);
     verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(profile.getLanguage()));
   }
@@ -278,7 +281,7 @@ class QProfileRuleImplIT {
     RuleActivation updateActivation = RuleActivation.create(rule.getUuid(), null, of(paramWithDefault.getName(), ""));
     changes = activate(profile, updateActivation);
 
-    assertThatRuleIsUpdated(profile, rule, rule.getSeverityString(), null, of(paramWithDefault.getName(), "10"));
+    assertThatRuleIsUpdated(profile, rule, rule.getSeverityString(), rule.getDefaultImpactsMap(), null, of(paramWithDefault.getName(), "10"));
     assertThat(changes).hasSize(1);
     verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(profile.getLanguage()));
   }
@@ -299,7 +302,7 @@ class QProfileRuleImplIT {
     RuleActivation updateActivation = RuleActivation.create(rule.getUuid(), null, of(paramWithoutDefault.getName(), ""));
     changes = activate(profile, updateActivation);
 
-    assertThatRuleIsUpdated(profile, rule, rule.getSeverityString(), null, of(paramWithDefault.getName(),
+    assertThatRuleIsUpdated(profile, rule, rule.getSeverityString(), rule.getDefaultImpactsMap(), null, of(paramWithDefault.getName(),
       paramWithDefault.getDefaultValue()));
     assertThat(changes).hasSize(1);
     verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(profile.getLanguage()));
@@ -315,14 +318,14 @@ class QProfileRuleImplIT {
     RuleActivation activation = RuleActivation.create(rule.getUuid());
     List<ActiveRuleChange> changes = activate(profile, activation);
     db.getDbClient().activeRuleDao().deleteParametersByRuleProfileUuids(db.getSession(), asList(profile.getRulesProfileUuid()));
-    assertThatRuleIsActivated(profile, rule, changes, rule.getSeverityString(), null, emptyMap());
+    assertThatRuleIsActivated(profile, rule, changes, rule.getSeverityString(), rule.getDefaultImpactsMap(), null, emptyMap());
     verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(profile.getLanguage()));
 
     // contrary to activerule, the param is supposed to be inserted but not updated
     RuleActivation updateActivation = RuleActivation.create(rule.getUuid(), null, of(param.getName(), ""));
     changes = activate(profile, updateActivation);
 
-    assertThatRuleIsUpdated(profile, rule, rule.getSeverityString(), null, of(param.getName(), param.getDefaultValue()));
+    assertThatRuleIsUpdated(profile, rule, rule.getSeverityString(), rule.getDefaultImpactsMap(), null, of(param.getName(), param.getDefaultValue()));
     assertThat(changes).hasSize(1);
     verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(profile.getLanguage()));
   }
@@ -356,7 +359,6 @@ class QProfileRuleImplIT {
     List<ActiveRuleChange> changes = activate(profile, activation);
     verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(profile.getLanguage()));
 
-
     // update without any severity or params => keep
     RuleActivation update = RuleActivation.create(rule.getUuid());
     changes = activate(profile, update);
@@ -418,13 +420,13 @@ class QProfileRuleImplIT {
     // initial activation
     RuleActivation activation = RuleActivation.create(customRule.getUuid(), MAJOR, emptyMap());
     List<ActiveRuleChange> changes = activate(profile, activation);
-    assertThatRuleIsActivated(profile, customRule, null, MAJOR, null, of("format", "txt"));
+    assertThatRuleIsActivated(profile, customRule, null, MAJOR, Map.of(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.MEDIUM), null, of("format", "txt"));
     verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(profile.getLanguage()));
 
     // update -> parameter is not changed
     RuleActivation updateActivation = RuleActivation.create(customRule.getUuid(), BLOCKER, of("format", "xml"));
     changes = activate(profile, updateActivation);
-    assertThatRuleIsActivated(profile, customRule, null, BLOCKER, null, of("format", "txt"));
+    assertThatRuleIsActivated(profile, customRule, null, BLOCKER, Map.of(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.BLOCKER), null, of("format", "txt"));
     verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(profile.getLanguage()));
   }
 
@@ -504,8 +506,8 @@ class QProfileRuleImplIT {
 
     List<ActiveRuleChange> changes = activate(childProfile, RuleActivation.create(rule.getUuid()));
     assertThatProfileHasNoActiveRules(parentProfile);
-    assertThatRuleIsActivated(childProfile, rule, changes, rule.getSeverityString(), null, emptyMap());
-    assertThatRuleIsActivated(grandChildProfile, rule, changes, rule.getSeverityString(), INHERITED, emptyMap());
+    assertThatRuleIsActivated(childProfile, rule, changes, rule.getSeverityString(), rule.getDefaultImpactsMap(), null, emptyMap());
+    assertThatRuleIsActivated(grandChildProfile, rule, changes, rule.getSeverityString(), rule.getDefaultImpactsMap(), INHERITED, emptyMap());
     verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), any(), eq(childProfile.getLanguage()));
   }
 
@@ -521,16 +523,16 @@ class QProfileRuleImplIT {
 
     // Rule already active on childProfile2
     List<ActiveRuleChange> changes = activate(childProfile2, activation);
-    assertThatRuleIsActivated(childProfile2, rule, changes, rule.getSeverityString(), null, emptyMap());
+    assertThatRuleIsActivated(childProfile2, rule, changes, rule.getSeverityString(), rule.getDefaultImpactsMap(), null, emptyMap());
     verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(parentProfile.getLanguage()));
     deactivate(childProfile3, rule);
     assertThatProfileHasNoActiveRules(childProfile3);
 
     changes = activate(parentProfile, activation);
-    assertThatRuleIsActivated(parentProfile, rule, changes, rule.getSeverityString(), null, emptyMap());
-    assertThatRuleIsActivated(childProfile, rule, changes, rule.getSeverityString(), INHERITED, emptyMap());
-    assertThatRuleIsUpdated(childProfile2, rule, rule.getSeverityString(), INHERITED, emptyMap());
-    assertThatRuleIsActivated(childProfile3, rule, changes, rule.getSeverityString(), INHERITED, emptyMap());
+    assertThatRuleIsActivated(parentProfile, rule, changes, rule.getSeverityString(), rule.getDefaultImpactsMap(), null, emptyMap());
+    assertThatRuleIsActivated(childProfile, rule, changes, rule.getSeverityString(), rule.getDefaultImpactsMap(), INHERITED, emptyMap());
+    assertThatRuleIsUpdated(childProfile2, rule, rule.getSeverityString(), rule.getDefaultImpactsMap(), INHERITED, emptyMap());
+    assertThatRuleIsActivated(childProfile3, rule, changes, rule.getSeverityString(), rule.getDefaultImpactsMap(), INHERITED, emptyMap());
     assertThat(changes).hasSize(4);
     verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(parentProfile.getLanguage()));
   }
@@ -538,20 +540,21 @@ class QProfileRuleImplIT {
   @Test
   void activate_whenChildAlreadyActivatedRuleWithOverriddenValues_shouldNotOverrideValues() {
     RuleDto rule = createRule();
+    rule.replaceAllDefaultImpacts(List.of(new ImpactDto().setSoftwareQuality(SoftwareQuality.MAINTAINABILITY).setSeverity(org.sonar.api.issue.impact.Severity.HIGH)));
     QProfileDto parentProfile = createProfile(rule);
     QProfileDto childProfile = createChildProfile(parentProfile);
     QProfileDto childProfile2 = createChildProfile(childProfile);
     QProfileDto childProfile3 = createChildProfile(childProfile2);
 
     List<ActiveRuleChange> changes = activate(childProfile2, RuleActivation.create(rule.getUuid(), CRITICAL, emptyMap()));
-    assertThatRuleIsActivated(childProfile2, rule, changes, CRITICAL, null, emptyMap());
-    assertThatRuleIsActivated(childProfile3, rule, changes, CRITICAL, INHERITED, emptyMap());
+    assertThatRuleIsActivated(childProfile2, rule, changes, CRITICAL, Map.of(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.HIGH), null, emptyMap());
+    assertThatRuleIsActivated(childProfile3, rule, changes, CRITICAL, Map.of(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.HIGH), INHERITED, emptyMap());
     verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(parentProfile.getLanguage()));
 
     changes = activate(parentProfile, RuleActivation.create(rule.getUuid(), MAJOR, emptyMap()));
-    assertThatRuleIsActivated(parentProfile, rule, changes, MAJOR, null, emptyMap());
-    assertThatRuleIsActivated(childProfile, rule, changes, MAJOR, INHERITED, emptyMap());
-    assertThatRuleIsUpdated(childProfile2, rule, CRITICAL, OVERRIDES, emptyMap());
+    assertThatRuleIsActivated(parentProfile, rule, changes, MAJOR, Map.of(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.MEDIUM), null, emptyMap());
+    assertThatRuleIsActivated(childProfile, rule, changes, MAJOR, Map.of(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.MEDIUM), INHERITED, emptyMap());
+    assertThatRuleIsUpdated(childProfile2, rule, CRITICAL, Map.of(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.HIGH), OVERRIDES, emptyMap());
     // childProfile3 is neither activated nor updated, it keeps its inherited value from childProfile2
     assertThat(changes).hasSize(3);
     verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(parentProfile.getLanguage()));
@@ -564,12 +567,12 @@ class QProfileRuleImplIT {
     QProfileDto childProfile = createChildProfile(parentProfile);
 
     List<ActiveRuleChange> changes = activate(parentProfile, RuleActivation.create(rule.getUuid(), CRITICAL, emptyMap()));
-    assertThatRuleIsActivated(parentProfile, rule, changes, CRITICAL, null, emptyMap());
+    assertThatRuleIsActivated(parentProfile, rule, changes, CRITICAL, Map.of(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.HIGH), null, emptyMap());
     deactivate(childProfile, rule);
     assertThatProfileHasNoActiveRules(childProfile);
 
     changes = activate(childProfile, RuleActivation.create(rule.getUuid(), CRITICAL, emptyMap()));
-    assertThatRuleIsActivated(childProfile, rule, changes, CRITICAL, INHERITED, emptyMap());
+    assertThatRuleIsActivated(childProfile, rule, changes, CRITICAL, Map.of(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.HIGH), INHERITED, emptyMap());
     assertThat(changes).hasSize(1);
   }
 
@@ -580,12 +583,12 @@ class QProfileRuleImplIT {
     QProfileDto childProfile = createChildProfile(parentProfile);
 
     List<ActiveRuleChange> changes = activate(parentProfile, RuleActivation.create(rule.getUuid(), CRITICAL, emptyMap()));
-    assertThatRuleIsActivated(parentProfile, rule, changes, CRITICAL, null, emptyMap());
+    assertThatRuleIsActivated(parentProfile, rule, changes, CRITICAL, Map.of(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.HIGH), null, emptyMap());
     deactivate(childProfile, rule);
     assertThatProfileHasNoActiveRules(childProfile);
 
     changes = activate(childProfile, RuleActivation.create(rule.getUuid(), MAJOR, emptyMap()));
-    assertThatRuleIsActivated(childProfile, rule, changes, MAJOR, OVERRIDES, emptyMap());
+    assertThatRuleIsActivated(childProfile, rule, changes, MAJOR, Map.of(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.MEDIUM), OVERRIDES, emptyMap());
     assertThat(changes).hasSize(1);
   }
 
@@ -609,8 +612,9 @@ class QProfileRuleImplIT {
     verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(childProfile.getLanguage()));
 
     assertThatProfileHasNoActiveRules(parentProfile);
-    assertThatRuleIsUpdated(childProfile, rule, CRITICAL, null, of(param.getName(), "bar"));
-    assertThatRuleIsUpdated(grandChildProfile, rule, CRITICAL, INHERITED, of(param.getName(), "bar"));
+    assertThatRuleIsUpdated(childProfile, rule, CRITICAL, Map.of(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.HIGH), null, of(param.getName(), "bar"));
+    assertThatRuleIsUpdated(grandChildProfile, rule, CRITICAL, Map.of(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.HIGH), INHERITED,
+      of(param.getName(), "bar"));
     assertThat(changes).hasSize(2);
     verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(childProfile.getLanguage()));
   }
@@ -631,8 +635,9 @@ class QProfileRuleImplIT {
     changes = activate(grandChildProfile, overrideActivation);
 
     assertThatProfileHasNoActiveRules(parentProfile);
-    assertThatRuleIsUpdated(childProfile, rule, MAJOR, null, of(param.getName(), "foo"));
-    assertThatRuleIsUpdated(grandChildProfile, rule, CRITICAL, OVERRIDES, of(param.getName(), "bar"));
+    assertThatRuleIsUpdated(childProfile, rule, MAJOR, Map.of(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.MEDIUM), null, of(param.getName(), "foo"));
+    assertThatRuleIsUpdated(grandChildProfile, rule, CRITICAL, Map.of(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.HIGH), OVERRIDES,
+      of(param.getName(), "bar"));
     assertThat(changes).hasSize(1);
     verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(childProfile.getLanguage()));
   }
@@ -658,8 +663,9 @@ class QProfileRuleImplIT {
     changes = activate(childProfile, updateActivation);
 
     assertThatProfileHasNoActiveRules(parentProfile);
-    assertThatRuleIsUpdated(childProfile, rule, BLOCKER, null, of(param.getName(), "baz"));
-    assertThatRuleIsUpdated(grandChildProfile, rule, CRITICAL, OVERRIDES, of(param.getName(), "bar"));
+    assertThatRuleIsUpdated(childProfile, rule, BLOCKER, Map.of(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.BLOCKER), null, of(param.getName(), "baz"));
+    assertThatRuleIsUpdated(grandChildProfile, rule, CRITICAL, Map.of(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.HIGH), OVERRIDES,
+      of(param.getName(), "bar"));
     assertThat(changes).hasSize(1);
     verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(childProfile.getLanguage()));
   }
@@ -686,9 +692,10 @@ class QProfileRuleImplIT {
 
     Map<String, String> test = new HashMap<>();
     test.put(param.getName(), param.getDefaultValue());
-    assertThatRuleIsUpdated(parentProfile, rule, rule.getSeverityString(), null, test);
-    assertThatRuleIsUpdated(childProfile, rule, rule.getSeverityString(), INHERITED, of(param.getName(), param.getDefaultValue()));
-    assertThatRuleIsUpdated(grandChildProfile, rule, CRITICAL, OVERRIDES, of(param.getName(), "bar"));
+    assertThatRuleIsUpdated(parentProfile, rule, rule.getSeverityString(), rule.getDefaultImpactsMap(), null, test);
+    assertThatRuleIsUpdated(childProfile, rule, rule.getSeverityString(), rule.getDefaultImpactsMap(), INHERITED, of(param.getName(), param.getDefaultValue()));
+    assertThatRuleIsUpdated(grandChildProfile, rule, CRITICAL, Map.of(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.HIGH), OVERRIDES,
+      of(param.getName(), "bar"));
     assertThat(changes).hasSize(2);
     verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(parentProfile.getLanguage()));
   }
@@ -707,8 +714,8 @@ class QProfileRuleImplIT {
     RuleActivation parentActivation = RuleActivation.create(rule.getUuid(), MAJOR, of(param.getName(), "bar"));
     changes = activate(parentProfile, parentActivation);
 
-    assertThatRuleIsUpdated(parentProfile, rule, MAJOR, null, of(param.getName(), "bar"));
-    assertThatRuleIsUpdated(childProfile, rule, MAJOR, OVERRIDES, of(param.getName(), "foo"));
+    assertThatRuleIsUpdated(parentProfile, rule, MAJOR, Map.of(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.MEDIUM), null, of(param.getName(), "bar"));
+    assertThatRuleIsUpdated(childProfile, rule, MAJOR, Map.of(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.MEDIUM), OVERRIDES, of(param.getName(), "foo"));
     assertThat(changes).hasSize(2);
     verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(parentProfile.getLanguage()));
   }
@@ -727,8 +734,8 @@ class QProfileRuleImplIT {
     RuleActivation parentActivation = RuleActivation.create(rule.getUuid(), MAJOR, of(param.getName(), "foo"));
     changes = activate(parentProfile, parentActivation);
 
-    assertThatRuleIsUpdated(parentProfile, rule, MAJOR, null, of(param.getName(), "foo"));
-    assertThatRuleIsUpdated(childProfile, rule, MAJOR, INHERITED, of(param.getName(), "foo"));
+    assertThatRuleIsUpdated(parentProfile, rule, MAJOR, Map.of(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.MEDIUM), null, of(param.getName(), "foo"));
+    assertThatRuleIsUpdated(childProfile, rule, MAJOR, Map.of(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.MEDIUM), INHERITED, of(param.getName(), "foo"));
     assertThat(changes).hasSize(2);
     verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(parentProfile.getLanguage()));
   }
@@ -747,7 +754,7 @@ class QProfileRuleImplIT {
     RuleActivation overrideActivation = RuleActivation.create(rule.getUuid(), MAJOR, of(param.getName(), "foo"));
     changes = activate(childProfile, overrideActivation);
 
-    assertThatRuleIsUpdated(childProfile, rule, MAJOR, INHERITED, of(param.getName(), "foo"));
+    assertThatRuleIsUpdated(childProfile, rule, MAJOR, Map.of(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.MEDIUM), INHERITED, of(param.getName(), "foo"));
     assertThat(changes).isEmpty();
     verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(childProfile.getLanguage()));
   }
@@ -766,7 +773,7 @@ class QProfileRuleImplIT {
     RuleActivation overrideActivation = RuleActivation.create(rule.getUuid(), CRITICAL, of(param.getName(), "bar"));
     changes = activate(childProfile, overrideActivation);
 
-    assertThatRuleIsUpdated(childProfile, rule, CRITICAL, OVERRIDES, of(param.getName(), "bar"));
+    assertThatRuleIsUpdated(childProfile, rule, CRITICAL, Map.of(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.HIGH), OVERRIDES, of(param.getName(), "bar"));
     assertThat(changes).hasSize(1);
     verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(childProfile.getLanguage()));
   }
@@ -779,8 +786,8 @@ class QProfileRuleImplIT {
 
     RuleActivation activation = RuleActivation.create(rule.getUuid());
     List<ActiveRuleChange> changes = activate(parentProfile, activation);
-    assertThatRuleIsActivated(parentProfile, rule, changes, rule.getSeverityString(), null, emptyMap());
-    assertThatRuleIsActivated(childProfile, rule, changes, rule.getSeverityString(), INHERITED, emptyMap());
+    assertThatRuleIsActivated(parentProfile, rule, changes, rule.getSeverityString(), rule.getDefaultImpactsMap(), null, emptyMap());
+    assertThatRuleIsActivated(childProfile, rule, changes, rule.getSeverityString(), rule.getDefaultImpactsMap(), INHERITED, emptyMap());
     verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(parentProfile.getLanguage()));
 
     changes = deactivate(parentProfile, rule);
@@ -802,15 +809,15 @@ class QProfileRuleImplIT {
 
     // Rule active on parentProfile, childProfile1 and childProfile3 but not on childProfile2
     List<ActiveRuleChange> changes = activate(parentProfile, activation);
-    assertThatRuleIsActivated(parentProfile, rule, changes, rule.getSeverityString(), null, emptyMap());
-    assertThatRuleIsActivated(childProfile, rule, changes, rule.getSeverityString(), INHERITED, emptyMap());
-    assertThatRuleIsActivated(childProfile2, rule, changes, rule.getSeverityString(), INHERITED, emptyMap());
-    assertThatRuleIsActivated(childProfile3, rule, changes, rule.getSeverityString(), INHERITED, emptyMap());
+    assertThatRuleIsActivated(parentProfile, rule, changes, rule.getSeverityString(), rule.getDefaultImpactsMap(), null, emptyMap());
+    assertThatRuleIsActivated(childProfile, rule, changes, rule.getSeverityString(), rule.getDefaultImpactsMap(), INHERITED, emptyMap());
+    assertThatRuleIsActivated(childProfile2, rule, changes, rule.getSeverityString(), rule.getDefaultImpactsMap(), INHERITED, emptyMap());
+    assertThatRuleIsActivated(childProfile3, rule, changes, rule.getSeverityString(), rule.getDefaultImpactsMap(), INHERITED, emptyMap());
     verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(parentProfile.getLanguage()));
     deactivate(childProfile2, rule);
     changes = activate(childProfile3, activation);
     assertThatProfileHasNoActiveRules(childProfile2);
-    assertThatRuleIsActivated(childProfile3, rule, changes, rule.getSeverityString(), null, emptyMap());
+    assertThatRuleIsActivated(childProfile3, rule, changes, rule.getSeverityString(), rule.getDefaultImpactsMap(), null, emptyMap());
 
     changes = deactivate(parentProfile, rule);
     assertThatProfileHasNoActiveRules(parentProfile);
@@ -829,13 +836,13 @@ class QProfileRuleImplIT {
 
     RuleActivation activation = RuleActivation.create(rule.getUuid());
     List<ActiveRuleChange> changes = activate(parentProfile, activation);
-    assertThatRuleIsActivated(parentProfile, rule, changes, rule.getSeverityString(), null, emptyMap());
-    assertThatRuleIsActivated(childProfile, rule, changes, rule.getSeverityString(), INHERITED, emptyMap());
+    assertThatRuleIsActivated(parentProfile, rule, changes, rule.getSeverityString(), rule.getDefaultImpactsMap(), null, emptyMap());
+    assertThatRuleIsActivated(childProfile, rule, changes, rule.getSeverityString(), rule.getDefaultImpactsMap(), INHERITED, emptyMap());
     verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(parentProfile.getLanguage()));
 
     activation = RuleActivation.create(rule.getUuid(), CRITICAL, null);
     changes = activate(childProfile, activation);
-    assertThatRuleIsUpdated(childProfile, rule, CRITICAL, OVERRIDES, emptyMap());
+    assertThatRuleIsUpdated(childProfile, rule, CRITICAL, Map.of(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.HIGH), OVERRIDES, emptyMap());
     verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(childProfile.getLanguage()));
 
     changes = deactivate(parentProfile, rule);
@@ -853,8 +860,8 @@ class QProfileRuleImplIT {
 
     RuleActivation activation = RuleActivation.create(rule.getUuid());
     List<ActiveRuleChange> changes = activate(parentProfile, activation);
-    assertThatRuleIsActivated(parentProfile, rule, changes, rule.getSeverityString(), null, emptyMap());
-    assertThatRuleIsActivated(childProfile, rule, changes, rule.getSeverityString(), INHERITED, emptyMap());
+    assertThatRuleIsActivated(parentProfile, rule, changes, rule.getSeverityString(), rule.getDefaultImpactsMap(), null, emptyMap());
+    assertThatRuleIsActivated(childProfile, rule, changes, rule.getSeverityString(), rule.getDefaultImpactsMap(), INHERITED, emptyMap());
 
     changes = deactivate(childProfile, rule);
     assertThatProfileHasNoActiveRules(childProfile);
@@ -872,8 +879,8 @@ class QProfileRuleImplIT {
 
     RuleActivation activation = RuleActivation.create(rule.getUuid());
     List<ActiveRuleChange> changes = activate(parentProfile, activation);
-    assertThatRuleIsActivated(parentProfile, rule, changes, rule.getSeverityString(), null, emptyMap());
-    assertThatRuleIsActivated(childProfile, rule, changes, rule.getSeverityString(), INHERITED, emptyMap());
+    assertThatRuleIsActivated(parentProfile, rule, changes, rule.getSeverityString(), rule.getDefaultImpactsMap(), null, emptyMap());
+    assertThatRuleIsActivated(childProfile, rule, changes, rule.getSeverityString(), rule.getDefaultImpactsMap(), INHERITED, emptyMap());
 
     assertThatThrownBy(() -> deactivate(childProfile, rule))
       .isInstanceOf(BadRequestException.class)
@@ -889,19 +896,19 @@ class QProfileRuleImplIT {
 
     RuleActivation activation = RuleActivation.create(rule.getUuid(), CRITICAL, null);
     List<ActiveRuleChange> changes = activate(parentProfile, activation);
-    assertThatRuleIsActivated(parentProfile, rule, changes, CRITICAL, null, emptyMap());
-    assertThatRuleIsActivated(childProfile, rule, changes, CRITICAL, INHERITED, emptyMap());
+    assertThatRuleIsActivated(parentProfile, rule, changes, CRITICAL, Map.of(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.HIGH), null, emptyMap());
+    assertThatRuleIsActivated(childProfile, rule, changes, CRITICAL, Map.of(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.HIGH), INHERITED, emptyMap());
     assertThat(changes).hasSize(2);
 
     RuleActivation childActivation = RuleActivation.create(rule.getUuid(), BLOCKER, null);
     changes = activate(childProfile, childActivation);
-    assertThatRuleIsUpdated(childProfile, rule, BLOCKER, OVERRIDES, emptyMap());
+    assertThatRuleIsUpdated(childProfile, rule, BLOCKER, Map.of(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.BLOCKER), OVERRIDES, emptyMap());
     assertThat(changes).hasSize(1);
 
     RuleActivation resetActivation = RuleActivation.createReset(rule.getUuid());
     changes = activate(childProfile, resetActivation);
-    assertThatRuleIsUpdated(childProfile, rule, CRITICAL, INHERITED, emptyMap());
-    assertThatRuleIsUpdated(parentProfile, rule, CRITICAL, null, emptyMap());
+    assertThatRuleIsUpdated(childProfile, rule, CRITICAL, Map.of(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.HIGH), INHERITED, emptyMap());
+    assertThatRuleIsUpdated(parentProfile, rule, CRITICAL, Map.of(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.HIGH), null, emptyMap());
     assertThat(changes).hasSize(1);
 
   }
@@ -915,31 +922,31 @@ class QProfileRuleImplIT {
 
     RuleActivation activation = RuleActivation.create(rule.getUuid(), CRITICAL, null);
     List<ActiveRuleChange> changes = activate(baseProfile, activation);
-    assertThatRuleIsActivated(baseProfile, rule, changes, CRITICAL, null, emptyMap());
-    assertThatRuleIsActivated(childProfile, rule, changes, CRITICAL, INHERITED, emptyMap());
-    assertThatRuleIsActivated(grandChildProfile, rule, changes, CRITICAL, INHERITED, emptyMap());
+    assertThatRuleIsActivated(baseProfile, rule, changes, CRITICAL, Map.of(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.HIGH), null, emptyMap());
+    assertThatRuleIsActivated(childProfile, rule, changes, CRITICAL, Map.of(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.HIGH), INHERITED, emptyMap());
+    assertThatRuleIsActivated(grandChildProfile, rule, changes, CRITICAL, Map.of(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.HIGH), INHERITED, emptyMap());
     assertThat(changes).hasSize(3);
 
     RuleActivation childActivation = RuleActivation.create(rule.getUuid(), BLOCKER, null);
     changes = activate(childProfile, childActivation);
-    assertThatRuleIsUpdated(childProfile, rule, BLOCKER, OVERRIDES, emptyMap());
-    assertThatRuleIsUpdated(grandChildProfile, rule, BLOCKER, INHERITED, emptyMap());
+    assertThatRuleIsUpdated(childProfile, rule, BLOCKER, Map.of(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.BLOCKER), OVERRIDES, emptyMap());
+    assertThatRuleIsUpdated(grandChildProfile, rule, BLOCKER, Map.of(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.BLOCKER), INHERITED, emptyMap());
     assertThat(changes).hasSize(2);
 
     // Reset on parent do not change child nor grandchild
     RuleActivation resetActivation = RuleActivation.createReset(rule.getUuid());
     changes = activate(baseProfile, resetActivation);
-    assertThatRuleIsUpdated(baseProfile, rule, rule.getSeverityString(), null, emptyMap());
-    assertThatRuleIsUpdated(childProfile, rule, BLOCKER, OVERRIDES, emptyMap());
-    assertThatRuleIsUpdated(grandChildProfile, rule, BLOCKER, INHERITED, emptyMap());
+    assertThatRuleIsUpdated(baseProfile, rule, rule.getSeverityString(), rule.getDefaultImpactsMap(), null, emptyMap());
+    assertThatRuleIsUpdated(childProfile, rule, BLOCKER, Map.of(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.BLOCKER), OVERRIDES, emptyMap());
+    assertThatRuleIsUpdated(grandChildProfile, rule, BLOCKER, Map.of(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.BLOCKER), INHERITED, emptyMap());
     assertThat(changes).hasSize(1);
 
     // Reset on child change grandchild
     resetActivation = RuleActivation.createReset(rule.getUuid());
     changes = activate(childProfile, resetActivation);
-    assertThatRuleIsUpdated(baseProfile, rule, rule.getSeverityString(), null, emptyMap());
-    assertThatRuleIsUpdated(childProfile, rule, rule.getSeverityString(), INHERITED, emptyMap());
-    assertThatRuleIsUpdated(grandChildProfile, rule, rule.getSeverityString(), INHERITED, emptyMap());
+    assertThatRuleIsUpdated(baseProfile, rule, rule.getSeverityString(), rule.getDefaultImpactsMap(), null, emptyMap());
+    assertThatRuleIsUpdated(childProfile, rule, rule.getSeverityString(), rule.getDefaultImpactsMap(), INHERITED, emptyMap());
+    assertThatRuleIsUpdated(grandChildProfile, rule, rule.getSeverityString(), rule.getDefaultImpactsMap(), INHERITED, emptyMap());
     assertThat(changes).hasSize(2);
   }
 
@@ -978,7 +985,8 @@ class QProfileRuleImplIT {
     assertThat(bulkChangeResult.countSucceeded()).isEqualTo(bulkSize);
     assertThat(bulkChangeResult.getChanges()).hasSize(bulkSize);
     assertThat(db.getDbClient().activeRuleDao().selectByProfile(db.getSession(), profile)).hasSize(bulkSize);
-    rules.forEach(r -> assertThatRuleIsActivated(profile, r, null, MINOR, true, null, emptyMap()));
+    rules
+      .forEach(r -> assertThatRuleIsActivated(profile, r, null, MINOR, Map.of(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.LOW), true, null, emptyMap()));
   }
 
   @Test
@@ -1022,8 +1030,8 @@ class QProfileRuleImplIT {
     QProfileDto childProfile = createChildProfile(parentProfile);
 
     activate(parentProfile, RuleActivation.create(rule.getUuid()));
-    assertThatRuleIsActivated(parentProfile, rule, null, rule.getSeverityString(), null, emptyMap());
-    assertThatRuleIsActivated(childProfile, rule, null, rule.getSeverityString(), INHERITED, emptyMap());
+    assertThatRuleIsActivated(parentProfile, rule, null, rule.getSeverityString(), rule.getDefaultImpactsMap(), null, emptyMap());
+    assertThatRuleIsActivated(childProfile, rule, null, rule.getSeverityString(), rule.getDefaultImpactsMap(), INHERITED, emptyMap());
 
     ruleIndexer.indexAll();
 
@@ -1060,14 +1068,16 @@ class QProfileRuleImplIT {
     assertThat(result.countFailed()).isZero();
 
     // Rule1 must be activated with BLOCKER on all profiles
-    assertThatRuleIsActivated(parentProfile, rule1, null, BLOCKER, true, null, emptyMap());
-    assertThatRuleIsActivated(childProfile, rule1, null, BLOCKER, true, INHERITED, emptyMap());
-    assertThatRuleIsActivated(grandchildProfile, rule1, null, BLOCKER, true, INHERITED, emptyMap());
+    assertThatRuleIsActivated(parentProfile, rule1, null, BLOCKER, Map.of(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.BLOCKER), true, null, emptyMap());
+    assertThatRuleIsActivated(childProfile, rule1, null, BLOCKER, Map.of(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.BLOCKER), true, INHERITED,
+      emptyMap());
+    assertThatRuleIsActivated(grandchildProfile, rule1, null, BLOCKER, Map.of(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.BLOCKER), true, INHERITED,
+      emptyMap());
 
     // Rule2 did not changed
-    assertThatRuleIsActivated(parentProfile, rule2, null, rule2.getSeverityString(), null, emptyMap());
-    assertThatRuleIsActivated(childProfile, rule2, null, rule2.getSeverityString(), INHERITED, emptyMap());
-    assertThatRuleIsActivated(grandchildProfile, rule2, null, rule2.getSeverityString(), INHERITED, emptyMap());
+    assertThatRuleIsActivated(parentProfile, rule2, null, rule2.getSeverityString(), rule2.getDefaultImpactsMap(), null, emptyMap());
+    assertThatRuleIsActivated(childProfile, rule2, null, rule2.getSeverityString(), rule2.getDefaultImpactsMap(), INHERITED, emptyMap());
+    assertThatRuleIsActivated(grandchildProfile, rule2, null, rule2.getSeverityString(), rule2.getDefaultImpactsMap(), INHERITED, emptyMap());
   }
 
   @Test
@@ -1144,7 +1154,8 @@ class QProfileRuleImplIT {
   }
 
   private void assertThatRuleIsActivated(QProfileDto profile, RuleDto rule, @Nullable List<ActiveRuleChange> changes,
-    String expectedSeverity, boolean expectedPrioritizedRule, @Nullable ActiveRuleInheritance expectedInheritance,
+    String expectedSeverity, Map<SoftwareQuality, org.sonar.api.issue.impact.Severity> impacts, boolean expectedPrioritizedRule,
+    @Nullable ActiveRuleInheritance expectedInheritance,
     Map<String, String> expectedParams) {
     OrgActiveRuleDto activeRule = db.getDbClient().activeRuleDao().selectByProfile(db.getSession(), profile)
       .stream()
@@ -1153,6 +1164,7 @@ class QProfileRuleImplIT {
       .orElseThrow(IllegalStateException::new);
 
     assertThat(activeRule.getSeverityString()).isEqualTo(expectedSeverity);
+    assertThat(activeRule.getImpacts()).isEqualTo(impacts);
     assertThat(activeRule.isPrioritizedRule()).isEqualTo(expectedPrioritizedRule);
     assertThat(activeRule.getInheritance()).isEqualTo(expectedInheritance != null ? expectedInheritance.name() : null);
 
@@ -1171,8 +1183,9 @@ class QProfileRuleImplIT {
   }
 
   private void assertThatRuleIsActivated(QProfileDto profile, RuleDto rule, @Nullable List<ActiveRuleChange> changes,
-    String expectedSeverity, @Nullable ActiveRuleInheritance expectedInheritance, Map<String, String> expectedParams) {
-    assertThatRuleIsActivated(profile, rule, changes, expectedSeverity, false, expectedInheritance, expectedParams);
+    String expectedSeverity, Map<SoftwareQuality, org.sonar.api.issue.impact.Severity> expectedImpacts, @Nullable ActiveRuleInheritance expectedInheritance,
+    Map<String, String> expectedParams) {
+    assertThatRuleIsActivated(profile, rule, changes, expectedSeverity, expectedImpacts, false, expectedInheritance, expectedParams);
   }
 
   private void assertThatRuleIsNotPresent(QProfileDto profile, RuleDto rule) {
@@ -1185,7 +1198,8 @@ class QProfileRuleImplIT {
   }
 
   private void assertThatRuleIsUpdated(QProfileDto profile, RuleDto rule,
-    String expectedSeverity, @Nullable ActiveRuleInheritance expectedInheritance, Map<String, String> expectedParams) {
+    String expectedSeverity, Map<SoftwareQuality, org.sonar.api.issue.impact.Severity> expectedImpacts, @Nullable ActiveRuleInheritance expectedInheritance,
+    Map<String, String> expectedParams) {
     OrgActiveRuleDto activeRule = db.getDbClient().activeRuleDao().selectByProfile(db.getSession(), profile)
       .stream()
       .filter(ar -> ar.getRuleKey().equals(rule.getKey()))
@@ -1193,6 +1207,7 @@ class QProfileRuleImplIT {
       .orElseThrow(IllegalStateException::new);
 
     assertThat(activeRule.getSeverityString()).isEqualTo(expectedSeverity);
+    assertThat(activeRule.getImpacts()).isEqualTo(expectedImpacts);
     assertThat(activeRule.getInheritance()).isEqualTo(expectedInheritance != null ? expectedInheritance.name() : null);
 
     List<ActiveRuleParamDto> params = db.getDbClient().activeRuleDao().selectParamsByActiveRuleUuid(db.getSession(), activeRule.getUuid());
@@ -1214,7 +1229,8 @@ class QProfileRuleImplIT {
   }
 
   private RuleDto createRule() {
-    return db.rules().insert(r -> r.setSeverity(Severity.MAJOR));
+    return db.rules().insert(r -> r.setSeverity(Severity.MAJOR)
+      .replaceAllDefaultImpacts(List.of(new ImpactDto().setSoftwareQuality(SoftwareQuality.MAINTAINABILITY).setSeverity(org.sonar.api.issue.impact.Severity.MEDIUM))));
   }
 
   private RuleDto createJavaRule() {
index 2cbacb0e67c6420c99e788cb2870229a9ec5805f..9945ba828d18ce041ede68bcc69f4705e49edfaa 100644 (file)
@@ -28,10 +28,11 @@ import java.util.Random;
 import java.util.function.Consumer;
 import java.util.function.Function;
 import java.util.stream.Collectors;
-import org.junit.Rule;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
 import org.mockito.ArgumentCaptor;
 import org.sonar.api.config.Configuration;
+import org.sonar.api.issue.impact.SoftwareQuality;
 import org.sonar.api.resources.Languages;
 import org.sonar.api.rule.RuleKey;
 import org.sonar.api.rule.Severity;
@@ -39,11 +40,11 @@ import org.sonar.api.rules.RulePriority;
 import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition;
 import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition.BuiltInActiveRule;
 import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition.NewBuiltInQualityProfile;
-import org.sonar.api.testfixtures.log.LogTester;
+import org.sonar.api.testfixtures.log.LogTesterJUnit5;
 import org.sonar.api.utils.System2;
 import org.sonar.api.utils.Version;
 import org.sonar.core.platform.SonarQubeVersion;
-import org.sonar.core.util.UuidFactoryFast;
+import org.sonar.core.util.UuidFactoryImpl;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbTester;
 import org.sonar.db.qualityprofile.ActiveRuleDto;
@@ -88,38 +89,38 @@ import static org.sonar.server.qualityprofile.ActiveRuleChange.Type.ACTIVATED;
 import static org.sonar.server.qualityprofile.ActiveRuleChange.Type.DEACTIVATED;
 import static org.sonar.server.qualityprofile.ActiveRuleChange.Type.UPDATED;
 
-public class RegisterQualityProfilesNotificationIT {
+class RegisterQualityProfilesNotificationIT {
 
   private static final Random RANDOM = new SecureRandom();
 
   private System2 system2 = mock(System2.class);
-  @Rule
+  @RegisterExtension
   public DbTester db = DbTester.create(system2);
-  @Rule
+  @RegisterExtension
   public UserSessionRule userSessionRule = UserSessionRule.standalone();
-  @Rule
+  @RegisterExtension
   public BuiltInQProfileRepositoryRule builtInQProfileRepositoryRule = new BuiltInQProfileRepositoryRule();
-  @Rule
-  public LogTester logTester = new LogTester();
-
-  private DbClient dbClient = db.getDbClient();
-  private TypeValidations typeValidations = mock(TypeValidations.class);
-  private ActiveRuleIndexer activeRuleIndexer = mock(ActiveRuleIndexer.class);
-  private QualityProfileChangeEventService qualityProfileChangeEventService = mock(QualityProfileChangeEventService.class);
-  private ServerRuleFinder ruleFinder = new DefaultRuleFinder(dbClient, mock(RuleDescriptionFormatter.class));
-  private SonarQubeVersion sonarQubeVersion = new SonarQubeVersion(Version.create(10, 3));
-  private BuiltInQProfileInsert builtInQProfileInsert = new BuiltInQProfileInsertImpl(dbClient, ruleFinder, system2, UuidFactoryFast.getInstance(),
+  @RegisterExtension
+  public LogTesterJUnit5 logTester = new LogTesterJUnit5();
+
+  private final DbClient dbClient = db.getDbClient();
+  private final TypeValidations typeValidations = mock(TypeValidations.class);
+  private final ActiveRuleIndexer activeRuleIndexer = mock(ActiveRuleIndexer.class);
+  private final QualityProfileChangeEventService qualityProfileChangeEventService = mock(QualityProfileChangeEventService.class);
+  private final ServerRuleFinder ruleFinder = new DefaultRuleFinder(dbClient, mock(RuleDescriptionFormatter.class));
+  private final SonarQubeVersion sonarQubeVersion = new SonarQubeVersion(Version.create(10, 3));
+  private final BuiltInQProfileInsert builtInQProfileInsert = new BuiltInQProfileInsertImpl(dbClient, ruleFinder, system2, UuidFactoryImpl.INSTANCE,
     typeValidations, activeRuleIndexer, sonarQubeVersion);
-  private RuleActivator ruleActivator = new RuleActivator(system2, dbClient, typeValidations, userSessionRule, mock(Configuration.class), sonarQubeVersion);
-  private QProfileRules qProfileRules = new QProfileRulesImpl(dbClient, ruleActivator, mock(RuleIndex.class), activeRuleIndexer, qualityProfileChangeEventService);
-  private BuiltInQProfileUpdate builtInQProfileUpdate = new BuiltInQProfileUpdateImpl(dbClient, ruleActivator, activeRuleIndexer, qualityProfileChangeEventService);
-  private BuiltInQualityProfilesUpdateListener builtInQualityProfilesNotification = mock(BuiltInQualityProfilesUpdateListener.class);
+  private final RuleActivator ruleActivator = new RuleActivator(system2, dbClient, typeValidations, userSessionRule, mock(Configuration.class), sonarQubeVersion);
+  private final QProfileRules qProfileRules = new QProfileRulesImpl(dbClient, ruleActivator, mock(RuleIndex.class), activeRuleIndexer, qualityProfileChangeEventService);
+  private final BuiltInQProfileUpdate builtInQProfileUpdate = new BuiltInQProfileUpdateImpl(dbClient, ruleActivator, activeRuleIndexer, qualityProfileChangeEventService);
+  private final BuiltInQualityProfilesUpdateListener builtInQualityProfilesNotification = mock(BuiltInQualityProfilesUpdateListener.class);
   private final Languages languages = LanguageTesting.newLanguages();
-  private RegisterQualityProfiles underTest = new RegisterQualityProfiles(builtInQProfileRepositoryRule, dbClient,
+  private final RegisterQualityProfiles underTest = new RegisterQualityProfiles(builtInQProfileRepositoryRule, dbClient,
     builtInQProfileInsert, builtInQProfileUpdate, builtInQualityProfilesNotification, system2, languages);
 
   @Test
-  public void do_not_send_notification_on_new_profile() {
+  void do_not_send_notification_on_new_profile() {
     String language = newLanguageKey();
     builtInQProfileRepositoryRule.add(newLanguage(language), "Sonar way");
     builtInQProfileRepositoryRule.initialize();
@@ -130,11 +131,11 @@ public class RegisterQualityProfilesNotificationIT {
   }
 
   @Test
-  public void do_not_send_notification_when_profile_is_not_updated() {
+  void do_not_send_notification_when_profile_is_not_updated() {
     String language = newLanguageKey();
     RuleDto dbRule = db.rules().insert(r -> r.setLanguage(language));
     RulesProfileDto dbProfile = insertBuiltInProfile(language);
-    activateRuleInDb(dbProfile, dbRule, MAJOR);
+    activateRuleInDb(dbProfile, dbRule, MAJOR, Map.of(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.MEDIUM));
     addPluginProfile(dbProfile, dbRule);
     builtInQProfileRepositoryRule.initialize();
 
@@ -144,11 +145,11 @@ public class RegisterQualityProfilesNotificationIT {
   }
 
   @Test
-  public void send_notification_when_a_new_rule_is_activated() {
+  void send_notification_when_a_new_rule_is_activated() {
     String language = newLanguageKey();
     RuleDto existingRule = db.rules().insert(r -> r.setLanguage(language));
     RulesProfileDto dbProfile = insertBuiltInProfile(language);
-    activateRuleInDb(dbProfile, existingRule, MAJOR);
+    activateRuleInDb(dbProfile, existingRule, MAJOR, Map.of(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.MEDIUM));
     RuleDto newRule = db.rules().insert(r -> r.setLanguage(language));
     addPluginProfile(dbProfile, existingRule, newRule);
     builtInQProfileRepositoryRule.initialize();
@@ -167,11 +168,11 @@ public class RegisterQualityProfilesNotificationIT {
   }
 
   @Test
-  public void send_notification_when_a_rule_is_deactivated() {
+  void send_notification_when_a_rule_is_deactivated() {
     String language = newLanguageKey();
     RuleDto existingRule = db.rules().insert(r -> r.setLanguage(language));
     RulesProfileDto dbProfile = insertBuiltInProfile(language);
-    activateRuleInDb(dbProfile, existingRule, MAJOR);
+    activateRuleInDb(dbProfile, existingRule, MAJOR, Map.of(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.MEDIUM));
     addPluginProfile(dbProfile);
     builtInQProfileRepositoryRule.initialize();
 
@@ -189,19 +190,19 @@ public class RegisterQualityProfilesNotificationIT {
   }
 
   @Test
-  public void send_a_single_notification_when_multiple_rules_are_activated() {
+  void send_a_single_notification_when_multiple_rules_are_activated() {
     String language = newLanguageKey();
 
     RuleDto existingRule1 = db.rules().insert(r -> r.setLanguage(language));
     RuleDto newRule1 = db.rules().insert(r -> r.setLanguage(language));
     RulesProfileDto dbProfile1 = insertBuiltInProfile(language);
-    activateRuleInDb(dbProfile1, existingRule1, MAJOR);
+    activateRuleInDb(dbProfile1, existingRule1, MAJOR, Map.of(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.MEDIUM));
     addPluginProfile(dbProfile1, existingRule1, newRule1);
 
     RuleDto existingRule2 = db.rules().insert(r -> r.setLanguage(language));
     RuleDto newRule2 = db.rules().insert(r -> r.setLanguage(language));
     RulesProfileDto dbProfile2 = insertBuiltInProfile(language);
-    activateRuleInDb(dbProfile2, existingRule2, MAJOR);
+    activateRuleInDb(dbProfile2, existingRule2, MAJOR, Map.of(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.MEDIUM));
     addPluginProfile(dbProfile2, existingRule2, newRule2);
     builtInQProfileRepositoryRule.initialize();
 
@@ -223,7 +224,7 @@ public class RegisterQualityProfilesNotificationIT {
   }
 
   @Test
-  public void notification_does_not_include_inherited_profiles_when_rule_is_added() {
+  void notification_does_not_include_inherited_profiles_when_rule_is_added() {
     String language = newLanguageKey();
     RuleDto newRule = db.rules().insert(r -> r.setLanguage(language));
 
@@ -246,7 +247,7 @@ public class RegisterQualityProfilesNotificationIT {
   }
 
   @Test
-  public void notification_does_not_include_inherited_profiled_when_rule_is_changed() {
+  void notification_does_not_include_inherited_profiled_when_rule_is_changed() {
     String language = newLanguageKey();
     RuleDto rule = db.rules().insert(r -> r.setLanguage(language).setSeverity(Severity.MINOR));
 
@@ -272,7 +273,7 @@ public class RegisterQualityProfilesNotificationIT {
   }
 
   @Test
-  public void notification_does_not_include_inherited_profiles_when_rule_is_deactivated() {
+  void notification_does_not_include_inherited_profiles_when_rule_is_deactivated() {
     String language = newLanguageKey();
     RuleDto rule = db.rules().insert(r -> r.setLanguage(language).setSeverity(Severity.MINOR));
 
@@ -299,11 +300,11 @@ public class RegisterQualityProfilesNotificationIT {
   }
 
   @Test
-  public void notification_contains_send_start_and_end_date() {
+  void notification_contains_send_start_and_end_date() {
     String language = newLanguageKey();
     RuleDto existingRule = db.rules().insert(r -> r.setLanguage(language));
     RulesProfileDto dbProfile = insertBuiltInProfile(language);
-    activateRuleInDb(dbProfile, existingRule, MAJOR);
+    activateRuleInDb(dbProfile, existingRule, MAJOR, Map.of(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.MEDIUM));
     RuleDto newRule = db.rules().insert(r -> r.setLanguage(language));
     addPluginProfile(dbProfile, existingRule, newRule);
     builtInQProfileRepositoryRule.initialize();
@@ -356,10 +357,11 @@ public class RegisterQualityProfilesNotificationIT {
     return ruleProfileDto;
   }
 
-  private void activateRuleInDb(RulesProfileDto profile, RuleDto rule, RulePriority severity) {
+  private void activateRuleInDb(RulesProfileDto profile, RuleDto rule, RulePriority severity, Map<SoftwareQuality, org.sonar.api.issue.impact.Severity> impacts) {
     ActiveRuleDto dto = new ActiveRuleDto()
       .setProfileUuid(profile.getUuid())
       .setSeverity(severity.name())
+      .setImpacts(impacts)
       .setRuleUuid(rule.getUuid())
       .setCreatedAt(RANDOM.nextLong(Long.MAX_VALUE))
       .setUpdatedAt(RANDOM.nextLong(Long.MAX_VALUE));
index 6b81751a8326151a207b3d97d8ae64bba5ef53d3..885ca6c4c46d40359a00799c1ecc8c009ff4c33b 100644 (file)
@@ -25,11 +25,12 @@ import java.util.Map;
 import java.util.Optional;
 import javax.annotation.Nullable;
 import org.assertj.core.groups.Tuple;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
 import org.sonar.api.config.Configuration;
 import org.sonar.api.impl.utils.TestSystem2;
+import org.sonar.api.issue.impact.SoftwareQuality;
 import org.sonar.api.rule.RuleKey;
 import org.sonar.api.rule.Severity;
 import org.sonar.api.rules.RulePriority;
@@ -39,6 +40,7 @@ import org.sonar.api.utils.System2;
 import org.sonar.api.utils.Version;
 import org.sonar.core.platform.SonarQubeVersion;
 import org.sonar.db.DbTester;
+import org.sonar.db.issue.ImpactDto;
 import org.sonar.db.qualityprofile.ActiveRuleDto;
 import org.sonar.db.qualityprofile.ActiveRuleKey;
 import org.sonar.db.qualityprofile.ActiveRuleParamDto;
@@ -73,16 +75,16 @@ import static org.sonar.api.rules.RulePriority.MINOR;
 import static org.sonar.db.qualityprofile.QualityProfileTesting.newRuleProfileDto;
 import static org.sonar.server.qualityprofile.ActiveRuleInheritance.INHERITED;
 
-public class BuiltInQProfileUpdateImplIT {
+class BuiltInQProfileUpdateImplIT {
 
   private static final long NOW = 1_000;
   private static final long PAST = NOW - 100;
 
-  @Rule
+  @RegisterExtension
   public BuiltInQProfileRepositoryRule builtInProfileRepository = new BuiltInQProfileRepositoryRule();
-  @Rule
+  @RegisterExtension
   public DbTester db = DbTester.create();
-  @Rule
+  @RegisterExtension
   public UserSessionRule userSession = UserSessionRule.standalone();
   private System2 system2 = new TestSystem2().setNow(NOW);
   private ActiveRuleIndexer activeRuleIndexer = mock(ActiveRuleIndexer.class);
@@ -96,8 +98,8 @@ public class BuiltInQProfileUpdateImplIT {
 
   private RulesProfileDto persistedProfile;
 
-  @Before
-  public void setUp() {
+  @BeforeEach
+  void setUp() {
     persistedProfile = newRuleProfileDto(rp -> rp
       .setIsBuiltIn(true)
       .setLanguage("xoo")
@@ -107,8 +109,10 @@ public class BuiltInQProfileUpdateImplIT {
   }
 
   @Test
-  public void activate_new_rules() {
-    RuleDto rule1 = db.rules().insert(r -> r.setLanguage("xoo"));
+  void activate_new_rules() {
+    RuleDto rule1 = db.rules()
+      .insert(r -> r.setLanguage("xoo").replaceAllDefaultImpacts(List.of(newImpactDto(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.HIGH),
+        newImpactDto(SoftwareQuality.RELIABILITY, org.sonar.api.issue.impact.Severity.LOW))));
     RuleDto rule2 = db.rules().insert(r -> r.setLanguage("xoo"));
     BuiltInQualityProfilesDefinition.Context context = new BuiltInQualityProfilesDefinition.Context();
     NewBuiltInQualityProfile newQp = context.createBuiltInQualityProfile("Sonar way", "xoo");
@@ -121,34 +125,41 @@ public class BuiltInQProfileUpdateImplIT {
 
     List<ActiveRuleDto> activeRules = db.getDbClient().activeRuleDao().selectByRuleProfile(db.getSession(), persistedProfile);
     assertThat(activeRules).hasSize(2);
-    assertThatRuleIsNewlyActivated(activeRules, rule1, CRITICAL);
-    assertThatRuleIsNewlyActivated(activeRules, rule2, MAJOR);
+    assertThatRuleIsNewlyActivated(activeRules, rule1, CRITICAL, Map.of(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.HIGH,
+      SoftwareQuality.RELIABILITY, org.sonar.api.issue.impact.Severity.LOW));
+    assertThatRuleIsNewlyActivated(activeRules, rule2, MAJOR, Map.of(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.MEDIUM));
     assertThatProfileIsMarkedAsUpdated(persistedProfile);
     verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(persistedProfile.getLanguage()));
   }
 
+  private static ImpactDto newImpactDto(SoftwareQuality maintainability, org.sonar.api.issue.impact.Severity high) {
+    return new ImpactDto().setSoftwareQuality(maintainability).setSeverity(high);
+  }
+
   @Test
-  public void already_activated_rule_is_updated_in_case_of_differences() {
-    RuleDto rule = db.rules().insert(r -> r.setLanguage("xoo"));
+  void already_activated_rule_is_updated_in_case_of_differences() {
+    RuleDto rule = db.rules().insert(
+      r -> r.setLanguage("xoo")
+        .replaceAllDefaultImpacts(List.of(new ImpactDto().setSoftwareQuality(SoftwareQuality.MAINTAINABILITY).setSeverity(org.sonar.api.issue.impact.Severity.HIGH))));
     BuiltInQualityProfilesDefinition.Context context = new BuiltInQualityProfilesDefinition.Context();
     NewBuiltInQualityProfile newQp = context.createBuiltInQualityProfile("Sonar way", "xoo");
-    newQp.activateRule(rule.getRepositoryKey(), rule.getRuleKey()).overrideSeverity(Severity.CRITICAL);
+    newQp.activateRule(rule.getRepositoryKey(), rule.getRuleKey()).overrideSeverity(Severity.BLOCKER);
     newQp.done();
     BuiltInQProfile builtIn = builtInProfileRepository.create(context.profile("xoo", "Sonar way"), rule);
 
-    activateRuleInDb(persistedProfile, rule, BLOCKER);
+    activateRuleInDb(persistedProfile, rule, BLOCKER, Map.of(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.HIGH));
 
     List<ActiveRuleChange> changes = underTest.update(db.getSession(), builtIn, persistedProfile);
 
     List<ActiveRuleDto> activeRules = db.getDbClient().activeRuleDao().selectByRuleProfile(db.getSession(), persistedProfile);
     assertThat(activeRules).hasSize(1);
-    assertThatRuleIsUpdated(activeRules, rule, CRITICAL);
+    assertThatRuleIsUpdated(activeRules, rule, BLOCKER, Map.of(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.BLOCKER));
     assertThatProfileIsMarkedAsUpdated(persistedProfile);
     verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(persistedProfile.getLanguage()));
   }
 
   @Test
-  public void already_activated_rule_is_not_touched_if_no_differences() {
+  void already_activated_rule_is_not_touched_if_no_differences() {
     RuleDto rule = db.rules().insert(r -> r.setLanguage("xoo"));
     BuiltInQualityProfilesDefinition.Context context = new BuiltInQualityProfilesDefinition.Context();
     NewBuiltInQualityProfile newQp = context.createBuiltInQualityProfile("Sonar way", "xoo");
@@ -156,7 +167,7 @@ public class BuiltInQProfileUpdateImplIT {
     newQp.done();
     BuiltInQProfile builtIn = builtInProfileRepository.create(context.profile("xoo", "Sonar way"), rule);
 
-    activateRuleInDb(persistedProfile, rule, CRITICAL);
+    activateRuleInDb(persistedProfile, rule, CRITICAL, Map.of(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.HIGH));
 
     underTest.update(db.getSession(), builtIn, persistedProfile);
 
@@ -168,7 +179,7 @@ public class BuiltInQProfileUpdateImplIT {
   }
 
   @Test
-  public void deactivate_rule_that_is_not_in_built_in_definition_anymore() {
+  void deactivate_rule_that_is_not_in_built_in_definition_anymore() {
     RuleDto rule1 = db.rules().insert(r -> r.setLanguage("xoo"));
     RuleDto rule2 = db.rules().insert(r -> r.setLanguage("xoo"));
     BuiltInQualityProfilesDefinition.Context context = new BuiltInQualityProfilesDefinition.Context();
@@ -179,7 +190,7 @@ public class BuiltInQProfileUpdateImplIT {
 
     // built-in definition contains only rule2
     // so rule1 must be deactivated
-    activateRuleInDb(persistedProfile, rule1, CRITICAL);
+    activateRuleInDb(persistedProfile, rule1, CRITICAL, Map.of());
 
     List<ActiveRuleChange> changes = underTest.update(db.getSession(), builtIn, persistedProfile);
 
@@ -191,7 +202,7 @@ public class BuiltInQProfileUpdateImplIT {
   }
 
   @Test
-  public void activate_deactivate_and_update_three_rules_at_the_same_time() {
+  void activate_deactivate_and_update_three_rules_at_the_same_time() {
     RuleDto rule1 = db.rules().insert(r -> r.setLanguage("xoo"));
     RuleDto rule2 = db.rules().insert(r -> r.setLanguage("xoo"));
     RuleDto rule3 = db.rules().insert(r -> r.setLanguage("xoo"));
@@ -206,15 +217,15 @@ public class BuiltInQProfileUpdateImplIT {
     // rule1 must be updated (blocker to critical)
     // rule2 must be activated
     // rule3 must be deactivated
-    activateRuleInDb(persistedProfile, rule1, BLOCKER);
-    activateRuleInDb(persistedProfile, rule3, BLOCKER);
+    activateRuleInDb(persistedProfile, rule1, BLOCKER, Map.of(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.HIGH));
+    activateRuleInDb(persistedProfile, rule3, BLOCKER, Map.of(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.HIGH));
 
     List<ActiveRuleChange> changes = underTest.update(db.getSession(), builtIn, persistedProfile);
 
     List<ActiveRuleDto> activeRules = db.getDbClient().activeRuleDao().selectByRuleProfile(db.getSession(), persistedProfile);
     assertThat(activeRules).hasSize(2);
-    assertThatRuleIsUpdated(activeRules, rule1, CRITICAL);
-    assertThatRuleIsNewlyActivated(activeRules, rule2, MAJOR);
+    assertThatRuleIsUpdated(activeRules, rule1, CRITICAL, Map.of(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.HIGH));
+    assertThatRuleIsNewlyActivated(activeRules, rule2, MAJOR, Map.of(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.MEDIUM));
     assertThatRuleIsDeactivated(activeRules, rule3);
     assertThatProfileIsMarkedAsUpdated(persistedProfile);
     verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(persistedProfile.getLanguage()));
@@ -222,7 +233,7 @@ public class BuiltInQProfileUpdateImplIT {
 
   // SONAR-10473
   @Test
-  public void activate_rule_on_built_in_profile_resets_severity_to_default_if_not_overridden() {
+  void activate_rule_on_built_in_profile_resets_severity_to_default_if_not_overridden() {
     RuleDto rule = db.rules().insert(r -> r.setSeverity(Severity.MAJOR).setLanguage("xoo"));
 
     BuiltInQualityProfilesDefinition.Context context = new BuiltInQualityProfilesDefinition.Context();
@@ -233,20 +244,21 @@ public class BuiltInQProfileUpdateImplIT {
     underTest.update(db.getSession(), builtIn, persistedProfile);
 
     List<ActiveRuleDto> activeRules = db.getDbClient().activeRuleDao().selectByRuleProfile(db.getSession(), persistedProfile);
-    assertThatRuleIsNewlyActivated(activeRules, rule, MAJOR);
+    assertThatRuleIsNewlyActivated(activeRules, rule, MAJOR, Map.of(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.HIGH));
 
     // emulate an upgrade of analyzer that changes the default severity of the rule
     rule.setSeverity(Severity.MINOR);
+    rule.replaceAllDefaultImpacts(List.of(newImpactDto(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.LOW)));
     db.rules().update(rule);
 
     List<ActiveRuleChange> changes = underTest.update(db.getSession(), builtIn, persistedProfile);
     activeRules = db.getDbClient().activeRuleDao().selectByRuleProfile(db.getSession(), persistedProfile);
-    assertThatRuleIsNewlyActivated(activeRules, rule, MINOR);
+    assertThatRuleIsNewlyActivated(activeRules, rule, MINOR, Map.of(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.LOW));
     verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(persistedProfile.getLanguage()));
   }
 
   @Test
-  public void activate_rule_on_built_in_profile_resets_params_to_default_if_not_overridden() {
+  void activate_rule_on_built_in_profile_resets_params_to_default_if_not_overridden() {
     RuleDto rule = db.rules().insert(r -> r.setLanguage("xoo"));
     RuleParamDto ruleParam = db.rules().insertRuleParam(rule, p -> p.setName("min").setDefaultValue("10"));
 
@@ -274,7 +286,7 @@ public class BuiltInQProfileUpdateImplIT {
   }
 
   @Test
-  public void propagate_activation_to_descendant_profiles() {
+  void propagate_activation_to_descendant_profiles() {
     RuleDto rule = db.rules().insert(r -> r.setLanguage("xoo"));
 
     QProfileDto profile = db.qualityProfiles().insert(p -> p.setLanguage(rule.getLanguage()).setIsBuiltIn(true));
@@ -297,14 +309,17 @@ public class BuiltInQProfileUpdateImplIT {
 
   // SONAR-14559
   @Test
-  public void propagate_rule_update_to_descendant_active_rule() {
-    RuleDto rule = db.rules().insert(r -> r.setLanguage("xoo").setSeverity(Severity.BLOCKER));
+  void propagate_rule_update_to_descendant_active_rule() {
+    RuleDto rule = db.rules().insert(r -> r.setLanguage("xoo").setSeverity(Severity.BLOCKER)
+      .replaceAllDefaultImpacts(List.of(newImpactDto(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.BLOCKER))));
 
     QProfileDto parentProfile = db.qualityProfiles().insert(p -> p.setLanguage(rule.getLanguage()).setIsBuiltIn(true));
-    activateRuleInDb(RulesProfileDto.from(parentProfile), rule, RulePriority.valueOf(Severity.MINOR), null);
+    activateRuleInDb(RulesProfileDto.from(parentProfile), rule, RulePriority.valueOf(Severity.MINOR),
+      Map.of(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.LOW));
 
     QProfileDto childProfile = createChildProfile(parentProfile);
-    activateRuleInDb(RulesProfileDto.from(childProfile), rule, RulePriority.valueOf(Severity.MINOR), INHERITED);
+    activateRuleInDb(RulesProfileDto.from(childProfile), rule, RulePriority.valueOf(Severity.MINOR),
+      Map.of(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.LOW), INHERITED);
 
     BuiltInQualityProfilesDefinition.Context context = new BuiltInQualityProfilesDefinition.Context();
     NewBuiltInQualityProfile newQp = context.createBuiltInQualityProfile(parentProfile.getName(), parentProfile.getLanguage());
@@ -316,26 +331,26 @@ public class BuiltInQProfileUpdateImplIT {
     assertThat(changes).hasSize(2);
 
     List<ActiveRuleDto> parentActiveRules = db.getDbClient().activeRuleDao().selectByRuleProfile(db.getSession(), RulesProfileDto.from(parentProfile));
-    assertThatRuleIsUpdated(parentActiveRules, rule, RulePriority.BLOCKER, null);
+    assertThatRuleIsUpdated(parentActiveRules, rule, RulePriority.BLOCKER, Map.of(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.BLOCKER));
 
     List<ActiveRuleDto> childActiveRules = db.getDbClient().activeRuleDao().selectByRuleProfile(db.getSession(), RulesProfileDto.from(childProfile));
-    assertThatRuleIsUpdated(childActiveRules, rule, RulePriority.BLOCKER, INHERITED);
+    assertThatRuleIsUpdated(childActiveRules, rule, RulePriority.BLOCKER, Map.of(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.BLOCKER), INHERITED);
     verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(persistedProfile.getLanguage()));
   }
 
   @Test
-  public void propagate_rule_param_update_to_descendant_active_rule_params() {
+  void propagate_rule_param_update_to_descendant_active_rule_params() {
     RuleDto rule = db.rules().insert(r -> r.setLanguage("xoo").setSeverity(Severity.BLOCKER));
     RuleParamDto ruleParam = db.rules().insertRuleParam(rule, p -> p.setName("min").setDefaultValue("10"));
 
     QProfileDto parentProfile = db.qualityProfiles().insert(p -> p.setLanguage(rule.getLanguage()).setIsBuiltIn(true));
     ActiveRuleDto parentActiveRuleDto = activateRuleInDb(RulesProfileDto.from(parentProfile), rule,
-        RulePriority.valueOf(Severity.MINOR), null);
+      RulePriority.valueOf(Severity.MINOR), Map.of(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.HIGH));
     activateRuleParamInDb(parentActiveRuleDto, ruleParam, "20");
 
     QProfileDto childProfile = createChildProfile(parentProfile);
     ActiveRuleDto childActiveRuleDto = activateRuleInDb(RulesProfileDto.from(childProfile), rule,
-        RulePriority.valueOf(Severity.MINOR), INHERITED);
+      RulePriority.valueOf(Severity.MINOR), Map.of(), INHERITED);
     activateRuleParamInDb(childActiveRuleDto, ruleParam, "20");
 
     BuiltInQualityProfilesDefinition.Context context = new BuiltInQualityProfilesDefinition.Context();
@@ -353,7 +368,7 @@ public class BuiltInQProfileUpdateImplIT {
   }
 
   @Test
-  public void propagate_deactivation_to_descendant_profiles() {
+  void propagate_deactivation_to_descendant_profiles() {
     RuleDto rule = db.rules().insert(r -> r.setLanguage("xoo"));
 
     QProfileDto profile = db.qualityProfiles().insert(p -> p.setLanguage(rule.getLanguage()).setIsBuiltIn(true));
@@ -423,16 +438,19 @@ public class BuiltInQProfileUpdateImplIT {
       .containsExactlyInAnyOrder(expectedParams);
   }
 
-  private static void assertThatRuleIsNewlyActivated(List<ActiveRuleDto> activeRules, RuleDto rule, RulePriority severity) {
+  private static void assertThatRuleIsNewlyActivated(List<ActiveRuleDto> activeRules, RuleDto rule, RulePriority severity,
+    Map<SoftwareQuality, org.sonar.api.issue.impact.Severity> impacts) {
     ActiveRuleDto activeRule = findRule(activeRules, rule).get();
 
     assertThat(activeRule.getInheritance()).isNull();
     assertThat(activeRule.getSeverityString()).isEqualTo(severity.name());
+    assertThat(activeRule.getImpacts()).isEqualTo(impacts);
     assertThat(activeRule.getCreatedAt()).isEqualTo(NOW);
     assertThat(activeRule.getUpdatedAt()).isEqualTo(NOW);
   }
 
-  private static void assertThatRuleIsUpdated(List<ActiveRuleDto> activeRules, RuleDto rule, RulePriority severity, @Nullable ActiveRuleInheritance expectedInheritance) {
+  private static void assertThatRuleIsUpdated(List<ActiveRuleDto> activeRules, RuleDto rule, RulePriority severity,
+    Map<SoftwareQuality, org.sonar.api.issue.impact.Severity> impacts, @Nullable ActiveRuleInheritance expectedInheritance) {
     ActiveRuleDto activeRule = findRule(activeRules, rule).get();
 
     if (expectedInheritance != null) {
@@ -441,16 +459,14 @@ public class BuiltInQProfileUpdateImplIT {
       assertThat(activeRule.getInheritance()).isNull();
     }
     assertThat(activeRule.getSeverityString()).isEqualTo(severity.name());
+    assertThat(activeRule.getImpacts()).isEqualTo(impacts);
     assertThat(activeRule.getCreatedAt()).isEqualTo(PAST);
     assertThat(activeRule.getUpdatedAt()).isEqualTo(NOW);
   }
 
-  private static void assertThatRuleIsUpdated(List<ActiveRuleDto> activeRules, RuleDto rule, RulePriority severity) {
-    assertThatRuleIsUpdated(activeRules, rule, severity, null);
-  }
-
-  private static void assertThatRuleIsUpdated(ActiveRuleDto activeRules, RuleDto rule, RulePriority severity, @Nullable ActiveRuleInheritance expectedInheritance) {
-    assertThatRuleIsUpdated(singletonList(activeRules), rule, severity, expectedInheritance);
+  private static void assertThatRuleIsUpdated(List<ActiveRuleDto> activeRules, RuleDto rule, RulePriority severity,
+    Map<SoftwareQuality, org.sonar.api.issue.impact.Severity> impacts) {
+    assertThatRuleIsUpdated(activeRules, rule, severity, impacts, null);
   }
 
   private static void assertThatRuleIsUntouched(List<ActiveRuleDto> activeRules, RuleDto rule, RulePriority severity) {
@@ -496,15 +512,17 @@ public class BuiltInQProfileUpdateImplIT {
       .findFirst();
   }
 
-  private ActiveRuleDto activateRuleInDb(RulesProfileDto profile, RuleDto rule, RulePriority severity) {
-    return activateRuleInDb(profile, rule, severity, null);
+  private ActiveRuleDto activateRuleInDb(RulesProfileDto profile, RuleDto rule, RulePriority severity, Map<SoftwareQuality, org.sonar.api.issue.impact.Severity> impacts) {
+    return activateRuleInDb(profile, rule, severity, impacts, null);
   }
 
-  private ActiveRuleDto activateRuleInDb(RulesProfileDto ruleProfile, RuleDto rule, RulePriority severity, @Nullable ActiveRuleInheritance inheritance) {
+  private ActiveRuleDto activateRuleInDb(RulesProfileDto ruleProfile, RuleDto rule, RulePriority severity, Map<SoftwareQuality, org.sonar.api.issue.impact.Severity> impacts,
+    @Nullable ActiveRuleInheritance inheritance) {
     ActiveRuleDto dto = new ActiveRuleDto()
       .setKey(ActiveRuleKey.of(ruleProfile, RuleKey.of(rule.getRepositoryKey(), rule.getRuleKey())))
       .setProfileUuid(ruleProfile.getUuid())
       .setSeverity(severity.name())
+      .setImpacts(impacts)
       .setRuleUuid(rule.getUuid())
       .setInheritance(inheritance != null ? inheritance.name() : null)
       .setCreatedAt(PAST)
@@ -516,10 +534,10 @@ public class BuiltInQProfileUpdateImplIT {
 
   private void activateRuleParamInDb(ActiveRuleDto activeRuleDto, RuleParamDto ruleParamDto, String value) {
     ActiveRuleParamDto dto = new ActiveRuleParamDto()
-        .setActiveRuleUuid(activeRuleDto.getUuid())
-        .setRulesParameterUuid(ruleParamDto.getUuid())
-        .setKey(ruleParamDto.getName())
-        .setValue(value);
+      .setActiveRuleUuid(activeRuleDto.getUuid())
+      .setRulesParameterUuid(ruleParamDto.getUuid())
+      .setKey(ruleParamDto.getName())
+      .setValue(value);
     db.getDbClient().activeRuleDao().insertParam(db.getSession(), activeRuleDto, dto);
     db.commit();
   }
index e7184d2f52b0cf1bb5d7d94c9e151e2ed55a54bc..96bda676488d4600c2602b1033c020af321510cf 100644 (file)
 package org.sonar.server.qualityprofile.builtin;
 
 import java.util.List;
+import java.util.Map;
 import javax.annotation.Nullable;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.RegisterExtension;
 import org.sonar.api.config.Configuration;
 import org.sonar.api.impl.utils.TestSystem2;
+import org.sonar.api.issue.impact.SoftwareQuality;
 import org.sonar.api.rule.RuleKey;
 import org.sonar.api.rule.Severity;
 import org.sonar.api.rules.RulePriority;
@@ -33,6 +35,7 @@ import org.sonar.api.utils.Version;
 import org.sonar.core.platform.SonarQubeVersion;
 import org.sonar.db.DbSession;
 import org.sonar.db.DbTester;
+import org.sonar.db.issue.ImpactDto;
 import org.sonar.db.qualityprofile.ActiveRuleDto;
 import org.sonar.db.qualityprofile.ActiveRuleKey;
 import org.sonar.db.qualityprofile.ActiveRuleParamDto;
@@ -59,6 +62,9 @@ import static java.util.Map.of;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.junit.Assert.assertThrows;
 import static org.mockito.Mockito.mock;
+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;
 import static org.sonar.server.qualityprofile.ActiveRuleInheritance.INHERITED;
 import static org.sonar.server.qualityprofile.ActiveRuleInheritance.OVERRIDES;
 
@@ -90,12 +96,12 @@ class RuleActivatorIT {
 
     QProfileDto parentProfile = db.qualityProfiles().insert(p -> p.setLanguage(rule.getLanguage()).setIsBuiltIn(true));
     ActiveRuleDto parentActiveRuleDto = activateRuleInDb(RulesProfileDto.from(parentProfile), rule,
-      RulePriority.valueOf(Severity.BLOCKER), false, null);
+      RulePriority.valueOf(Severity.BLOCKER), Map.of(MAINTAINABILITY, org.sonar.api.issue.impact.Severity.HIGH), false, null);
     ActiveRuleParamDto parentActiveRuleParam = activateRuleParamInDb(parentActiveRuleDto, ruleParam, "10");
 
     QProfileDto childProfile = createChildProfile(parentProfile);
     ActiveRuleDto childActiveRuleDto = activateRuleInDb(RulesProfileDto.from(childProfile), rule,
-      RulePriority.valueOf(Severity.MINOR), true, OVERRIDES);
+      RulePriority.valueOf(Severity.MINOR), Map.of(MAINTAINABILITY, org.sonar.api.issue.impact.Severity.LOW), true, OVERRIDES);
     ActiveRuleParamDto childActiveRuleParam = activateRuleParamInDb(childActiveRuleDto, ruleParam, "15");
 
     DbSession session = db.getSession();
@@ -117,23 +123,25 @@ class RuleActivatorIT {
     ActiveRuleChange activeRuleResult = result.get(0);
     assertThat(activeRuleResult.getParameters()).containsEntry("min", "10");
     assertThat(activeRuleResult.getSeverity()).isEqualTo(Severity.BLOCKER);
+    assertThat(activeRuleResult.getImpactSeverities()).isEqualTo(Map.of(MAINTAINABILITY, org.sonar.api.issue.impact.Severity.HIGH));
     assertThat(activeRuleResult.isPrioritizedRule()).isFalse();
     assertThat(activeRuleResult.getInheritance()).isEqualTo(ActiveRuleInheritance.INHERITED);
   }
 
   @Test
   void request_new_severity_and_prioritized_rule_and_param_for_child_rule() {
-    RuleDto rule = db.rules().insert(r -> r.setLanguage("xoo").setSeverity(Severity.BLOCKER));
+    RuleDto rule = db.rules().insert(r -> r.setLanguage("xoo").setSeverity(Severity.BLOCKER)
+      .replaceAllDefaultImpacts(List.of(newImpactDto(MAINTAINABILITY, org.sonar.api.issue.impact.Severity.BLOCKER))));
     RuleParamDto ruleParam = db.rules().insertRuleParam(rule, p -> p.setName("min").setDefaultValue("10"));
 
     QProfileDto parentProfile = db.qualityProfiles().insert(p -> p.setLanguage(rule.getLanguage()).setIsBuiltIn(true));
     ActiveRuleDto parentActiveRuleDto = activateRuleInDb(RulesProfileDto.from(parentProfile), rule,
-      RulePriority.valueOf(Severity.BLOCKER), null, null);
+      RulePriority.valueOf(Severity.BLOCKER), Map.of(MAINTAINABILITY, org.sonar.api.issue.impact.Severity.BLOCKER), null, null);
     ActiveRuleParamDto parentActiveRuleParam = activateRuleParamInDb(parentActiveRuleDto, ruleParam, "10");
 
     QProfileDto childProfile = createChildProfile(parentProfile);
     ActiveRuleDto childActiveRuleDto = activateRuleInDb(RulesProfileDto.from(childProfile), rule,
-      RulePriority.valueOf(Severity.BLOCKER), null, INHERITED);
+      RulePriority.valueOf(Severity.BLOCKER), Map.of(MAINTAINABILITY, org.sonar.api.issue.impact.Severity.BLOCKER), null, INHERITED);
     ActiveRuleParamDto childActiveRuleParam = activateRuleParamInDb(childActiveRuleDto, ruleParam, "10");
 
     DbSession session = db.getSession();
@@ -155,10 +163,133 @@ class RuleActivatorIT {
     ActiveRuleChange activeRuleResult = result.get(0);
     assertThat(activeRuleResult.getParameters()).containsEntry("min", "15");
     assertThat(activeRuleResult.getSeverity()).isEqualTo(Severity.MINOR);
+    assertThat(activeRuleResult.getImpactSeverities()).containsExactlyEntriesOf(Map.of(MAINTAINABILITY, org.sonar.api.issue.impact.Severity.LOW));
     assertThat(activeRuleResult.isPrioritizedRule()).isTrue();
     assertThat(activeRuleResult.getInheritance()).isEqualTo(OVERRIDES);
   }
 
+  @Test
+  void activate_whenOnlyOneImpactAndImpactDoesntMatchRuleType_shouldOverrideSeverity() {
+    RuleDto rule = db.rules().insert(r -> r.setLanguage("xoo").setSeverity(Severity.BLOCKER)
+      .replaceAllDefaultImpacts(List.of(newImpactDto(SECURITY, org.sonar.api.issue.impact.Severity.BLOCKER))));
+    RuleParamDto ruleParam = db.rules().insertRuleParam(rule, p -> p.setName("min").setDefaultValue("10"));
+
+    QProfileDto parentProfile = db.qualityProfiles().insert(p -> p.setLanguage(rule.getLanguage()).setIsBuiltIn(true));
+    ActiveRuleDto parentActiveRuleDto = activateRuleInDb(RulesProfileDto.from(parentProfile), rule,
+      RulePriority.valueOf(Severity.BLOCKER), Map.of(SECURITY, org.sonar.api.issue.impact.Severity.BLOCKER), null, null);
+    ActiveRuleParamDto parentActiveRuleParam = activateRuleParamInDb(parentActiveRuleDto, ruleParam, "10");
+
+    QProfileDto childProfile = createChildProfile(parentProfile);
+    ActiveRuleDto childActiveRuleDto = activateRuleInDb(RulesProfileDto.from(childProfile), rule,
+      RulePriority.valueOf(Severity.BLOCKER), Map.of(SECURITY, org.sonar.api.issue.impact.Severity.BLOCKER), null, INHERITED);
+    ActiveRuleParamDto childActiveRuleParam = activateRuleParamInDb(childActiveRuleDto, ruleParam, "10");
+
+    DbSession session = db.getSession();
+    RuleActivation request = RuleActivation.createOverrideImpacts(rule.getUuid(), Map.of(SECURITY, org.sonar.api.issue.impact.Severity.LOW), of("min", "15"));
+    RuleActivationContext context = new RuleActivationContext.Builder()
+      .setProfiles(asList(parentProfile, childProfile))
+      .setBaseProfile(RulesProfileDto.from(childProfile))
+      .setDate(NOW)
+      .setDescendantProfilesSupplier((profiles, ruleUuids) -> new Result(emptyList(), emptyList(), emptyList()))
+      .setRules(singletonList(rule))
+      .setRuleParams(singletonList(ruleParam))
+      .setActiveRules(asList(parentActiveRuleDto, childActiveRuleDto))
+      .setActiveRuleParams(asList(parentActiveRuleParam, childActiveRuleParam))
+      .build();
+
+    List<ActiveRuleChange> result = underTest.activate(session, request, context);
+
+    assertThat(result).hasSize(1);
+    ActiveRuleChange activeRuleResult = result.get(0);
+    assertThat(activeRuleResult.getSeverity()).isEqualTo(Severity.MINOR);
+    assertThat(activeRuleResult.getImpactSeverities()).containsExactlyEntriesOf(Map.of(SECURITY, org.sonar.api.issue.impact.Severity.LOW));
+    assertThat(activeRuleResult.getInheritance()).isEqualTo(OVERRIDES);
+  }
+
+  @Test
+  void activate_whenTwoImpactsAndImpactsDoesntMatchRuleType_shouldNotOverrideSeverity() {
+    RuleDto rule = db.rules().insert(r -> r.setLanguage("xoo").setSeverity(Severity.BLOCKER)
+      .replaceAllDefaultImpacts(
+        List.of(newImpactDto(SECURITY, org.sonar.api.issue.impact.Severity.BLOCKER), new ImpactDto(RELIABILITY, org.sonar.api.issue.impact.Severity.BLOCKER))));
+    RuleParamDto ruleParam = db.rules().insertRuleParam(rule, p -> p.setName("min").setDefaultValue("10"));
+
+    QProfileDto parentProfile = db.qualityProfiles().insert(p -> p.setLanguage(rule.getLanguage()).setIsBuiltIn(true));
+    ActiveRuleDto parentActiveRuleDto = activateRuleInDb(RulesProfileDto.from(parentProfile), rule,
+      RulePriority.valueOf(Severity.BLOCKER), Map.of(SECURITY, org.sonar.api.issue.impact.Severity.BLOCKER, RELIABILITY, org.sonar.api.issue.impact.Severity.BLOCKER), null, null);
+    ActiveRuleParamDto parentActiveRuleParam = activateRuleParamInDb(parentActiveRuleDto, ruleParam, "10");
+
+    QProfileDto childProfile = createChildProfile(parentProfile);
+    ActiveRuleDto childActiveRuleDto = activateRuleInDb(RulesProfileDto.from(childProfile), rule,
+      RulePriority.valueOf(Severity.BLOCKER), Map.of(SECURITY, org.sonar.api.issue.impact.Severity.BLOCKER, RELIABILITY, org.sonar.api.issue.impact.Severity.BLOCKER), null,
+      INHERITED);
+    ActiveRuleParamDto childActiveRuleParam = activateRuleParamInDb(childActiveRuleDto, ruleParam, "10");
+
+    DbSession session = db.getSession();
+    RuleActivation request = RuleActivation.createOverrideImpacts(rule.getUuid(),
+      Map.of(SECURITY, org.sonar.api.issue.impact.Severity.LOW, RELIABILITY, org.sonar.api.issue.impact.Severity.LOW), of("min", "15"));
+    RuleActivationContext context = new RuleActivationContext.Builder()
+      .setProfiles(asList(parentProfile, childProfile))
+      .setBaseProfile(RulesProfileDto.from(childProfile))
+      .setDate(NOW)
+      .setDescendantProfilesSupplier((profiles, ruleUuids) -> new Result(emptyList(), emptyList(), emptyList()))
+      .setRules(singletonList(rule))
+      .setRuleParams(singletonList(ruleParam))
+      .setActiveRules(asList(parentActiveRuleDto, childActiveRuleDto))
+      .setActiveRuleParams(asList(parentActiveRuleParam, childActiveRuleParam))
+      .build();
+
+    List<ActiveRuleChange> result = underTest.activate(session, request, context);
+
+    assertThat(result).hasSize(1);
+    ActiveRuleChange activeRuleResult = result.get(0);
+    assertThat(activeRuleResult.getSeverity()).isEqualTo(Severity.BLOCKER);
+    assertThat(activeRuleResult.getImpactSeverities())
+      .containsExactlyInAnyOrderEntriesOf(Map.of(SECURITY, org.sonar.api.issue.impact.Severity.LOW, RELIABILITY, org.sonar.api.issue.impact.Severity.LOW));
+    assertThat(activeRuleResult.getInheritance()).isEqualTo(OVERRIDES);
+  }
+
+  private static ImpactDto newImpactDto(SoftwareQuality security, org.sonar.api.issue.impact.Severity severity) {
+    return new ImpactDto().setSoftwareQuality(security).setSeverity(severity);
+  }
+
+  @Test
+  void activate_whenImpactSeveritiesIsOverridden_shouldMapToRuleSeverity() {
+    RuleDto rule = db.rules().insert(r -> r.setLanguage("xoo").setSeverity(Severity.BLOCKER)
+      .replaceAllDefaultImpacts(List.of(newImpactDto(MAINTAINABILITY, org.sonar.api.issue.impact.Severity.BLOCKER))));
+    RuleParamDto ruleParam = db.rules().insertRuleParam(rule, p -> p.setName("min").setDefaultValue("10"));
+
+    QProfileDto parentProfile = db.qualityProfiles().insert(p -> p.setLanguage(rule.getLanguage()).setIsBuiltIn(true));
+    ActiveRuleDto parentActiveRuleDto = activateRuleInDb(RulesProfileDto.from(parentProfile), rule,
+      RulePriority.valueOf(Severity.BLOCKER), Map.of(MAINTAINABILITY, org.sonar.api.issue.impact.Severity.BLOCKER), null, null);
+    ActiveRuleParamDto parentActiveRuleParam = activateRuleParamInDb(parentActiveRuleDto, ruleParam, "10");
+
+    QProfileDto childProfile = createChildProfile(parentProfile);
+    ActiveRuleDto childActiveRuleDto = activateRuleInDb(RulesProfileDto.from(childProfile), rule,
+      RulePriority.valueOf(Severity.BLOCKER), Map.of(MAINTAINABILITY, org.sonar.api.issue.impact.Severity.BLOCKER), null, INHERITED);
+    ActiveRuleParamDto childActiveRuleParam = activateRuleParamInDb(childActiveRuleDto, ruleParam, "10");
+
+    DbSession session = db.getSession();
+    RuleActivation request = RuleActivation.createOverrideImpacts(rule.getUuid(), Map.of(MAINTAINABILITY, org.sonar.api.issue.impact.Severity.LOW), of("min", "15"));
+    RuleActivationContext context = new RuleActivationContext.Builder()
+      .setProfiles(asList(parentProfile, childProfile))
+      .setBaseProfile(RulesProfileDto.from(childProfile))
+      .setDate(NOW)
+      .setDescendantProfilesSupplier((profiles, ruleUuids) -> new Result(emptyList(), emptyList(), emptyList()))
+      .setRules(singletonList(rule))
+      .setRuleParams(singletonList(ruleParam))
+      .setActiveRules(asList(parentActiveRuleDto, childActiveRuleDto))
+      .setActiveRuleParams(asList(parentActiveRuleParam, childActiveRuleParam))
+      .build();
+
+    List<ActiveRuleChange> result = underTest.activate(session, request, context);
+
+    assertThat(result).hasSize(1);
+    ActiveRuleChange activeRuleResult = result.get(0);
+    assertThat(activeRuleResult.getSeverity()).isEqualTo(Severity.MINOR);
+    assertThat(activeRuleResult.getImpactSeverities()).containsExactlyEntriesOf(Map.of(MAINTAINABILITY, org.sonar.api.issue.impact.Severity.LOW));
+    assertThat(activeRuleResult.getInheritance()).isEqualTo(OVERRIDES);
+  }
+
   @Test
   void set_severity_and_param_for_child_rule_when_activating() {
     RuleDto rule = db.rules().insert(r -> r.setLanguage("xoo").setSeverity(Severity.BLOCKER));
@@ -186,6 +317,7 @@ class RuleActivatorIT {
     assertThat(result).hasSize(1);
     assertThat(result.get(0).getParameters()).containsEntry("min", "10");
     assertThat(result.get(0).getSeverity()).isEqualTo(Severity.BLOCKER);
+    assertThat(result.get(0).getImpactSeverities()).containsExactlyEntriesOf(Map.of(MAINTAINABILITY, org.sonar.api.issue.impact.Severity.HIGH));
     assertThat(result.get(0).getInheritance()).isNull();
   }
 
@@ -210,18 +342,17 @@ class RuleActivatorIT {
       .setActiveRuleParams(emptyList())
       .build();
 
-
     assertThrows("xoo rule repo:rule cannot be activated on xoo2 profile qp", BadRequestException.class,
       () -> underTest.activate(session, resetRequest, context));
   }
 
-
-  private ActiveRuleDto activateRuleInDb(RulesProfileDto ruleProfile, RuleDto rule, RulePriority severity,
+  private ActiveRuleDto activateRuleInDb(RulesProfileDto ruleProfile, RuleDto rule, RulePriority severity, Map<SoftwareQuality, org.sonar.api.issue.impact.Severity> impacts,
     @Nullable Boolean prioritizedRule, @Nullable ActiveRuleInheritance inheritance) {
     ActiveRuleDto dto = new ActiveRuleDto()
       .setKey(ActiveRuleKey.of(ruleProfile, RuleKey.of(rule.getRepositoryKey(), rule.getRuleKey())))
       .setProfileUuid(ruleProfile.getUuid())
       .setSeverity(severity.name())
+      .setImpacts(impacts)
       .setPrioritizedRule(TRUE.equals(prioritizedRule))
       .setRuleUuid(rule.getUuid())
       .setInheritance(inheritance != null ? inheritance.name() : null)
@@ -245,9 +376,9 @@ class RuleActivatorIT {
 
   private QProfileDto createChildProfile(QProfileDto parent) {
     return db.qualityProfiles().insert(p -> p
-        .setLanguage(parent.getLanguage())
-        .setParentKee(parent.getKee())
-        .setName("Child of " + parent.getName()))
+      .setLanguage(parent.getLanguage())
+      .setParentKee(parent.getKee())
+      .setName("Child of " + parent.getName()))
       .setIsBuiltIn(false);
   }
 }
index ed37db38cb9cafc7a2d17cc7a862541ae4d8d6ef..571bf550852f8a030492bbaf19fc298881d1ceb6 100644 (file)
@@ -24,6 +24,7 @@ import com.google.common.collect.ImmutableBiMap;
 import java.util.EnumMap;
 import java.util.Map;
 import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
 import org.sonar.api.issue.impact.Severity;
 import org.sonar.api.issue.impact.SoftwareQuality;
 import org.sonar.api.rules.RuleType;
@@ -46,12 +47,12 @@ public class QProfileImpactSeverityMapper {
   private QProfileImpactSeverityMapper() {
   }
 
-  public static Map<SoftwareQuality, Severity> mapImpactSeverities(String severity, Map<SoftwareQuality, Severity> ruleImpacts, RuleType ruleType) {
-    if (ruleType == RuleType.SECURITY_HOTSPOT) {
-      return Map.of();
+  public static Map<SoftwareQuality, Severity> mapImpactSeverities(@Nullable String severity, Map<SoftwareQuality, Severity> ruleImpacts, RuleType ruleType) {
+    Map<SoftwareQuality, Severity> result = ruleImpacts.isEmpty() ? Map.of() : new EnumMap<>(ruleImpacts);
+    if (severity == null || ruleImpacts.isEmpty()) {
+      return result;
     }
     SoftwareQuality softwareQuality = ImpactMapper.convertToSoftwareQuality(ruleType);
-    Map<SoftwareQuality, Severity> result = new EnumMap<>(ruleImpacts);
     if (ruleImpacts.containsKey(softwareQuality)) {
       result.put(softwareQuality, SEVERITY_MAPPING.inverse().get(severity));
     } else if (ruleImpacts.size() == 1) {
@@ -61,7 +62,7 @@ public class QProfileImpactSeverityMapper {
   }
 
   @CheckForNull
-  public static String mapSeverity(Map<SoftwareQuality, Severity> impacts, RuleType ruleType, String ruleSeverity) {
+  public static String mapSeverity(Map<SoftwareQuality, Severity> impacts, RuleType ruleType, @Nullable String ruleSeverity) {
     SoftwareQuality softwareQuality = ImpactMapper.convertToSoftwareQuality(ruleType);
     if (impacts.containsKey(softwareQuality)) {
       return SEVERITY_MAPPING.get(impacts.get(softwareQuality));
index 695fdce7070d59968b50081c56f76e8b7f758933..98d6bf350b9ef320b9f56f871b19541fcee7a514 100644 (file)
@@ -33,6 +33,8 @@ import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
 import org.apache.commons.lang3.StringUtils;
 import org.sonar.api.config.Configuration;
+import org.sonar.api.issue.impact.Severity;
+import org.sonar.api.issue.impact.SoftwareQuality;
 import org.sonar.api.rule.RuleStatus;
 import org.sonar.api.server.ServerSide;
 import org.sonar.api.server.rule.RuleParamType;
@@ -53,6 +55,7 @@ import org.sonar.db.rule.RuleDto;
 import org.sonar.db.rule.RuleParamDto;
 import org.sonar.server.qualityprofile.ActiveRuleChange;
 import org.sonar.server.qualityprofile.ActiveRuleInheritance;
+import org.sonar.server.qualityprofile.QProfileImpactSeverityMapper;
 import org.sonar.server.qualityprofile.RuleActivation;
 import org.sonar.server.qualityprofile.builtin.RuleActivationContext.ActiveRuleWrapper;
 import org.sonar.server.qualityprofile.builtin.RuleActivationContext.RuleWrapper;
@@ -155,6 +158,7 @@ public class RuleActivator {
     if (context.isCascading() && activeRule.get().getInheritance() == null) {
       // The rule is being propagated, but it was activated directly on this profile before
       change.setSeverity(activeRule.get().getSeverityString());
+      change.setImpactSeverities(activeRule.get().getImpacts());
       for (ActiveRuleParamDto activeParam : activeRule.getParams()) {
         change.setParameter(activeParam.getKey(), activeParam.getValue());
       }
@@ -213,6 +217,11 @@ public class RuleActivator {
       parentActiveRule != null ? parentActiveRule.get().getSeverityString() : null,
       rule.get().getSeverityString());
     change.setSeverity(severity);
+
+    Map<SoftwareQuality, Severity> impactsSeverities = parentActiveRule != null ? parentActiveRule.get().getImpacts()
+      : rule.get().getDefaultImpactsMap();
+    change.setImpactSeverities(impactsSeverities);
+
     change.setPrioritizedRule(parentActiveRule != null && parentActiveRule.get().isPrioritizedRule());
 
     for (RuleParamDto ruleParamDto : rule.getParams()) {
@@ -229,8 +238,9 @@ public class RuleActivator {
   private void applySeverityAndPrioritizedRuleAndParamsWhenBuiltInProfile(RuleActivation request, RuleActivationContext context,
     ActiveRuleChange change, RuleWrapper rule) {
     // for builtin quality profiles, the severity from profile, when null use the default severity of the rule
-    String severity = firstNonNull(request.getSeverity(), rule.get().getSeverityString());
-    change.setSeverity(severity);
+    SeverityConfiguration severityConfiguration = determineSeverityConfiguration(request, rule.get(), null, null);
+    change.setSeverity(severityConfiguration.severity());
+    change.setImpactSeverities(severityConfiguration.impacts());
 
     boolean prioritizedRule = TRUE.equals(request.isPrioritizedRule());
     change.setPrioritizedRule(prioritizedRule);
@@ -245,6 +255,29 @@ public class RuleActivator {
     }
   }
 
+  private static SeverityConfiguration determineSeverityConfiguration(RuleActivation request, RuleDto ruleDto, @Nullable ActiveRuleWrapper activeRule,
+    @Nullable ActiveRuleWrapper parentActiveRule) {
+    String requestSeverity = request.getSeverity();
+    if (requestSeverity != null) {
+      // When standard severity is requested to be overridden, we translate it to the impact to override
+      return new SeverityConfiguration(requestSeverity,
+        QProfileImpactSeverityMapper.mapImpactSeverities(requestSeverity, ruleDto.getDefaultImpactsMap(), ruleDto.getEnumType()));
+    } else if (!request.getImpactSeverities().isEmpty()) {
+      // If an impact is request to be overridden, we translat it to the standard severity
+      return new SeverityConfiguration(
+        QProfileImpactSeverityMapper.mapSeverity(request.getImpactSeverities(), ruleDto.getEnumType(), ruleDto.getSeverityString()),
+        request.getImpactSeverities());
+    } else if (activeRule != null && activeRule.get().doesOverride()) {
+      return new SeverityConfiguration(activeRule.get().getSeverityString(), activeRule.get().getImpacts());
+    } else if (parentActiveRule != null) {
+      return new SeverityConfiguration(parentActiveRule.get().getSeverityString(), parentActiveRule.get().getImpacts());
+    } else if (activeRule != null) {
+      return new SeverityConfiguration(activeRule.get().getSeverityString(), activeRule.get().getImpacts());
+    } else {
+      return new SeverityConfiguration(ruleDto.getSeverityString(), ruleDto.getDefaultImpactsMap());
+    }
+  }
+
   /**
    * 1. apply requested severity and param
    * 2. if rule activated and overridden - apply user value
@@ -254,9 +287,10 @@ public class RuleActivator {
   private void applySeverityAndPrioritizedRuleAndParamsWhenNonBuiltInProfile(RuleActivation request, RuleActivationContext context,
     ActiveRuleChange change,
     RuleWrapper rule, @Nullable ActiveRuleWrapper activeRule, @Nullable ActiveRuleWrapper parentActiveRule) {
-    String severity = getSeverityForNonBuiltInProfile(request, rule, activeRule, parentActiveRule);
+    SeverityConfiguration severityConfiguration = getSeverityConfigurationForNonBuiltInProfile(request, rule, activeRule, parentActiveRule);
     boolean prioritizedRule = getPrioritizedRuleForNonBuiltInProfile(request, activeRule, parentActiveRule);
-    change.setSeverity(severity);
+    change.setSeverity(severityConfiguration.severity());
+    change.setImpactSeverities(severityConfiguration.impacts());
     change.setPrioritizedRule(prioritizedRule);
 
     for (RuleParamDto ruleParamDto : rule.getParams()) {
@@ -285,25 +319,12 @@ public class RuleActivator {
     }
   }
 
-  private static String getSeverityForNonBuiltInProfile(RuleActivation request, RuleWrapper rule, @Nullable ActiveRuleWrapper activeRule,
+  private record SeverityConfiguration(@Nullable String severity, Map<SoftwareQuality, Severity> impacts) {
+  }
+
+  private static SeverityConfiguration getSeverityConfigurationForNonBuiltInProfile(RuleActivation request, RuleWrapper rule, @Nullable ActiveRuleWrapper activeRule,
     @Nullable ActiveRuleWrapper parentActiveRule) {
-    String severity;
-    if (activeRule != null) {
-      ActiveRuleDto activeRuleDto = activeRule.get();
-      // load severity from request, else keep existing one (if overridden), else from parent if rule inherited, else from default
-      severity = firstNonNull(
-        request.getSeverity(),
-        activeRuleDto.doesOverride() ? activeRuleDto.getSeverityString() : null,
-        parentActiveRule != null ? parentActiveRule.get().getSeverityString() : activeRuleDto.getSeverityString(),
-        rule.get().getSeverityString());
-    } else {
-      // load severity from request, else from parent, else from default
-      severity = firstNonNull(
-        request.getSeverity(),
-        parentActiveRule != null ? parentActiveRule.get().getSeverityString() : null,
-        rule.get().getSeverityString());
-    }
-    return severity;
+    return determineSeverityConfiguration(request, rule.get(), activeRule, parentActiveRule);
   }
 
   private static boolean getPrioritizedRuleForNonBuiltInProfile(RuleActivation request, @Nullable ActiveRuleWrapper activeRule,
@@ -356,6 +377,7 @@ public class RuleActivator {
     if (severity != null) {
       activeRule.setSeverity(severity);
     }
+    activeRule.setImpacts(change.getImpactSeverities());
     activeRule.setPrioritizedRule(TRUE.equals(change.isPrioritizedRule()));
     ActiveRuleInheritance inheritance = change.getInheritance();
     if (inheritance != null) {
@@ -388,6 +410,7 @@ public class RuleActivator {
     if (severity != null) {
       ruleDto.setSeverity(severity);
     }
+    ruleDto.setImpacts(change.getImpactSeverities());
     Boolean prioritizedRule = change.isPrioritizedRule();
     if (prioritizedRule != null) {
       ruleDto.setPrioritizedRule(prioritizedRule);
@@ -553,6 +576,10 @@ public class RuleActivator {
     if (severity != null && !severity.equals(activeRule.get().getSeverityString())) {
       return false;
     }
+    Map<SoftwareQuality, Severity> impactSeverities = change.getImpactSeverities();
+    if (!impactSeverities.equals(activeRule.get().getImpacts())) {
+      return false;
+    }
     Boolean prioritizedRule = change.isPrioritizedRule();
     if (prioritizedRule != null && prioritizedRule != activeRule.get().isPrioritizedRule()) {
       return false;
@@ -577,6 +604,9 @@ public class RuleActivator {
     if (!StringUtils.equals(change.getSeverity(), parentActiveRule.get().getSeverityString())) {
       return false;
     }
+    if (!change.getImpactSeverities().equals(parentActiveRule.get().getImpacts())) {
+      return false;
+    }
     if (change.isPrioritizedRule() != null && !Objects.equals(change.isPrioritizedRule(), parentActiveRule.get().isPrioritizedRule())) {
       return false;
     }
index a13fb9cc04fd7b18c809a4490e3cf47d1988d03f..c38b07d31f2ec1226807bf7876293f73fadbabfd 100644 (file)
@@ -37,13 +37,22 @@ class QProfileImpactSeverityMapperTest {
   @Test
   void mapImpactSeverities_whenSecurityHotspot_shouldReturnEmptyMap() {
     Map<SoftwareQuality, org.sonar.api.issue.impact.Severity> result = QProfileImpactSeverityMapper.mapImpactSeverities(Severity.MAJOR,
-      Map.of(SoftwareQuality.MAINTAINABILITY,
-        org.sonar.api.issue.impact.Severity.HIGH),
+      Map.of(),
       RuleType.SECURITY_HOTSPOT);
 
     assertThat(result).isEmpty();
   }
 
+  @Test
+  void mapImpactSeverities_whenSeverityIsNull_shouldReturnRuleImpacts() {
+    Map<SoftwareQuality, org.sonar.api.issue.impact.Severity> impacts = Map.of(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.HIGH);
+    Map<SoftwareQuality, org.sonar.api.issue.impact.Severity> result = QProfileImpactSeverityMapper.mapImpactSeverities(null,
+      impacts,
+      RuleType.SECURITY_HOTSPOT);
+
+    assertThat(result).isEqualTo(impacts);
+  }
+
   @Test
   void mapImpactSeverities_whenOneImpact_shouldReturnOverriddenImpact() {
     Map<SoftwareQuality, org.sonar.api.issue.impact.Severity> result = QProfileImpactSeverityMapper.mapImpactSeverities(Severity.INFO,