]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-20355 Enable deactivation of inherited rules
authorEric Giffon <eric.giffon@sonarsource.com>
Fri, 8 Sep 2023 10:24:21 +0000 (12:24 +0200)
committersonartech <sonartech@sonarsource.com>
Thu, 14 Sep 2023 20:02:39 +0000 (20:02 +0000)
18 files changed:
server/sonar-webserver-webapi/src/it/java/org/sonar/server/qualityprofile/QProfileComparisonIT.java
server/sonar-webserver-webapi/src/it/java/org/sonar/server/qualityprofile/QProfileResetImplIT.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/QProfileRulesImplIT.java
server/sonar-webserver-webapi/src/it/java/org/sonar/server/qualityprofile/QProfileTreeImplIT.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/it/java/org/sonar/server/qualityprofile/ws/ChangeParentActionIT.java
server/sonar-webserver-webapi/src/it/java/org/sonar/server/qualityprofile/ws/CreateActionIT.java
server/sonar-webserver-webapi/src/it/java/org/sonar/server/qualityprofile/ws/InheritanceActionIT.java
server/sonar-webserver-webapi/src/it/java/org/sonar/server/qualityprofile/ws/QProfilesWsMediumIT.java
server/sonar-webserver-webapi/src/it/java/org/sonar/server/rule/ws/SearchActionIT.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/QProfileResetImpl.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/builtin/RuleActivationContext.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/builtin/RuleActivator.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/ws/DeactivateRuleAction.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/ws/DeactivateRulesAction.java

index fedcf65c942f11e25eb20c35cc077e3b07db9cf1..39fd4f9d3c26e396662590c1da6025a985a94440 100644 (file)
@@ -26,6 +26,7 @@ import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
+import org.sonar.api.config.Configuration;
 import org.sonar.api.rule.Severity;
 import org.sonar.api.server.rule.RuleParamType;
 import org.sonar.api.utils.System2;
