import org.sonar.api.utils.log.Loggers;
import org.sonar.api.utils.log.Profiler;
import org.sonar.core.util.UuidFactory;
-import org.sonar.core.util.stream.MoreCollectors;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.qualityprofile.ActiveRuleDto;
// FIXME lack of resiliency, active rules index is corrupted if rule index fails
// to be updated. Only a single DB commit should be executed.
ruleIndexer.commitAndIndex(dbSession, registerRulesContext.getAllModified().map(RuleDto::getUuid).collect(toSet()));
+ changes.forEach(arChange -> dbClient.qProfileChangeDao().insert(dbSession, arChange.toDto(null)));
activeRuleIndexer.commitAndIndex(dbSession, changes);
registerRulesContext.getRenamed().forEach(e -> LOG.info("Rule {} re-keyed to {}", e.getValue(), e.getKey().getKey()));
profiler.stopDebug();
* If an extended repository do not exists anymore, then related active rules will be removed.
*/
private List<ActiveRuleChange> removeActiveRulesOnStillExistingRepositories(DbSession dbSession, RegisterRulesContext recorder, List<RulesDefinition.Repository> context) {
- List<String> repositoryKeys = context.stream()
- .map(RulesDefinition.ExtendedRepository::key)
- .collect(MoreCollectors.toList(context.size()));
-
+ Set<String> existingAndRenamedRepositories = getExistingAndRenamedRepositories(recorder, context);
List<ActiveRuleChange> changes = new ArrayList<>();
Profiler profiler = Profiler.create(Loggers.get(getClass()));
- recorder.getRemoved().forEach(rule -> {
- // SONAR-4642 Remove active rules only when repository still exists
- if (repositoryKeys.contains(rule.getRepositoryKey())) {
+
+ recorder.getRemoved()
+ .filter(rule -> existingAndRenamedRepositories.contains(rule.getRepositoryKey()))
+ .forEach(rule -> {
+ // SONAR-4642 Remove active rules only when repository still exists
profiler.start();
changes.addAll(qProfileRules.deleteRule(dbSession, rule));
profiler.stopDebug(format("Remove active rule for rule %s", rule.getKey()));
- }
- });
+ });
+
return changes;
}
+ private Set<String> getExistingAndRenamedRepositories(RegisterRulesContext recorder, Collection<RulesDefinition.Repository> context) {
+ return Stream.concat(
+ context.stream().map(RulesDefinition.ExtendedRepository::key),
+ recorder.getRenamed().map(Map.Entry::getValue).map(RuleKey::repository))
+ .collect(toSet());
+ }
+
private void update(DbSession session, RuleDto rule) {
rule.setUpdatedAt(system2.now());
dbClient.ruleDao().update(session, rule);
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.DbTester;
+import org.sonar.db.qualityprofile.ActiveRuleDto;
+import org.sonar.db.qualityprofile.QProfileChangeDto;
+import org.sonar.db.qualityprofile.QProfileChangeQuery;
+import org.sonar.db.qualityprofile.QProfileDto;
import org.sonar.db.rule.DeprecatedRuleKeyDto;
import org.sonar.db.rule.RuleDescriptionSectionDto;
import org.sonar.db.rule.RuleDto;
import org.sonar.server.es.SearchOptions;
import org.sonar.server.es.metadata.MetadataIndex;
import org.sonar.server.plugins.ServerPluginRepository;
+import org.sonar.server.qualityprofile.ActiveRuleChange;
import org.sonar.server.qualityprofile.QProfileRules;
import org.sonar.server.qualityprofile.index.ActiveRuleIndexer;
import org.sonar.server.rule.index.RuleIndex;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.assertj.core.api.Assertions.tuple;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.sonar.db.rule.RuleDescriptionSectionDto.DEFAULT_KEY;
import static org.sonar.db.rule.RuleDescriptionSectionDto.builder;
import static org.sonar.db.rule.RuleDescriptionSectionDto.createDefaultRuleDescriptionSection;
+import static org.sonar.server.qualityprofile.ActiveRuleChange.Type.DEACTIVATED;
@RunWith(DataProviderRunner.class)
public class RegisterRulesTest {
private RuleIndexer ruleIndexer;
private ActiveRuleIndexer activeRuleIndexer;
private RuleIndex ruleIndex;
- private RuleDescriptionSectionsGenerator ruleDescriptionSectionsGenerator = mock(RuleDescriptionSectionsGenerator.class);
- private RuleDescriptionSectionsGeneratorResolver resolver = mock(RuleDescriptionSectionsGeneratorResolver.class);
+ private final RuleDescriptionSectionsGenerator ruleDescriptionSectionsGenerator = mock(RuleDescriptionSectionsGenerator.class);
+ private final RuleDescriptionSectionsGeneratorResolver resolver = mock(RuleDescriptionSectionsGeneratorResolver.class);
@Before
public void before() {
.hasMessage("The rule 'newKey1' of repository 'fake' is declared several times");
}
+ @Test
+ public void removed_rule_should_appear_in_changelog() {
+ //GIVEN
+ QProfileDto qProfileDto = db.qualityProfiles().insert();
+ RuleDto ruleDto = db.rules().insert(RULE_KEY1);
+ db.qualityProfiles().activateRule(qProfileDto, ruleDto);
+ ActiveRuleChange arChange = new ActiveRuleChange(DEACTIVATED, ActiveRuleDto.createFor(qProfileDto, ruleDto), ruleDto);
+ when(qProfileRules.deleteRule(any(DbSession.class), eq(ruleDto))).thenReturn(List.of(arChange));
+ //WHEN
+ execute(context -> context.createRepository("fake", "java").done());
+ //THEN
+ List<QProfileChangeDto> qProfileChangeDtos = dbClient.qProfileChangeDao().selectByQuery(db.getSession(), new QProfileChangeQuery(qProfileDto.getKee()));
+ assertThat(qProfileChangeDtos).extracting(QProfileChangeDto::getRulesProfileUuid, QProfileChangeDto::getChangeType)
+ .contains(tuple(qProfileDto.getRulesProfileUuid(), "DEACTIVATED"));
+ }
+
+ @Test
+ public void removed_rule_should_be_deleted_when_renamed_repository() {
+ //GIVEN
+ RuleDto removedRuleDto = db.rules().insert(RuleKey.of("old_repo", "removed_rule"));
+ RuleDto renamedRuleDto = db.rules().insert(RuleKey.of("old_repo", "renamed_rule"));
+ //WHEN
+ execute(context -> createRule(context, "java", "new_repo", renamedRuleDto.getRuleKey(),
+ rule -> rule.addDeprecatedRuleKey(renamedRuleDto.getRepositoryKey(), renamedRuleDto.getRuleKey())));
+ //THEN
+ verify(qProfileRules).deleteRule(any(DbSession.class), eq(removedRuleDto));
+ }
+
private void execute(RulesDefinition... defs) {
ServerPluginRepository pluginRepository = mock(ServerPluginRepository.class);
when(pluginRepository.getPluginKey(any(RulesDefinition.class))).thenReturn(FAKE_PLUGIN_KEY);