@@ -78,7 +79,8 @@ public class QProfileComparisonIT {
     RuleIndex ruleIndex = new RuleIndex(es.client(), System2.INSTANCE);
     ActiveRuleIndexer activeRuleIndexer = new ActiveRuleIndexer(db, es.client());
     QualityProfileChangeEventService qualityProfileChangeEventService = mock(QualityProfileChangeEventService.class);
-    RuleActivator ruleActivator = new RuleActivator(System2.INSTANCE, db, new TypeValidations(singletonList(new IntegerTypeValidation())), userSession);
+    RuleActivator ruleActivator = new RuleActivator(System2.INSTANCE, db, new TypeValidations(singletonList(new IntegerTypeValidation())),
+      userSession, mock(Configuration.class));
     qProfileRules = new QProfileRulesImpl(db, ruleActivator, ruleIndex, activeRuleIndexer, qualityProfileChangeEventService);
     comparison = new QProfileComparison(db);
 
index e26eac8c0b86d2932a5e5015025a6819efa15189..ed66fb65dfcf02b9d49990c09b6c10a57041a0b3 100644 (file)
@@ -21,6 +21,7 @@ package org.sonar.server.qualityprofile;
 
 import org.junit.Rule;
 import org.junit.Test;
+import org.sonar.api.config.Configuration;
 import org.sonar.api.impl.utils.AlwaysIncreasingSystem2;
 import org.sonar.api.utils.System2;
 import org.sonar.db.DbTester;
@@ -64,7 +65,7 @@ public class QProfileResetImplIT {
   private ActiveRuleIndexer activeRuleIndexer = mock(ActiveRuleIndexer.class);
   private QualityProfileChangeEventService qualityProfileChangeEventService = mock(QualityProfileChangeEventService.class);
   private TypeValidations typeValidations = new TypeValidations(asList(new StringTypeValidation(), new IntegerTypeValidation()));
-  private RuleActivator ruleActivator = new RuleActivator(system2, db.getDbClient(), typeValidations, userSession);
+  private RuleActivator ruleActivator = new RuleActivator(system2, db.getDbClient(), typeValidations, userSession, mock(Configuration.class));
   private QProfileTree qProfileTree = new QProfileTreeImpl(db.getDbClient(), ruleActivator, system2, activeRuleIndexer, mock(QualityProfileChangeEventService.class));
   private QProfileRules qProfileRules = new QProfileRulesImpl(db.getDbClient(), ruleActivator, null, activeRuleIndexer, qualityProfileChangeEventService);
   private QProfileResetImpl underTest = new QProfileResetImpl(db.getDbClient(), ruleActivator, activeRuleIndexer, mock(QualityProfileChangeEventService.class));
@@ -89,7 +90,7 @@ public class QProfileResetImplIT {
   }
 
   @Test
-  public void inherited_rules_are_not_disabled() {
+  public void reset_whenRuleInherited_canBeDisabled() {
     QProfileDto parentProfile = db.qualityProfiles().insert(p -> p.setLanguage(LANGUAGE));
     QProfileDto childProfile = db.qualityProfiles().insert(p -> p.setLanguage(LANGUAGE));
     qProfileTree.setParentAndCommit(db.getSession(), childProfile, parentProfile);
@@ -102,7 +103,7 @@ public class QProfileResetImplIT {
 
     assertThat(db.getDbClient().activeRuleDao().selectByProfile(db.getSession(), childProfile))
       .extracting(OrgActiveRuleDto::getRuleKey)
-      .containsExactlyInAnyOrder(newRule.getKey(), existingRule.getKey());
+      .containsExactlyInAnyOrder(newRule.getKey());
     verify(qualityProfileChangeEventService, times(2)).distributeRuleChangeEvent(any(), any(), eq(childProfile.getLanguage()));
   }
 
index b9ea7fcc91fe3350fbfc93944a1d8e8733566ee7..590db9d189ad1a4cd4a39f5789e35646cfbf361a 100644 (file)
@@ -29,11 +29,14 @@ import java.util.stream.IntStream;
 import javax.annotation.Nullable;
 import org.junit.Rule;
 import org.junit.Test;
+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.rule.RuleStatus;
 import org.sonar.api.rule.Severity;
 import org.sonar.api.utils.System2;
+import org.sonar.core.config.CorePropertyDefinitions;
 import org.sonar.db.DbTester;
 import org.sonar.db.qualityprofile.ActiveRuleParamDto;
 import org.sonar.db.qualityprofile.OrgActiveRuleDto;
@@ -74,6 +77,7 @@ import static org.sonar.api.rule.Severity.MAJOR;
 import static org.sonar.api.rule.Severity.MINOR;
 import static org.sonar.db.rule.RuleTesting.newCustomRule;
 import static org.sonar.server.qualityprofile.ActiveRuleInheritance.INHERITED;
+import static org.sonar.server.qualityprofile.ActiveRuleInheritance.OVERRIDES;
 
 public class QProfileRuleImplIT {
 
@@ -89,8 +93,9 @@ public class QProfileRuleImplIT {
   private RuleIndexer ruleIndexer = new RuleIndexer(es.client(), db.getDbClient());
   private TypeValidations typeValidations = new TypeValidations(asList(new StringTypeValidation(), new IntegerTypeValidation()));
   private QualityProfileChangeEventService qualityProfileChangeEventService = mock(QualityProfileChangeEventService.class);
+  private Configuration configuration = mock(Configuration.class);
 
-  private RuleActivator ruleActivator = new RuleActivator(system2, db.getDbClient(), typeValidations, userSession);
+  private RuleActivator ruleActivator = new RuleActivator(system2, db.getDbClient(), typeValidations, userSession, configuration);
   private QProfileRules underTest = new QProfileRulesImpl(db.getDbClient(), ruleActivator, ruleIndex, activeRuleIndexer, qualityProfileChangeEventService);
 
   @Test
@@ -482,7 +487,7 @@ public class QProfileRuleImplIT {
   }
 
   @Test
-  public void activation_on_child_profile_is_propagated_to_descendants() {
+  public void activate_shouldPropagateActivationOnChildren() {
     RuleDto rule = createRule();
     QProfileDto parentProfile = createProfile(rule);
     QProfileDto childProfile = createChildProfile(parentProfile);
@@ -495,6 +500,86 @@ public class QProfileRuleImplIT {
     verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), any(), eq(childProfile.getLanguage()));
   }
 
+  @Test
+  public void activate_whenChildProfileAlreadyActivatedRule_shouldNotStopPropagating() {
+    RuleDto rule = createRule();
+    QProfileDto parentProfile = createProfile(rule);
+    QProfileDto childProfile = createChildProfile(parentProfile);
+    QProfileDto childProfile2 = createChildProfile(childProfile);
+    QProfileDto childProfile3 = createChildProfile(childProfile2);
+
+    RuleActivation activation = RuleActivation.create(rule.getUuid(), MAJOR, emptyMap());
+
+    // Rule already active on childProfile2
+    List<ActiveRuleChange> changes = activate(childProfile2, activation);
+    assertThatRuleIsActivated(childProfile2, rule, changes, rule.getSeverityString(), 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());
+    assertThat(changes).hasSize(4);
+    verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(parentProfile.getLanguage()));
+  }
+
+  @Test
+  public void activate_whenChildAlreadyActivatedRuleWithOverriddenValues_shouldNotOverrideValues() {
+    RuleDto rule = createRule();
+    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());
+    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());
+    // 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()));
+  }
+
+  @Test
+  public void activate_whenParentHasRuleWithSameValues_shouldMarkInherited() {
+    RuleDto rule = createRule();
+    QProfileDto parentProfile = createProfile(rule);
+    QProfileDto childProfile = createChildProfile(parentProfile);
+
+    List<ActiveRuleChange> changes = activate(parentProfile, RuleActivation.create(rule.getUuid(), CRITICAL, emptyMap()));
+    assertThatRuleIsActivated(parentProfile, rule, changes, CRITICAL, null, emptyMap());
+    deactivate(childProfile, rule);
+    assertThatProfileHasNoActiveRules(childProfile);
+
+    changes = activate(childProfile, RuleActivation.create(rule.getUuid(), CRITICAL, emptyMap()));
+    assertThatRuleIsActivated(childProfile, rule, changes, CRITICAL, INHERITED, emptyMap());
+    assertThat(changes).hasSize(1);
+  }
+
+  @Test
+  public void activate_whenParentHasRuleWithDifferentValues_shouldMarkOverridden() {
+    RuleDto rule = createRule();
+    QProfileDto parentProfile = createProfile(rule);
+    QProfileDto childProfile = createChildProfile(parentProfile);
+
+    List<ActiveRuleChange> changes = activate(parentProfile, RuleActivation.create(rule.getUuid(), CRITICAL, emptyMap()));
+    assertThatRuleIsActivated(parentProfile, rule, changes, CRITICAL, null, emptyMap());
+    deactivate(childProfile, rule);
+    assertThatProfileHasNoActiveRules(childProfile);
+
+    changes = activate(childProfile, RuleActivation.create(rule.getUuid(), MAJOR, emptyMap()));
+    assertThatRuleIsActivated(childProfile, rule, changes, MAJOR, OVERRIDES, emptyMap());
+    assertThat(changes).hasSize(1);
+  }
+
   @Test
   public void update_on_child_profile_is_propagated_to_descendants() {
     RuleDto rule = createRule();
@@ -538,7 +623,7 @@ public class QProfileRuleImplIT {
 
     assertThatProfileHasNoActiveRules(parentProfile);
     assertThatRuleIsUpdated(childProfile, rule, MAJOR, null, of(param.getName(), "foo"));
-    assertThatRuleIsUpdated(grandChildProfile, rule, CRITICAL, ActiveRuleInheritance.OVERRIDES, of(param.getName(), "bar"));
+    assertThatRuleIsUpdated(grandChildProfile, rule, CRITICAL, OVERRIDES, of(param.getName(), "bar"));
     assertThat(changes).hasSize(1);
     verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(childProfile.getLanguage()));
   }
@@ -565,7 +650,7 @@ public class QProfileRuleImplIT {
 
     assertThatProfileHasNoActiveRules(parentProfile);
     assertThatRuleIsUpdated(childProfile, rule, BLOCKER, null, of(param.getName(), "baz"));
-    assertThatRuleIsUpdated(grandChildProfile, rule, CRITICAL, ActiveRuleInheritance.OVERRIDES, of(param.getName(), "bar"));
+    assertThatRuleIsUpdated(grandChildProfile, rule, CRITICAL, OVERRIDES, of(param.getName(), "bar"));
     assertThat(changes).hasSize(1);
     verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(childProfile.getLanguage()));
   }
@@ -594,13 +679,13 @@ public class QProfileRuleImplIT {
     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, ActiveRuleInheritance.OVERRIDES, of(param.getName(), "bar"));
+    assertThatRuleIsUpdated(grandChildProfile, rule, CRITICAL, OVERRIDES, of(param.getName(), "bar"));
     assertThat(changes).hasSize(2);
     verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(parentProfile.getLanguage()));
   }
 
   @Test
-  public void active_on_parent_a_rule_already_activated_on_child() {
+  public void activate_whenRuleAlreadyActiveOnChildWithDifferentValues_shouldMarkOverridden() {
     RuleDto rule = createRule();
     RuleParamDto param = db.rules().insertRuleParam(rule);
     QProfileDto parentProfile = createProfile(rule);
@@ -610,17 +695,37 @@ public class QProfileRuleImplIT {
     List<ActiveRuleChange> changes = activate(childProfile, childActivation);
     verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(childProfile.getLanguage()));
 
-    RuleActivation parentActivation = RuleActivation.create(rule.getUuid(), CRITICAL, of(param.getName(), "bar"));
+    RuleActivation parentActivation = RuleActivation.create(rule.getUuid(), MAJOR, of(param.getName(), "bar"));
     changes = activate(parentProfile, parentActivation);
 
-    assertThatRuleIsUpdated(parentProfile, rule, CRITICAL, null, of(param.getName(), "bar"));
-    assertThatRuleIsUpdated(childProfile, rule, MAJOR, ActiveRuleInheritance.OVERRIDES, of(param.getName(), "foo"));
+    assertThatRuleIsUpdated(parentProfile, rule, MAJOR, null, of(param.getName(), "bar"));
+    assertThatRuleIsUpdated(childProfile, rule, MAJOR, OVERRIDES, of(param.getName(), "foo"));
     assertThat(changes).hasSize(2);
     verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(parentProfile.getLanguage()));
   }
 
   @Test
-  public void do_not_mark_as_overridden_if_same_values_than_parent() {
+  public void activate_whenRuleAlreadyActiveOnChildWithSameValues_shouldMarkInherited() {
+    RuleDto rule = createRule();
+    RuleParamDto param = db.rules().insertRuleParam(rule);
+    QProfileDto parentProfile = createProfile(rule);
+    QProfileDto childProfile = createChildProfile(parentProfile);
+
+    RuleActivation childActivation = RuleActivation.create(rule.getUuid(), MAJOR, of(param.getName(), "foo"));
+    List<ActiveRuleChange> changes = activate(childProfile, childActivation);
+    verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(childProfile.getLanguage()));
+
+    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"));
+    assertThat(changes).hasSize(2);
+    verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(parentProfile.getLanguage()));
+  }
+
+  @Test
+  public void activate_whenSettingValuesOnChildAndParentHasSameValues_shouldMarkInherited() {
     RuleDto rule = createRule();
     RuleParamDto param = db.rules().insertRuleParam(rule);
     QProfileDto parentProfile = createProfile(rule);
@@ -639,7 +744,26 @@ public class QProfileRuleImplIT {
   }
 
   @Test
-  public void propagate_deactivation_on_children() {
+  public void activate_whenSettingValuesOnChildAndParentHasDifferentValues_shouldMarkOverridden() {
+    RuleDto rule = createRule();
+    RuleParamDto param = db.rules().insertRuleParam(rule);
+    QProfileDto parentProfile = createProfile(rule);
+    QProfileDto childProfile = createChildProfile(parentProfile);
+
+    RuleActivation parentActivation = RuleActivation.create(rule.getUuid(), MAJOR, of(param.getName(), "foo"));
+    List<ActiveRuleChange> changes = activate(parentProfile, parentActivation);
+    verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(parentProfile.getLanguage()));
+
+    RuleActivation overrideActivation = RuleActivation.create(rule.getUuid(), CRITICAL, of(param.getName(), "bar"));
+    changes = activate(childProfile, overrideActivation);
+
+    assertThatRuleIsUpdated(childProfile, rule, CRITICAL, OVERRIDES, of(param.getName(), "bar"));
+    assertThat(changes).hasSize(1);
+    verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(childProfile.getLanguage()));
+  }
+
+  @Test
+  public void deactivate_shouldPropagateDeactivationOnChildren() {
     RuleDto rule = createRule();
     QProfileDto parentProfile = createProfile(rule);
     QProfileDto childProfile = createChildProfile(parentProfile);
@@ -658,7 +782,38 @@ public class QProfileRuleImplIT {
   }
 
   @Test
-  public void propagate_deactivation_on_children_even_when_overridden() {
+  public void deactivate_whenChildAlreadyDeactivatedRule_shouldNotStopPropagating() {
+    RuleDto rule = createRule();
+    QProfileDto parentProfile = createProfile(rule);
+    QProfileDto childProfile = createChildProfile(parentProfile);
+    QProfileDto childProfile2 = createChildProfile(childProfile);
+    QProfileDto childProfile3 = createChildProfile(childProfile2);
+
+    RuleActivation activation = RuleActivation.create(rule.getUuid());
+
+    // 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());
+    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());
+
+    changes = deactivate(parentProfile, rule);
+    assertThatProfileHasNoActiveRules(parentProfile);
+    assertThatProfileHasNoActiveRules(childProfile);
+    assertThatProfileHasNoActiveRules(childProfile2);
+    assertThatProfileHasNoActiveRules(childProfile3);
+    assertThat(changes).hasSize(3);
+    verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(parentProfile.getLanguage()));
+  }
+
+  @Test
+  public void deactivate_whenChildOverridesRule_shouldPropagateDeactivation() {
     RuleDto rule = createRule();
     QProfileDto parentProfile = createProfile(rule);
     QProfileDto childProfile = createChildProfile(parentProfile);
@@ -671,6 +826,7 @@ public class QProfileRuleImplIT {
 
     activation = RuleActivation.create(rule.getUuid(), CRITICAL, null);
     changes = activate(childProfile, activation);
+    assertThatRuleIsUpdated(childProfile, rule, CRITICAL, OVERRIDES, emptyMap());
     verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(childProfile.getLanguage()));
 
     changes = deactivate(parentProfile, rule);
@@ -681,7 +837,26 @@ public class QProfileRuleImplIT {
   }
 
   @Test
-  public void cannot_deactivate_rule_inherited() {
+  public void deactivate_whenRuleInherited_canBeDeactivated() {
+    RuleDto rule = createRule();
+    QProfileDto parentProfile = createProfile(rule);
+    QProfileDto childProfile = createChildProfile(parentProfile);
+
+    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());
+
+    changes = deactivate(childProfile, rule);
+    assertThatProfileHasNoActiveRules(childProfile);
+    assertThat(changes).hasSize(1);
+    verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(parentProfile.getLanguage()));
+  }
+
+  @Test
+  public void deactivate_whenRuleInheritedAndPropertyDisabled_cannotBeDeactivated() {
+    Mockito.when(configuration.getBoolean(CorePropertyDefinitions.ALLOW_DISABLE_INHERITED_RULES)).thenReturn(Optional.of(false));
+
     RuleDto rule = createRule();
     QProfileDto parentProfile = createProfile(rule);
     QProfileDto childProfile = createChildProfile(parentProfile);
@@ -711,7 +886,7 @@ public class QProfileRuleImplIT {
 
     RuleActivation childActivation = RuleActivation.create(rule.getUuid(), BLOCKER, null);
     changes = activate(childProfile, childActivation);
-    assertThatRuleIsUpdated(childProfile, rule, BLOCKER, ActiveRuleInheritance.OVERRIDES, emptyMap());
+    assertThatRuleIsUpdated(childProfile, rule, BLOCKER, OVERRIDES, emptyMap());
     assertThat(changes).hasSize(1);
 
     RuleActivation resetActivation = RuleActivation.createReset(rule.getUuid());
@@ -738,7 +913,7 @@ public class QProfileRuleImplIT {
 
     RuleActivation childActivation = RuleActivation.create(rule.getUuid(), BLOCKER, null);
     changes = activate(childProfile, childActivation);
-    assertThatRuleIsUpdated(childProfile, rule, BLOCKER, ActiveRuleInheritance.OVERRIDES, emptyMap());
+    assertThatRuleIsUpdated(childProfile, rule, BLOCKER, OVERRIDES, emptyMap());
     assertThatRuleIsUpdated(grandChildProfile, rule, BLOCKER, INHERITED, emptyMap());
     assertThat(changes).hasSize(2);
 
@@ -746,7 +921,7 @@ public class QProfileRuleImplIT {
     RuleActivation resetActivation = RuleActivation.createReset(rule.getUuid());
     changes = activate(baseProfile, resetActivation);
     assertThatRuleIsUpdated(baseProfile, rule, rule.getSeverityString(), null, emptyMap());
-    assertThatRuleIsUpdated(childProfile, rule, BLOCKER, ActiveRuleInheritance.OVERRIDES, emptyMap());
+    assertThatRuleIsUpdated(childProfile, rule, BLOCKER, OVERRIDES, emptyMap());
     assertThatRuleIsUpdated(grandChildProfile, rule, BLOCKER, INHERITED, emptyMap());
     assertThat(changes).hasSize(1);
 
@@ -832,12 +1007,12 @@ public class QProfileRuleImplIT {
   }
 
   @Test
-  public void bulk_deactivation_ignores_errors() {
+  public void bulkDeactivateAndCommit_whenRuleInherited_canBeDeactivated() {
     RuleDto rule = createRule();
     QProfileDto parentProfile = createProfile(rule);
     QProfileDto childProfile = createChildProfile(parentProfile);
 
-    List<ActiveRuleChange> changes = activate(parentProfile, RuleActivation.create(rule.getUuid()));
+    activate(parentProfile, RuleActivation.create(rule.getUuid()));
     assertThatRuleIsActivated(parentProfile, rule, null, rule.getSeverityString(), null, emptyMap());
     assertThatRuleIsActivated(childProfile, rule, null, rule.getSeverityString(), INHERITED, emptyMap());
 
@@ -847,11 +1022,10 @@ public class QProfileRuleImplIT {
       .setQProfile(childProfile);
     BulkChangeResult bulkChangeResult = underTest.bulkDeactivateAndCommit(db.getSession(), childProfile, ruleQuery);
 
-    assertThat(bulkChangeResult.countFailed()).isOne();
-    assertThat(bulkChangeResult.countSucceeded()).isZero();
-    assertThat(bulkChangeResult.getChanges()).isEmpty();
-    assertThatRuleIsActivated(parentProfile, rule, null, rule.getSeverityString(), null, emptyMap());
-    assertThatRuleIsActivated(childProfile, rule, null, rule.getSeverityString(), INHERITED, emptyMap());
+    assertThat(bulkChangeResult.countFailed()).isZero();
+    assertThat(bulkChangeResult.countSucceeded()).isOne();
+    assertThat(bulkChangeResult.getChanges()).hasSize(1);
+    assertThatProfileHasNoActiveRules(childProfile);
   }
 
   @Test
index 6de0361ea53610a393f92535390939d12ba0eb12..8d387a53751aae54ca60d9e8245a044704adcec1 100644 (file)
@@ -23,6 +23,7 @@ import com.google.common.collect.ImmutableMap;
 import java.util.Collections;
 import org.junit.Rule;
 import org.junit.Test;
+import org.sonar.api.config.Configuration;
 import org.sonar.api.rule.Severity;
 import org.sonar.api.utils.System2;
 import org.sonar.db.DbTester;
@@ -62,7 +63,7 @@ public class QProfileRulesImplIT {
   private RuleIndex ruleIndex = new RuleIndex(es.client(), System2.INSTANCE);
   private ActiveRuleIndexer activeRuleIndexer = new ActiveRuleIndexer(db.getDbClient(), es.client());
   private RuleActivator ruleActivator = new RuleActivator(System2.INSTANCE, db.getDbClient(), new TypeValidations(singletonList(new IntegerTypeValidation())),
-    userSession);
+    userSession, mock(Configuration.class));
   private QualityProfileChangeEventService qualityProfileChangeEventService = mock(QualityProfileChangeEventService.class);
 
   private QProfileRules qProfileRules = new QProfileRulesImpl(db.getDbClient(), ruleActivator, ruleIndex, activeRuleIndexer, qualityProfileChangeEventService);
index d29c0f91b642d6b4c4cf189081aeed2d5ae5ed5f..703f5a5b3a148120a6e8ed74f87935ea90e6b90c 100644 (file)
@@ -25,6 +25,7 @@ import java.util.Optional;
 import javax.annotation.Nullable;
 import org.junit.Rule;
 import org.junit.Test;
+import org.sonar.api.config.Configuration;
 import org.sonar.api.impl.utils.AlwaysIncreasingSystem2;
 import org.sonar.api.rule.RuleStatus;
 import org.sonar.api.rule.Severity;
@@ -70,7 +71,7 @@ public class QProfileTreeImplIT {
   private ActiveRuleIndexer activeRuleIndexer = new ActiveRuleIndexer(db.getDbClient(), es.client());
   private TypeValidations typeValidations = new TypeValidations(asList(new StringTypeValidation(), new IntegerTypeValidation()));
   private QualityProfileChangeEventService qualityProfileChangeEventService = mock(QualityProfileChangeEventService.class);
-  private RuleActivator ruleActivator = new RuleActivator(system2, db.getDbClient(), typeValidations, userSession);
+  private RuleActivator ruleActivator = new RuleActivator(system2, db.getDbClient(), typeValidations, userSession, mock(Configuration.class));
   private QProfileRules qProfileRules = new QProfileRulesImpl(db.getDbClient(), ruleActivator, null, activeRuleIndexer, qualityProfileChangeEventService);
   private QProfileTree underTest = new QProfileTreeImpl(db.getDbClient(), ruleActivator, System2.INSTANCE, activeRuleIndexer, mock(QualityProfileChangeEventService.class));
 
index 0d259e67a3eb04a8e3a188c22271ed56ae9a52cd..eb47b5b99b3008c65c82262540d1b280f6631324 100644 (file)
@@ -31,6 +31,7 @@ import java.util.stream.Collectors;
 import org.junit.Rule;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
+import org.sonar.api.config.Configuration;
 import org.sonar.api.rule.RuleKey;
 import org.sonar.api.rule.Severity;
 import org.sonar.api.rules.RulePriority;
@@ -105,7 +106,7 @@ public class RegisterQualityProfilesNotificationIT {
   private ServerRuleFinder ruleFinder = new DefaultRuleFinder(dbClient, mock(RuleDescriptionFormatter.class));
   private BuiltInQProfileInsert builtInQProfileInsert = new BuiltInQProfileInsertImpl(dbClient, ruleFinder, system2, UuidFactoryFast.getInstance(),
     typeValidations, activeRuleIndexer);
-  private RuleActivator ruleActivator = new RuleActivator(system2, dbClient, typeValidations, userSessionRule);
+  private RuleActivator ruleActivator = new RuleActivator(system2, dbClient, typeValidations, userSessionRule, mock(Configuration.class));
   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);
index 71c4a03af9377bde17f40c824052cde4d0626067..0d5c11a1db464321e42e500338df943ae8cb76fd 100644 (file)
@@ -28,6 +28,7 @@ import org.assertj.core.groups.Tuple;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
+import org.sonar.api.config.Configuration;
 import org.sonar.api.impl.utils.TestSystem2;
 import org.sonar.api.rule.RuleKey;
 import org.sonar.api.rule.Severity;
@@ -35,7 +36,6 @@ import org.sonar.api.rules.RulePriority;
 import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition;
 import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition.NewBuiltInQualityProfile;
 import org.sonar.api.utils.System2;
-import org.sonar.db.DbSession;
 import org.sonar.db.DbTester;
 import org.sonar.db.qualityprofile.ActiveRuleDto;
 import org.sonar.db.qualityprofile.ActiveRuleKey;
@@ -64,7 +64,6 @@ import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoInteractions;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.sonar.api.rules.RulePriority.BLOCKER;
 import static org.sonar.api.rules.RulePriority.CRITICAL;
 import static org.sonar.api.rules.RulePriority.MAJOR;
@@ -87,7 +86,7 @@ public class BuiltInQProfileUpdateImplIT {
   private ActiveRuleIndexer activeRuleIndexer = mock(ActiveRuleIndexer.class);
   private TypeValidations typeValidations = new TypeValidations(asList(new StringTypeValidation(), new IntegerTypeValidation()));
   private QualityProfileChangeEventService qualityProfileChangeEventService = mock(QualityProfileChangeEventService.class);
-  private RuleActivator ruleActivator = new RuleActivator(system2, db.getDbClient(), typeValidations, userSession);
+  private RuleActivator ruleActivator = new RuleActivator(system2, db.getDbClient(), typeValidations, userSession, mock(Configuration.class));
 
   private BuiltInQProfileUpdateImpl underTest = new BuiltInQProfileUpdateImpl(db.getDbClient(), ruleActivator, activeRuleIndexer,
     qualityProfileChangeEventService);
@@ -350,39 +349,6 @@ public class BuiltInQProfileUpdateImplIT {
     verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(persistedProfile.getLanguage()));
   }
 
-  @Test
-  public void do_not_load_descendants_if_no_changes() {
-    RuleDto rule = db.rules().insert(r -> r.setLanguage("xoo"));
-
-    QProfileDto profile = db.qualityProfiles().insert(p -> p.setLanguage(rule.getLanguage()).setIsBuiltIn(true));
-    QProfileDto childProfile = createChildProfile(profile);
-
-    BuiltInQualityProfilesDefinition.Context context = new BuiltInQualityProfilesDefinition.Context();
-    NewBuiltInQualityProfile newQp = context.createBuiltInQualityProfile(profile.getName(), profile.getLanguage());
-    newQp.activateRule(rule.getRepositoryKey(), rule.getRuleKey());
-    newQp.done();
-
-    // first run
-    BuiltInQProfile builtIn = builtInProfileRepository.create(context.profile(profile.getLanguage(), profile.getName()), rule);
-    List<ActiveRuleChange> changes = underTest.update(db.getSession(), builtIn, RulesProfileDto.from(profile));
-    assertThat(changes).hasSize(2).extracting(ActiveRuleChange::getType).containsOnly(ActiveRuleChange.Type.ACTIVATED);
-    verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(persistedProfile.getLanguage()));
-
-    // second run, without any input changes
-    RuleActivator ruleActivatorWithoutDescendants = new RuleActivator(system2, db.getDbClient(), typeValidations, userSession) {
-      @Override
-      DescendantProfilesSupplier createDescendantProfilesSupplier(DbSession dbSession) {
-        return (profiles, ruleIds) -> {
-          throw new IllegalStateException("BOOM - descendants should not be loaded");
-        };
-      }
-    };
-    changes = new BuiltInQProfileUpdateImpl(db.getDbClient(), ruleActivatorWithoutDescendants, activeRuleIndexer, qualityProfileChangeEventService)
-      .update(db.getSession(), builtIn, RulesProfileDto.from(profile));
-    assertThat(changes).isEmpty();
-    verifyNoMoreInteractions(qualityProfileChangeEventService);
-  }
-
   @Test
   public void propagate_deactivation_to_descendant_profiles() {
     RuleDto rule = db.rules().insert(r -> r.setLanguage("xoo"));
index 208dc9ee6f9ebf355e3616797a8ba1372ca096d2..037a3985e88abc25f701d3bc4b2aed23e5f25037 100644 (file)
@@ -24,6 +24,7 @@ import java.util.List;
 import javax.annotation.Nullable;
 import org.junit.Rule;
 import org.junit.Test;
+import org.sonar.api.config.Configuration;
 import org.sonar.api.impl.utils.TestSystem2;
 import org.sonar.api.rule.RuleKey;
 import org.sonar.api.rule.Severity;
@@ -75,7 +76,7 @@ public class RuleActivatorIT {
   private final TypeValidations typeValidations = new TypeValidations(asList(new StringTypeValidation(), new IntegerTypeValidation()));
 
   private final QualityProfileChangeEventService qualityProfileChangeEventService = mock(QualityProfileChangeEventService.class);
-  private final RuleActivator underTest = new RuleActivator(system2, db.getDbClient(), typeValidations, userSession);
+  private final RuleActivator underTest = new RuleActivator(system2, db.getDbClient(), typeValidations, userSession, mock(Configuration.class));
 
   @Test
   public void reset_overridden_active_rule() {
index 145ea3bc32f578db0fdd35342b8296838284f9b8..f2bd3a0859dddbb1c204dc14c4b6585e85a6d39a 100644 (file)
@@ -24,6 +24,7 @@ import java.util.List;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
+import org.sonar.api.config.Configuration;
 import org.sonar.api.resources.Language;
 import org.sonar.api.resources.Languages;
 import org.sonar.api.rule.RuleKey;
@@ -99,7 +100,7 @@ public class ChangeParentActionIT {
     ruleIndexer = new RuleIndexer(esClient, dbClient);
     activeRuleIndexer = new ActiveRuleIndexer(dbClient, esClient);
     TypeValidations typeValidations = new TypeValidations(Collections.emptyList());
-    RuleActivator ruleActivator = new RuleActivator(System2.INSTANCE, dbClient, typeValidations, userSession);
+    RuleActivator ruleActivator = new RuleActivator(System2.INSTANCE, dbClient, typeValidations, userSession, mock(Configuration.class));
     qProfileTree = new QProfileTreeImpl(dbClient, ruleActivator, System2.INSTANCE, activeRuleIndexer, mock(QualityProfileChangeEventService.class));
     ChangeParentAction underTest = new ChangeParentAction(
       dbClient,
index 0bf5c551208261b5cafe607732bec3ba0344630d..6a7b9ee626017a0258f04bda9aece64e1d6d30d4 100644 (file)
@@ -25,6 +25,7 @@ import java.util.Collections;
 import java.util.Map;
 import org.junit.Rule;
 import org.junit.Test;
+import org.sonar.api.config.Configuration;
 import org.sonar.api.profiles.ProfileImporter;
 import org.sonar.api.profiles.RulesProfile;
 import org.sonar.api.rules.RulePriority;
@@ -89,7 +90,7 @@ public class CreateActionIT {
   private ActiveRuleIndexer activeRuleIndexer = new ActiveRuleIndexer(dbClient, es.client());
   private ProfileImporter[] profileImporters = createImporters();
   private QualityProfileChangeEventService qualityProfileChangeEventService = mock(QualityProfileChangeEventService.class);
-  private RuleActivator ruleActivator = new RuleActivator(System2.INSTANCE, dbClient, null, userSession);
+  private RuleActivator ruleActivator = new RuleActivator(System2.INSTANCE, dbClient, null, userSession, mock(Configuration.class));
   private QProfileRules qProfileRules = new QProfileRulesImpl(dbClient, ruleActivator, ruleIndex, activeRuleIndexer, qualityProfileChangeEventService);
   private QProfileExporters qProfileExporters = new QProfileExporters(dbClient, null, qProfileRules, profileImporters);
 
index ef5abc51e4b60ed7343f2fc2f77ceac2ef159c45..a722ac777428f8470347e036ac6caee540839812 100644 (file)
@@ -24,6 +24,7 @@ import java.util.ArrayList;
 import java.util.Date;
 import org.junit.Rule;
 import org.junit.Test;
+import org.sonar.api.config.Configuration;
 import org.sonar.api.resources.Languages;
 import org.sonar.api.rule.RuleKey;
 import org.sonar.api.rule.RuleStatus;
@@ -83,7 +84,7 @@ public class InheritanceActionIT {
 
   private RuleIndex ruleIndex = new RuleIndex(esClient, System2.INSTANCE);
   private QualityProfileChangeEventService qualityProfileChangeEventService = mock(QualityProfileChangeEventService.class);
-  private RuleActivator ruleActivator = new RuleActivator(System2.INSTANCE, dbClient, new TypeValidations(new ArrayList<>()), userSession);
+  private RuleActivator ruleActivator = new RuleActivator(System2.INSTANCE, dbClient, new TypeValidations(new ArrayList<>()), userSession, mock(Configuration.class));
   private QProfileRules qProfileRules = new QProfileRulesImpl(dbClient, ruleActivator, ruleIndex, activeRuleIndexer, qualityProfileChangeEventService);
   private QProfileTree qProfileTree = new QProfileTreeImpl(dbClient, ruleActivator, System2.INSTANCE, activeRuleIndexer, mock(QualityProfileChangeEventService.class));
 
index 2318e4b081900574e2a5dc70e43c8376a2f88b8d..aa004d08407493763905b863b6d7d8c23252e1f0 100644 (file)
@@ -25,6 +25,7 @@ import java.util.Optional;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
+import org.sonar.api.config.Configuration;
 import org.sonar.api.rule.RuleKey;
 import org.sonar.api.rule.RuleStatus;
 import org.sonar.api.rule.Severity;
@@ -85,7 +86,7 @@ public class QProfilesWsMediumIT {
   private final ActiveRuleIndexer activeRuleIndexer = new ActiveRuleIndexer(dbClient, es.client());
   private final TypeValidations typeValidations = new TypeValidations(emptyList());
   private final QualityProfileChangeEventService qualityProfileChangeEventService = mock(QualityProfileChangeEventService.class);
-  private final RuleActivator ruleActivator = new RuleActivator(System2.INSTANCE, dbClient, typeValidations, userSessionRule);
+  private final RuleActivator ruleActivator = new RuleActivator(System2.INSTANCE, dbClient, typeValidations, userSessionRule, mock(Configuration.class));
   private final QProfileRules qProfileRules = new QProfileRulesImpl(dbClient, ruleActivator, ruleIndex, activeRuleIndexer, qualityProfileChangeEventService);
   private final QProfileWsSupport qProfileWsSupport = new QProfileWsSupport(dbClient, userSessionRule);
   private final RuleQueryFactory ruleQueryFactory = new RuleQueryFactory(dbClient);
index b29807ccfcb64a30d5d65ddb345e2a762d1bd7c4..a53a3c95a8923f6ed62d750eab822e599b44bbb4 100644 (file)
@@ -34,6 +34,7 @@ import java.util.stream.Collectors;
 import javax.annotation.Nullable;
 import org.junit.Before;
 import org.junit.Test;
+import org.sonar.api.config.Configuration;
 import org.sonar.api.impl.utils.AlwaysIncreasingSystem2;
 import org.sonar.api.issue.impact.Severity;
 import org.sonar.api.issue.impact.SoftwareQuality;
@@ -135,7 +136,8 @@ public class SearchActionIT {
   private final SearchAction underTest = new SearchAction(ruleIndex, activeRuleCompleter, ruleQueryFactory, db.getDbClient(), ruleMapper,
     new RuleWsSupport(db.getDbClient(), userSession));
   private final TypeValidations typeValidations = new TypeValidations(asList(new StringTypeValidation(), new IntegerTypeValidation()));
-  private final RuleActivator ruleActivator = new RuleActivator(System2.INSTANCE, db.getDbClient(), typeValidations, userSession);
+  private final RuleActivator ruleActivator = new RuleActivator(System2.INSTANCE, db.getDbClient(), typeValidations, userSession,
+    mock(Configuration.class));
   private final QProfileRules qProfileRules = new QProfileRulesImpl(db.getDbClient(), ruleActivator, ruleIndex, activeRuleIndexer,
     qualityProfileChangeEventService);
   private final WsActionTester ws = new WsActionTester(underTest);
index 63a859213a9f892599023262e03265b11121e566..dbb30d3e83f24dee2c43aa5310ae31f97fdd92f8 100644 (file)
@@ -24,6 +24,7 @@ import java.util.Collection;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
+import java.util.stream.Collectors;
 import org.sonar.api.server.ServerSide;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
@@ -59,14 +60,10 @@ public class QProfileResetImpl implements QProfileReset {
     checkArgument(!profile.isBuiltIn(), "Operation forbidden for built-in Quality Profile '%s'", profile.getKee());
 
     BulkChangeResult result = new BulkChangeResult();
-    Set<String> rulesToBeDeactivated = new HashSet<>();
     // Keep reference to all the activated rules before backup restore
-    for (ActiveRuleDto activeRuleDto : db.activeRuleDao().selectByProfile(dbSession, profile)) {
-      if (activeRuleDto.getInheritance() == null) {
-        // inherited rules can't be deactivated
-        rulesToBeDeactivated.add(activeRuleDto.getRuleUuid());
-      }
-    }
+    Set<String> rulesToBeDeactivated = db.activeRuleDao().selectByProfile(dbSession, profile).stream()
+      .map(ActiveRuleDto::getRuleUuid)
+      .collect(Collectors.toSet());
     Set<String> ruleUuids = new HashSet<>(rulesToBeDeactivated.size() + activations.size());
     ruleUuids.addAll(rulesToBeDeactivated);
     activations.forEach(a -> ruleUuids.add(a.getRuleUuid()));
@@ -89,7 +86,7 @@ public class QProfileResetImpl implements QProfileReset {
       try {
         changes.addAll(activator.deactivate(dbSession, context, ruleUuid, false));
       } catch (BadRequestException e) {
-        // ignore, probably a rule inherited from parent that can't be deactivated
+        // ignore, could be a removed rule
       }
     }
     qualityProfileChangeEventService.distributeRuleChangeEvent(List.of(profile), changes, profile.getLanguage());
index 0f0728b259bc123e34c0a199ac38225c77df6dfc..488ce21d7713bdf2334d1b7d46267189011a6b93 100644 (file)
@@ -104,11 +104,15 @@ public class RuleActivationContext {
   private void register(Collection<ActiveRuleDto> activeRules, Collection<ActiveRuleParamDto> activeRuleParams) {
     ListMultimap<String, ActiveRuleParamDto> paramsByActiveRuleUuid = activeRuleParams.stream().collect(index(ActiveRuleParamDto::getActiveRuleUuid));
     for (ActiveRuleDto activeRule : activeRules) {
-      ActiveRuleWrapper wrapper = new ActiveRuleWrapper(activeRule, paramsByActiveRuleUuid.get(activeRule.getUuid()));
-      this.activeRulesByKey.put(activeRule.getKey(), wrapper);
+      register(activeRule, paramsByActiveRuleUuid.get(activeRule.getUuid()));
     }
   }
 
+  void register(ActiveRuleDto activeRule, Collection<ActiveRuleParamDto> activeRuleParams) {
+    ActiveRuleWrapper wrapper = new ActiveRuleWrapper(activeRule, activeRuleParams);
+    this.activeRulesByKey.put(activeRule.getKey(), wrapper);
+  }
+
   long getDate() {
     return date;
   }
index 3577fe7643a0993e53199e21665dcbd559b23d37..3f5d9b251aebaf4fb608f9c004356026914eb88d 100644 (file)
@@ -31,10 +31,12 @@ import java.util.stream.Stream;
 import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
 import org.apache.commons.lang.StringUtils;
+import org.sonar.api.config.Configuration;
 import org.sonar.api.rule.RuleStatus;
 import org.sonar.api.server.ServerSide;
 import org.sonar.api.server.rule.RuleParamType;
 import org.sonar.api.utils.System2;
+import org.sonar.core.config.CorePropertyDefinitions;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
 import org.sonar.db.qualityprofile.ActiveRuleDao;
@@ -67,15 +69,16 @@ public class RuleActivator {
   private final DbClient db;
   private final TypeValidations typeValidations;
   private final UserSession userSession;
+  private final Configuration configuration;
 
-  public RuleActivator(System2 system2, DbClient db, TypeValidations typeValidations, UserSession userSession) {
+  public RuleActivator(System2 system2, DbClient db, TypeValidations typeValidations, UserSession userSession, Configuration configuration) {
     this.system2 = system2;
     this.db = db;
     this.typeValidations = typeValidations;
     this.userSession = userSession;
+    this.configuration = configuration;
   }
 
-
   public List<ActiveRuleChange> activate(DbSession dbSession, Collection<RuleActivation> activations, RuleActivationContext context) {
     return activations.stream().map(a -> activate(dbSession, a, context))
       .flatMap(List::stream)
@@ -92,10 +95,10 @@ public class RuleActivator {
     checkRequest(RuleStatus.REMOVED != rule.getStatus(), "Rule was removed: %s", rule.getKey());
     checkRequest(!rule.isTemplate(), "Rule template can't be activated on a Quality profile: %s", rule.getKey());
     checkRequest(context.getRulesProfile().getLanguage().equals(rule.getLanguage()),
-      "%s rule %s cannot be activated on %s profile %s", rule.getLanguage(), rule.getKey(), context.getRulesProfile().getLanguage(), context.getRulesProfile().getName());
+      "%s rule %s cannot be activated on %s profile %s", rule.getLanguage(), rule.getKey(), context.getRulesProfile().getLanguage(),
+      context.getRulesProfile().getName());
     List<ActiveRuleChange> changes = new ArrayList<>();
-    ActiveRuleChange change;
-    boolean stopCascading = false;
+    ActiveRuleChange change = null;
 
     ActiveRuleWrapper activeRule = context.getActiveRule();
     ActiveRuleKey activeRuleKey = ActiveRuleKey.of(context.getRulesProfile(), rule.getKey());
@@ -107,16 +110,17 @@ public class RuleActivator {
       change = handleNewRuleActivation(activation, context, rule, activeRuleKey);
     } else {
       // already activated
-      if (context.isCascading() && activeRule.get().doesOverride()) {
-        // propagating to descendants, but child profile already overrides rule -> stop propagation
-        return changes;
-      }
-      change = new ActiveRuleChange(ActiveRuleChange.Type.UPDATED, activeRuleKey, rule);
-      stopCascading = handleUpdatedRuleActivation(activation, context, change, stopCascading, activeRule);
 
-      if (isSame(change, activeRule)) {
-        change = null;
-        stopCascading = true;
+      // No change if propagating to descendants, but child profile already overrides rule
+      if (!context.isCascading() || !activeRule.get().doesOverride()) {
+        change = new ActiveRuleChange(ActiveRuleChange.Type.UPDATED, activeRuleKey, rule);
+        handleUpdatedRuleActivation(activation, context, change, activeRule);
+
+        if (isSame(change, activeRule) || (context.isCascading() && activeRule.get().getInheritance() != null && !isSameAsParent(change, context))) {
+          // The rule config hasn't changed; or the rule is being propagated but the parent has a different config,
+          // which means the rule was overridden by a profile in the inheritance chain
+          change = null;
+        }
       }
     }
 
@@ -129,23 +133,20 @@ public class RuleActivator {
       updateProfileDates(dbSession, context);
     }
 
-    if (!stopCascading) {
-      changes.addAll(propagateActivationToDescendants(dbSession, activation, context));
-    }
+    changes.addAll(propagateActivationToDescendants(dbSession, activation, context));
 
     return changes;
   }
 
-  private boolean handleUpdatedRuleActivation(RuleActivation activation, RuleActivationContext context, ActiveRuleChange change,
-    boolean stopCascading, ActiveRuleWrapper activeRule) {
+  private void handleUpdatedRuleActivation(RuleActivation activation, RuleActivationContext context, ActiveRuleChange change,
+                                           ActiveRuleWrapper activeRule) {
     if (context.isCascading() && activeRule.get().getInheritance() == null) {
-      // activate on child, then on parent -> mark child as overriding parent
-      change.setInheritance(ActiveRuleInheritance.OVERRIDES);
+      // The rule is being propagated, but it was activated directly on this profile before
       change.setSeverity(activeRule.get().getSeverityString());
       for (ActiveRuleParamDto activeParam : activeRule.getParams()) {
         change.setParameter(activeParam.getKey(), activeParam.getValue());
       }
-      stopCascading = true;
+      change.setInheritance(isSameAsParent(change, context) ? ActiveRuleInheritance.INHERITED : ActiveRuleInheritance.OVERRIDES);
     } else {
       applySeverityAndParamToChange(activation, context, change);
       if (!context.isCascading() && context.getParentActiveRule() != null) {
@@ -153,14 +154,13 @@ public class RuleActivator {
         change.setInheritance(isSameAsParent(change, context) ? ActiveRuleInheritance.INHERITED : ActiveRuleInheritance.OVERRIDES);
       }
     }
-    return stopCascading;
   }
 
   private ActiveRuleChange handleNewRuleActivation(RuleActivation activation, RuleActivationContext context, RuleDto rule, ActiveRuleKey activeRuleKey) {
     ActiveRuleChange change = new ActiveRuleChange(ActiveRuleChange.Type.ACTIVATED, activeRuleKey, rule);
     applySeverityAndParamToChange(activation, context, change);
-    if (context.isCascading() || isSameAsParent(change, context)) {
-      change.setInheritance(ActiveRuleInheritance.INHERITED);
+    if (context.isCascading() || context.getParentActiveRule() != null) {
+      change.setInheritance(isSameAsParent(change, context) ? ActiveRuleInheritance.INHERITED : ActiveRuleInheritance.OVERRIDES);
     }
     return change;
   }
@@ -328,13 +328,16 @@ public class RuleActivator {
     activeRule.setUpdatedAt(system2.now());
     activeRule.setCreatedAt(system2.now());
     dao.insert(dbSession, activeRule);
+    List<ActiveRuleParamDto> params = new ArrayList<>();
     for (Map.Entry<String, String> param : change.getParameters().entrySet()) {
       if (param.getValue() != null) {
         ActiveRuleParamDto paramDto = ActiveRuleParamDto.createFor(rule.getParam(param.getKey()));
         paramDto.setValue(param.getValue());
+        params.add(paramDto);
         dao.insertParam(dbSession, activeRule, paramDto);
       }
     }
+    context.register(activeRule, params);
     return activeRule;
   }
 
@@ -384,15 +387,14 @@ public class RuleActivator {
   private List<ActiveRuleChange> doDeactivate(DbSession dbSession, RuleActivationContext context, boolean force) {
     List<ActiveRuleChange> changes = new ArrayList<>();
     ActiveRuleWrapper activeRule = context.getActiveRule();
-    if (activeRule == null) {
-      return changes;
-    }
+    if (activeRule != null) {
+      checkRequest(force || context.isCascading() || activeRule.get().getInheritance() == null || isAllowDisableInheritedRules(),
+        "Cannot deactivate inherited rule '%s'", context.getRule().get().getKey());
 
-    ActiveRuleChange change;
-    checkRequest(force || context.isCascading() || activeRule.get().getInheritance() == null, "Cannot deactivate inherited rule '%s'", context.getRule().get().getKey());
-    change = new ActiveRuleChange(ActiveRuleChange.Type.DEACTIVATED, activeRule.get(), context.getRule().get());
-    changes.add(change);
-    persist(change, context, dbSession);
+      ActiveRuleChange change = new ActiveRuleChange(ActiveRuleChange.Type.DEACTIVATED, activeRule.get(), context.getRule().get());
+      changes.add(change);
+      persist(change, context, dbSession);
+    }
 
     // get all inherited profiles (they are not built-in by design)
     context.getChildProfiles().forEach(child -> {
@@ -407,6 +409,10 @@ public class RuleActivator {
     return changes;
   }
 
+  private boolean isAllowDisableInheritedRules() {
+    return configuration.getBoolean(CorePropertyDefinitions.ALLOW_DISABLE_INHERITED_RULES).orElse(true);
+  }
+
   @CheckForNull
   private String validateParam(RuleParamDto ruleParam, @Nullable String value) {
     if (value != null) {
@@ -516,9 +522,6 @@ public class RuleActivator {
     return true;
   }
 
-  /**
-   * True if trying to override an inherited rule but with exactly the same values
-   */
   private static boolean isSameAsParent(ActiveRuleChange change, RuleActivationContext context) {
     ActiveRuleWrapper parentActiveRule = context.getParentActiveRule();
     if (parentActiveRule == null) {
index 95383c73c213107a00cd59a7a7194ab23fdc3d78..aef74f0f0b29f9694dbf2be982e19ea611659e57 100644 (file)
@@ -20,6 +20,7 @@
 package org.sonar.server.qualityprofile.ws;
 
 import org.sonar.api.rule.RuleKey;
+import org.sonar.api.server.ws.Change;
 import org.sonar.api.server.ws.Request;
 import org.sonar.api.server.ws.Response;
 import org.sonar.api.server.ws.WebService;
@@ -61,7 +62,8 @@ public class DeactivateRuleAction implements QProfileWsAction {
         "</ul>")
       .setHandler(this)
       .setPost(true)
-      .setSince("4.4");
+      .setSince("4.4")
+      .setChangelog(new Change("10.3", "Inherited rules can be deactivated (if the global admin setting is enabled)"));
 
     deactivate.createParam(PARAM_KEY)
       .setDescription("Quality Profile key. Can be obtained through <code>api/qualityprofiles/search</code>")
index 04dfb30bc556d3fb99adabf67349c1125f471d6f..3f0073ed41ffd382912cc6c8b35234bfa5f9608b 100644 (file)
@@ -70,6 +70,7 @@ public class DeactivateRulesAction implements QProfileWsAction {
       .setPost(true)
       .setSince("4.4")
       .setChangelog(
+        new Change("10.3", "Inherited rules can be deactivated (if the global admin setting is enabled)"),
         new Change("10.2", format("Parameters '%s', '%s', and '%s' are now deprecated.", PARAM_SEVERITIES, PARAM_ACTIVE_SEVERITIES, PARAM_TYPES)),
         new Change("10.0", "Parameter 'sansTop25' is deprecated"))
       .setHandler(this);