aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPierre <pierre.guillot@sonarsource.com>2022-02-03 12:10:18 +0100
committersonartech <sonartech@sonarsource.com>2022-02-18 15:48:04 +0000
commit0c8ff1bac53c2dfa89e7be014b64b411f24434c0 (patch)
treebcad0a2d4daafe106d82f7765be71fec146eb879
parent24cd8d71c3b6df3f33f1ff0232c83bbb0a9fc060 (diff)
downloadsonarqube-0c8ff1bac53c2dfa89e7be014b64b411f24434c0.tar.gz
sonarqube-0c8ff1bac53c2dfa89e7be014b64b411f24434c0.zip
SONAR-15919 add ruleset changes
-rw-r--r--server/sonar-main/src/test/java/org/sonar/application/cluster/AppNodesClusterHostsConsistencyTest.java12
-rw-r--r--server/sonar-process/src/main/java/org/sonar/process/cluster/hz/HazelcastMember.java6
-rw-r--r--server/sonar-process/src/main/java/org/sonar/process/cluster/hz/HazelcastMemberImpl.java16
-rw-r--r--server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/BuiltInQProfileUpdateImpl.java18
-rw-r--r--server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/DistributedRuleActivatorEventsDistributor.java45
-rw-r--r--server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/QualityProfileChangeEventService.java33
-rw-r--r--server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/QualityProfileChangeEventServiceImpl.java206
-rw-r--r--server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/RuleActivator.java14
-rw-r--r--server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/RuleActivatorEventsDistributor.java30
-rw-r--r--server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/StandaloneRuleActivatorEventsDistributor.java42
-rw-r--r--server/sonar-webserver-auth/src/test/java/org/sonar/server/qualityprofile/BuiltInQProfileUpdateImplTest.java41
-rw-r--r--server/sonar-webserver-auth/src/test/java/org/sonar/server/qualityprofile/RuleActivatorTest.java2
-rw-r--r--server/sonar-webserver-pushapi/build.gradle1
-rw-r--r--server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/ServerPushClient.java17
-rw-r--r--server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/sonarlint/SonarLintClient.java8
-rw-r--r--server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/sonarlint/SonarLintClientsRegistry.java88
-rw-r--r--server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/sonarlint/SonarLintPushAction.java4
-rw-r--r--server/sonar-webserver-pushapi/src/test/java/org/sonar/server/pushapi/sonarlint/SonarLintClientsRegistryTest.java3
-rw-r--r--server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/QProfileResetImpl.java5
-rw-r--r--server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/QProfileRulesImpl.java20
-rw-r--r--server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/QProfileTreeImpl.java7
-rw-r--r--server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/ws/AddProjectAction.java23
-rw-r--r--server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/ws/RemoveProjectAction.java20
-rw-r--r--server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/QProfileComparisonTest.java4
-rw-r--r--server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/QProfileResetImplTest.java17
-rw-r--r--server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/QProfileRuleImplTest.java137
-rw-r--r--server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/QProfileRulesImplTest.java12
-rw-r--r--server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/QProfileTreeImplTest.java16
-rw-r--r--server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/RegisterQualityProfilesNotificationTest.java5
-rw-r--r--server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/ws/AddProjectActionTest.java13
-rw-r--r--server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/ws/ChangeParentActionTest.java4
-rw-r--r--server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/ws/CreateActionTest.java5
-rw-r--r--server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/ws/InheritanceActionTest.java7
-rw-r--r--server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/ws/QProfilesWsMediumTest.java5
-rw-r--r--server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/ws/RemoveProjectActionTest.java12
-rw-r--r--server/sonar-webserver-webapi/src/test/java/org/sonar/server/rule/ws/SearchActionTest.java5
-rw-r--r--server/sonar-webserver-ws/src/main/java/org/sonar/server/ws/ServletRequest.java4
-rw-r--r--server/sonar-webserver/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java7
-rw-r--r--sonar-core/src/main/java/org/sonar/core/util/ParamChange.java38
-rw-r--r--sonar-core/src/main/java/org/sonar/core/util/RuleActivationListener.java25
-rw-r--r--sonar-core/src/main/java/org/sonar/core/util/RuleChange.java68
-rw-r--r--sonar-core/src/main/java/org/sonar/core/util/RuleSetChangeEvent.java65
42 files changed, 1027 insertions, 83 deletions
diff --git a/server/sonar-main/src/test/java/org/sonar/application/cluster/AppNodesClusterHostsConsistencyTest.java b/server/sonar-main/src/test/java/org/sonar/application/cluster/AppNodesClusterHostsConsistencyTest.java
index 40875de72c1..cf0c8a1cd9e 100644
--- a/server/sonar-main/src/test/java/org/sonar/application/cluster/AppNodesClusterHostsConsistencyTest.java
+++ b/server/sonar-main/src/test/java/org/sonar/application/cluster/AppNodesClusterHostsConsistencyTest.java
@@ -40,6 +40,8 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.sonar.application.config.TestAppSettings;
+import org.sonar.core.util.RuleActivationListener;
+import org.sonar.core.util.RuleSetChangeEvent;
import org.sonar.process.cluster.hz.DistributedAnswer;
import org.sonar.process.cluster.hz.DistributedCall;
import org.sonar.process.cluster.hz.DistributedCallback;
@@ -195,6 +197,16 @@ public class AppNodesClusterHostsConsistencyTest {
}
@Override
+ public void subscribeRuleActivationTopic(RuleActivationListener listener) {
+
+ }
+
+ @Override
+ public void publishEvent(RuleSetChangeEvent event) {
+
+ }
+
+ @Override
public void close() {
}
diff --git a/server/sonar-process/src/main/java/org/sonar/process/cluster/hz/HazelcastMember.java b/server/sonar-process/src/main/java/org/sonar/process/cluster/hz/HazelcastMember.java
index 65f43bae707..320a867eb29 100644
--- a/server/sonar-process/src/main/java/org/sonar/process/cluster/hz/HazelcastMember.java
+++ b/server/sonar-process/src/main/java/org/sonar/process/cluster/hz/HazelcastMember.java
@@ -26,6 +26,8 @@ import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.locks.Lock;
+import org.sonar.core.util.RuleActivationListener;
+import org.sonar.core.util.RuleSetChangeEvent;
import org.sonar.process.ProcessId;
public interface HazelcastMember extends AutoCloseable {
@@ -106,6 +108,10 @@ public interface HazelcastMember extends AutoCloseable {
*/
<T> void callAsync(DistributedCall<T> callable, MemberSelector memberSelector, DistributedCallback<T> callback);
+ void subscribeRuleActivationTopic(RuleActivationListener listener);
+
+ void publishEvent(RuleSetChangeEvent event);
+
@Override
void close();
}
diff --git a/server/sonar-process/src/main/java/org/sonar/process/cluster/hz/HazelcastMemberImpl.java b/server/sonar-process/src/main/java/org/sonar/process/cluster/hz/HazelcastMemberImpl.java
index 685910361af..9b223cb0c61 100644
--- a/server/sonar-process/src/main/java/org/sonar/process/cluster/hz/HazelcastMemberImpl.java
+++ b/server/sonar-process/src/main/java/org/sonar/process/cluster/hz/HazelcastMemberImpl.java
@@ -27,6 +27,8 @@ import com.hazelcast.core.HazelcastInstanceNotActiveException;
import com.hazelcast.core.IExecutorService;
import com.hazelcast.core.MultiExecutionCallback;
import com.hazelcast.cp.IAtomicReference;
+import com.hazelcast.topic.ITopic;
+import com.hazelcast.topic.MessageListener;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
@@ -37,6 +39,8 @@ import java.util.concurrent.TimeoutException;
import java.util.concurrent.locks.Lock;
import java.util.stream.Collectors;
import org.slf4j.LoggerFactory;
+import org.sonar.core.util.RuleActivationListener;
+import org.sonar.core.util.RuleSetChangeEvent;
class HazelcastMemberImpl implements HazelcastMember {
@@ -126,6 +130,18 @@ class HazelcastMemberImpl implements HazelcastMember {
}
@Override
+ public void subscribeRuleActivationTopic(RuleActivationListener listener) {
+ ITopic<RuleSetChangeEvent> topic = hzInstance.getTopic("ruleActivated");
+ MessageListener<RuleSetChangeEvent> hzListener = message -> listener.listen(message.getMessageObject());
+ topic.addMessageListener(hzListener);
+ }
+
+ @Override
+ public void publishEvent(RuleSetChangeEvent event) {
+ hzInstance.getTopic("ruleActivated").publish(event);
+ }
+
+ @Override
public void close() {
try {
hzInstance.shutdown();
diff --git a/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/BuiltInQProfileUpdateImpl.java b/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/BuiltInQProfileUpdateImpl.java
index d6e019d79c4..9f0f059f6b5 100644
--- a/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/BuiltInQProfileUpdateImpl.java
+++ b/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/BuiltInQProfileUpdateImpl.java
@@ -40,11 +40,14 @@ public class BuiltInQProfileUpdateImpl implements BuiltInQProfileUpdate {
private final DbClient dbClient;
private final RuleActivator ruleActivator;
private final ActiveRuleIndexer activeRuleIndexer;
+ private final QualityProfileChangeEventService qualityProfileChangeEventService;
- public BuiltInQProfileUpdateImpl(DbClient dbClient, RuleActivator ruleActivator, ActiveRuleIndexer activeRuleIndexer) {
+ public BuiltInQProfileUpdateImpl(DbClient dbClient, RuleActivator ruleActivator, ActiveRuleIndexer activeRuleIndexer,
+ QualityProfileChangeEventService qualityProfileChangeEventService) {
this.dbClient = dbClient;
this.ruleActivator = ruleActivator;
this.activeRuleIndexer = activeRuleIndexer;
+ this.qualityProfileChangeEventService = qualityProfileChangeEventService;
}
public List<ActiveRuleChange> update(DbSession dbSession, BuiltInQProfile builtInDefinition, RulesProfileDto initialRuleProfile) {
@@ -56,8 +59,8 @@ public class BuiltInQProfileUpdateImpl implements BuiltInQProfileUpdate {
// all rules, including those which are removed from built-in profile
Set<String> ruleUuids = Stream.concat(
- deactivatedRuleUuids.stream(),
- builtInDefinition.getActiveRules().stream().map(BuiltInQProfile.ActiveRule::getRuleUuid))
+ deactivatedRuleUuids.stream(),
+ builtInDefinition.getActiveRules().stream().map(BuiltInQProfile.ActiveRule::getRuleUuid))
.collect(toSet());
Collection<RuleActivation> activations = new ArrayList<>();
@@ -69,13 +72,16 @@ public class BuiltInQProfileUpdateImpl implements BuiltInQProfileUpdate {
RuleActivationContext context = ruleActivator.createContextForBuiltInProfile(dbSession, initialRuleProfile, ruleUuids);
List<ActiveRuleChange> changes = new ArrayList<>();
- for (RuleActivation activation : activations) {
- changes.addAll(ruleActivator.activate(dbSession, activation, context));
- }
+
+ changes.addAll(ruleActivator.activate(dbSession, activations, context));
// these rules are no longer part of the built-in profile
deactivatedRuleUuids.forEach(ruleUuid -> changes.addAll(ruleActivator.deactivate(dbSession, context, ruleUuid, false)));
+ if (!changes.isEmpty()) {
+ qualityProfileChangeEventService.distributeRuleChangeEvent(context.getProfiles(), changes, initialRuleProfile.getLanguage());
+ }
+
activeRuleIndexer.commitAndIndex(dbSession, changes);
return changes;
}
diff --git a/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/DistributedRuleActivatorEventsDistributor.java b/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/DistributedRuleActivatorEventsDistributor.java
new file mode 100644
index 00000000000..a6a3b847913
--- /dev/null
+++ b/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/DistributedRuleActivatorEventsDistributor.java
@@ -0,0 +1,45 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.qualityprofile;
+
+import org.sonar.api.server.ServerSide;
+import org.sonar.core.util.RuleActivationListener;
+import org.sonar.core.util.RuleSetChangeEvent;
+import org.sonar.process.cluster.hz.HazelcastMember;
+
+@ServerSide
+public class DistributedRuleActivatorEventsDistributor implements RuleActivatorEventsDistributor {
+
+ private HazelcastMember hazelcastMember;
+
+ public DistributedRuleActivatorEventsDistributor(HazelcastMember hazelcastMember) {
+ this.hazelcastMember = hazelcastMember;
+ }
+
+ @Override
+ public void subscribe(RuleActivationListener listener) {
+ hazelcastMember.subscribeRuleActivationTopic(listener);
+ }
+
+ @Override
+ public void pushEvent(RuleSetChangeEvent event) {
+ hazelcastMember.publishEvent(event);
+ }
+}
diff --git a/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/QualityProfileChangeEventService.java b/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/QualityProfileChangeEventService.java
new file mode 100644
index 00000000000..008afff27f5
--- /dev/null
+++ b/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/QualityProfileChangeEventService.java
@@ -0,0 +1,33 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.qualityprofile;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Optional;
+import org.sonar.db.project.ProjectDto;
+import org.sonar.db.qualityprofile.QProfileDto;
+
+public interface QualityProfileChangeEventService {
+
+ void publishRuleActivationToSonarLintClients(ProjectDto project, Optional<QProfileDto> activatedProfile, Optional<QProfileDto> deactivatedProfile);
+
+ void distributeRuleChangeEvent(Collection<QProfileDto> profiles, List<ActiveRuleChange> activeRuleChanges, String language);
+}
diff --git a/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/QualityProfileChangeEventServiceImpl.java b/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/QualityProfileChangeEventServiceImpl.java
new file mode 100644
index 00000000000..22c96e6ff4a
--- /dev/null
+++ b/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/QualityProfileChangeEventServiceImpl.java
@@ -0,0 +1,206 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.qualityprofile;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+import org.jetbrains.annotations.NotNull;
+import org.sonar.api.server.ServerSide;
+import org.sonar.core.util.ParamChange;
+import org.sonar.core.util.RuleChange;
+import org.sonar.core.util.RuleSetChangeEvent;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.project.ProjectDto;
+import org.sonar.db.qualityprofile.ActiveRuleParamDto;
+import org.sonar.db.qualityprofile.ProjectQprofileAssociationDto;
+import org.sonar.db.qualityprofile.QProfileDto;
+import org.sonar.db.rule.RuleDto;
+import org.sonar.server.rule.index.RuleIndex;
+import org.sonar.server.rule.index.RuleQuery;
+
+@ServerSide
+public class QualityProfileChangeEventServiceImpl implements QualityProfileChangeEventService {
+
+ private final DbClient dbClient;
+ private final RuleIndex ruleIndex;
+ private final RuleActivatorEventsDistributor eventsDistributor;
+
+ public QualityProfileChangeEventServiceImpl(DbClient dbClient, RuleIndex ruleIndex, RuleActivatorEventsDistributor eventsDistributor) {
+ this.dbClient = dbClient;
+ this.ruleIndex = ruleIndex;
+ this.eventsDistributor = eventsDistributor;
+ }
+
+ @Override
+ public void publishRuleActivationToSonarLintClients(ProjectDto project, Optional<QProfileDto> activatedProfile, Optional<QProfileDto> deactivatedProfile) {
+ List<RuleChange> activatedRules = new ArrayList<>();
+ List<RuleChange> deactivatedRules = new ArrayList<>();
+
+ try (DbSession dbSession = dbClient.openSession(false)) {
+
+ if (activatedProfile.isPresent()) {
+ RuleQuery query = new RuleQuery().setQProfile(activatedProfile.get()).setActivation(true).setIncludeExternal(true);
+ // .setLanguages() ?
+ Iterator<String> searchIdResult = ruleIndex.searchAll(query);
+ List<String> uuids = new ArrayList<>();
+ while (searchIdResult.hasNext()) {
+ uuids.add(searchIdResult.next());
+ }
+
+ List<RuleDto> ruleDtos = dbClient.ruleDao().selectByUuids(dbSession, uuids);
+ Map<String, List<ActiveRuleParamDto>> paramsByRuleUuid = dbClient.activeRuleDao().selectParamsByActiveRuleUuids(dbSession, uuids)
+ .stream().collect(Collectors.groupingBy(ActiveRuleParamDto::getActiveRuleUuid));
+
+ for (RuleDto ruleDto : ruleDtos) {
+ RuleChange ruleChange = toRuleChange(ruleDto, paramsByRuleUuid);
+ activatedRules.add(ruleChange);
+ }
+ }
+
+ if (deactivatedProfile.isPresent()) {
+ RuleQuery query = new RuleQuery().setQProfile(deactivatedProfile.get()).setActivation(true).setIncludeExternal(true);
+ // .setLanguages() ?
+ Iterator<String> searchIdResult = ruleIndex.searchAll(query);
+ List<String> uuids = new ArrayList<>();
+ while (searchIdResult.hasNext()) {
+ uuids.add(searchIdResult.next());
+ }
+
+ List<RuleDto> ruleDtos = dbClient.ruleDao().selectByUuids(dbSession, uuids);
+ Map<String, List<ActiveRuleParamDto>> paramsByRuleUuid = dbClient.activeRuleDao().selectParamsByActiveRuleUuids(dbSession, uuids)
+ .stream().collect(Collectors.groupingBy(ActiveRuleParamDto::getActiveRuleUuid));
+
+ for (RuleDto ruleDto : ruleDtos) {
+ RuleChange ruleChange = toRuleChange(ruleDto, paramsByRuleUuid);
+ deactivatedRules.add(ruleChange);
+ }
+ }
+
+ }
+
+ RuleSetChangeEvent event = new RuleSetChangeEvent(new String[]{project.getKey()}, activatedRules.toArray(new RuleChange[0]), deactivatedRules.toArray(new RuleChange[0]));
+ eventsDistributor.pushEvent(event);
+ }
+
+ @NotNull
+ private RuleChange toRuleChange(RuleDto ruleDto, Map<String, List<ActiveRuleParamDto>> paramsByRuleUuid) {
+ RuleChange ruleChange = new RuleChange();
+ ruleChange.setKey(ruleDto.getRuleKey());
+ ruleChange.setLanguage(ruleDto.getLanguage());
+ ruleChange.setSeverity(ruleDto.getSeverityString());
+
+ List<ParamChange> paramChanges = new ArrayList<>();
+ List<ActiveRuleParamDto> activeRuleParamDtos = paramsByRuleUuid.getOrDefault(ruleDto.getUuid(), new ArrayList<>());
+ for (ActiveRuleParamDto activeRuleParam : activeRuleParamDtos) {
+ paramChanges.add(new ParamChange(activeRuleParam.getKey(), activeRuleParam.getValue()));
+ }
+ ruleChange.setParams(paramChanges.toArray(new ParamChange[0]));
+
+ String templateUuid = ruleDto.getTemplateUuid();
+ if (templateUuid != null && !"".equals(templateUuid)) {
+ try (DbSession dbSession = dbClient.openSession(false)) {
+ RuleDto templateRule = dbClient.ruleDao().selectByUuid(templateUuid, dbSession)
+ .orElseThrow(() -> new IllegalStateException(String.format("unknow Template Rule '%s'", templateUuid)));
+ ruleChange.setTemplateKey(templateRule.getRuleKey());
+ }
+ }
+
+ return ruleChange;
+ }
+
+ public void distributeRuleChangeEvent(Collection<QProfileDto> profiles, List<ActiveRuleChange> activeRuleChanges, String language) {
+ if (activeRuleChanges.isEmpty()) {
+ return;
+ }
+
+ Set<RuleChange> activatedRules = new HashSet<>();
+ Set<RuleChange> deactivatedRules = new HashSet<>();
+
+ for (ActiveRuleChange arc : activeRuleChanges) {
+
+ RuleChange ruleChange = new RuleChange();
+ ruleChange.setKey(arc.getActiveRule().getRuleKey().rule());
+ ruleChange.setSeverity(arc.getSeverity());
+ ruleChange.setLanguage(language);
+
+ Optional<String> templateKey = templateKey(arc);
+ templateKey.ifPresent(ruleChange::setTemplateKey);
+
+ // params
+ List<ParamChange> paramChanges = new ArrayList<>();
+ for (Map.Entry<String, String> entry : arc.getParameters().entrySet()) {
+ paramChanges.add(new ParamChange(entry.getKey(), entry.getValue()));
+ }
+ ruleChange.setParams(paramChanges.toArray(new ParamChange[0]));
+
+ switch (arc.getType()) {
+ case ACTIVATED:
+ case UPDATED:
+ activatedRules.add(ruleChange);
+ break;
+ case DEACTIVATED:
+ deactivatedRules.add(ruleChange);
+ break;
+ }
+ }
+
+ Set<String> projectKeys = getProjectKeys(profiles);
+
+ RuleSetChangeEvent event = new RuleSetChangeEvent(projectKeys.toArray(new String[0]), activatedRules.toArray(new RuleChange[0]), deactivatedRules.toArray(new RuleChange[0]));
+ eventsDistributor.pushEvent(event);
+
+ }
+
+ private Optional<String> templateKey(ActiveRuleChange arc) {
+ try (DbSession dbSession = dbClient.openSession(false)) {
+ String ruleUuid = arc.getRuleUuid();
+ RuleDto rule = dbClient.ruleDao().selectByUuid(ruleUuid, dbSession).orElseThrow(() -> new IllegalStateException("unknow rule"));
+ String templateUuid = rule.getTemplateUuid();
+ if (templateUuid != null && !"".equals(templateUuid)) {
+ RuleDto templateRule = dbClient.ruleDao().selectByUuid(templateUuid, dbSession)
+ .orElseThrow(() -> new IllegalStateException(String.format("unknow Template Rule '%s'", templateUuid)));
+ return Optional.of(templateRule.getRuleKey());
+ }
+ }
+ return Optional.empty();
+ }
+
+ private Set<String> getProjectKeys(Collection<QProfileDto> profiles) {
+ Set<String> projectKeys = new HashSet<>();
+ try (DbSession dbSession = dbClient.openSession(false)) {
+ for (QProfileDto profileDto : profiles) {
+ List<ProjectQprofileAssociationDto> associationDtos = dbClient.qualityProfileDao().selectSelectedProjects(dbSession, profileDto, null);
+ for (ProjectQprofileAssociationDto associationDto : associationDtos) {
+ projectKeys.add(associationDto.getProjectKey());
+ }
+ }
+ return projectKeys;
+ }
+ }
+
+}
diff --git a/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/RuleActivator.java b/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/RuleActivator.java
index 63a98c72e49..713ae6ce9ab 100644
--- a/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/RuleActivator.java
+++ b/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/RuleActivator.java
@@ -52,6 +52,7 @@ import org.sonar.server.user.UserSession;
import org.sonar.server.util.TypeValidations;
import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.stream.Collectors.toList;
import static org.sonar.server.exceptions.BadRequestException.checkRequest;
/**
@@ -72,9 +73,17 @@ public class RuleActivator {
this.userSession = userSession;
}
+
+ public List<ActiveRuleChange> activate(DbSession dbSession, Collection<RuleActivation> activations, RuleActivationContext context) {
+ return activations.stream().map(a -> activate(dbSession, a, context))
+ .flatMap(List::stream)
+ .collect(toList());
+ }
+
public List<ActiveRuleChange> activate(DbSession dbSession, RuleActivation activation, RuleActivationContext context) {
context.reset(activation.getRuleUuid());
- return doActivate(dbSession, activation, context);
+ List<ActiveRuleChange> activeRuleChanges = doActivate(dbSession, activation, context);
+ return activeRuleChanges;
}
private List<ActiveRuleChange> doActivate(DbSession dbSession, RuleActivation activation, RuleActivationContext context) {
@@ -357,7 +366,8 @@ public class RuleActivator {
public List<ActiveRuleChange> deactivate(DbSession dbSession, RuleActivationContext context, String ruleUuid, boolean force) {
context.reset(ruleUuid);
- return doDeactivate(dbSession, context, force);
+ List<ActiveRuleChange> activeRuleChanges = doDeactivate(dbSession, context, force);
+ return activeRuleChanges;
}
private List<ActiveRuleChange> doDeactivate(DbSession dbSession, RuleActivationContext context, boolean force) {
diff --git a/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/RuleActivatorEventsDistributor.java b/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/RuleActivatorEventsDistributor.java
new file mode 100644
index 00000000000..d41f1a5abd1
--- /dev/null
+++ b/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/RuleActivatorEventsDistributor.java
@@ -0,0 +1,30 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.qualityprofile;
+
+import org.sonar.core.util.RuleActivationListener;
+import org.sonar.core.util.RuleSetChangeEvent;
+
+public interface RuleActivatorEventsDistributor {
+
+ void subscribe(RuleActivationListener listener);
+
+ void pushEvent(RuleSetChangeEvent event);
+} \ No newline at end of file
diff --git a/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/StandaloneRuleActivatorEventsDistributor.java b/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/StandaloneRuleActivatorEventsDistributor.java
new file mode 100644
index 00000000000..43fc97da319
--- /dev/null
+++ b/server/sonar-webserver-auth/src/main/java/org/sonar/server/qualityprofile/StandaloneRuleActivatorEventsDistributor.java
@@ -0,0 +1,42 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.qualityprofile;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.sonar.api.server.ServerSide;
+import org.sonar.core.util.RuleActivationListener;
+import org.sonar.core.util.RuleSetChangeEvent;
+
+@ServerSide
+public class StandaloneRuleActivatorEventsDistributor implements RuleActivatorEventsDistributor {
+
+ private List<RuleActivationListener> listeners = new ArrayList<>();
+
+ @Override
+ public void subscribe(RuleActivationListener listener) {
+ listeners.add(listener);
+ }
+
+ @Override
+ public void pushEvent(RuleSetChangeEvent event) {
+ listeners.forEach(l -> l.listen(event));
+ }
+}
diff --git a/server/sonar-webserver-auth/src/test/java/org/sonar/server/qualityprofile/BuiltInQProfileUpdateImplTest.java b/server/sonar-webserver-auth/src/test/java/org/sonar/server/qualityprofile/BuiltInQProfileUpdateImplTest.java
index cfbf49f137f..024de98b39f 100644
--- a/server/sonar-webserver-auth/src/test/java/org/sonar/server/qualityprofile/BuiltInQProfileUpdateImplTest.java
+++ b/server/sonar-webserver-auth/src/test/java/org/sonar/server/qualityprofile/BuiltInQProfileUpdateImplTest.java
@@ -56,7 +56,12 @@ import static java.util.Collections.emptyMap;
import static java.util.Collections.singletonList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.groups.Tuple.tuple;
+import static org.mockito.ArgumentMatchers.any;
+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;
@@ -78,9 +83,11 @@ public class BuiltInQProfileUpdateImplTest {
private System2 system2 = new TestSystem2().setNow(NOW);
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 BuiltInQProfileUpdateImpl underTest = new BuiltInQProfileUpdateImpl(db.getDbClient(), ruleActivator, activeRuleIndexer);
+ private BuiltInQProfileUpdateImpl underTest = new BuiltInQProfileUpdateImpl(db.getDbClient(), ruleActivator, activeRuleIndexer,
+ qualityProfileChangeEventService);
private RulesProfileDto persistedProfile;
@@ -105,13 +112,14 @@ public class BuiltInQProfileUpdateImplTest {
newQp.done();
BuiltInQProfile builtIn = builtInProfileRepository.create(context.profile("xoo", "Sonar way"), rule1, rule2);
- underTest.update(db.getSession(), builtIn, persistedProfile);
+ List<ActiveRuleChange> changes = underTest.update(db.getSession(), builtIn, persistedProfile);
List<ActiveRuleDto> activeRules = db.getDbClient().activeRuleDao().selectByRuleProfile(db.getSession(), persistedProfile);
assertThat(activeRules).hasSize(2);
assertThatRuleIsNewlyActivated(activeRules, rule1, CRITICAL);
assertThatRuleIsNewlyActivated(activeRules, rule2, MAJOR);
assertThatProfileIsMarkedAsUpdated(persistedProfile);
+ verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(persistedProfile.getLanguage()));
}
@Test
@@ -125,12 +133,13 @@ public class BuiltInQProfileUpdateImplTest {
activateRuleInDb(persistedProfile, rule, BLOCKER);
- underTest.update(db.getSession(), builtIn, persistedProfile);
+ 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);
assertThatProfileIsMarkedAsUpdated(persistedProfile);
+ verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(persistedProfile.getLanguage()));
}
@Test
@@ -150,6 +159,7 @@ public class BuiltInQProfileUpdateImplTest {
assertThat(activeRules).hasSize(1);
assertThatRuleIsUntouched(activeRules, rule, CRITICAL);
assertThatProfileIsNotMarkedAsUpdated(persistedProfile);
+ verifyNoInteractions(qualityProfileChangeEventService);
}
@Test
@@ -166,12 +176,13 @@ public class BuiltInQProfileUpdateImplTest {
// so rule1 must be deactivated
activateRuleInDb(persistedProfile, rule1, CRITICAL);
- underTest.update(db.getSession(), builtIn, persistedProfile);
+ List<ActiveRuleChange> changes = underTest.update(db.getSession(), builtIn, persistedProfile);
List<ActiveRuleDto> activeRules = db.getDbClient().activeRuleDao().selectByRuleProfile(db.getSession(), persistedProfile);
assertThat(activeRules).hasSize(1);
assertThatRuleIsDeactivated(activeRules, rule1);
assertThatProfileIsMarkedAsUpdated(persistedProfile);
+ verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(persistedProfile.getLanguage()));
}
@Test
@@ -193,7 +204,7 @@ public class BuiltInQProfileUpdateImplTest {
activateRuleInDb(persistedProfile, rule1, BLOCKER);
activateRuleInDb(persistedProfile, rule3, BLOCKER);
- underTest.update(db.getSession(), builtIn, persistedProfile);
+ List<ActiveRuleChange> changes = underTest.update(db.getSession(), builtIn, persistedProfile);
List<ActiveRuleDto> activeRules = db.getDbClient().activeRuleDao().selectByRuleProfile(db.getSession(), persistedProfile);
assertThat(activeRules).hasSize(2);
@@ -201,6 +212,7 @@ public class BuiltInQProfileUpdateImplTest {
assertThatRuleIsNewlyActivated(activeRules, rule2, MAJOR);
assertThatRuleIsDeactivated(activeRules, rule3);
assertThatProfileIsMarkedAsUpdated(persistedProfile);
+ verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(persistedProfile.getLanguage()));
}
// SONAR-10473
@@ -222,9 +234,10 @@ public class BuiltInQProfileUpdateImplTest {
rule.setSeverity(Severity.MINOR);
db.rules().update(rule);
- underTest.update(db.getSession(), builtIn, persistedProfile);
+ List<ActiveRuleChange> changes = underTest.update(db.getSession(), builtIn, persistedProfile);
activeRules = db.getDbClient().activeRuleDao().selectByRuleProfile(db.getSession(), persistedProfile);
assertThatRuleIsNewlyActivated(activeRules, rule, MINOR);
+ verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(persistedProfile.getLanguage()));
}
@Test
@@ -237,20 +250,22 @@ public class BuiltInQProfileUpdateImplTest {
newQp.activateRule(rule.getRepositoryKey(), rule.getRuleKey());
newQp.done();
BuiltInQProfile builtIn = builtInProfileRepository.create(context.profile(newQp.language(), newQp.name()), rule);
- underTest.update(db.getSession(), builtIn, persistedProfile);
+ List<ActiveRuleChange> changes = underTest.update(db.getSession(), builtIn, persistedProfile);
List<ActiveRuleDto> activeRules = db.getDbClient().activeRuleDao().selectByRuleProfile(db.getSession(), persistedProfile);
assertThat(activeRules).hasSize(1);
assertThatRuleHasParams(db, activeRules.get(0), tuple("min", "10"));
+ verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(persistedProfile.getLanguage()));
// emulate an upgrade of analyzer that changes the default value of parameter min
ruleParam.setDefaultValue("20");
db.getDbClient().ruleDao().updateRuleParam(db.getSession(), rule, ruleParam);
- underTest.update(db.getSession(), builtIn, persistedProfile);
+ changes = underTest.update(db.getSession(), builtIn, persistedProfile);
activeRules = db.getDbClient().activeRuleDao().selectByRuleProfile(db.getSession(), persistedProfile);
assertThat(activeRules).hasSize(1);
assertThatRuleHasParams(db, activeRules.get(0), tuple("min", "20"));
+ verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(persistedProfile.getLanguage()));
}
@Test
@@ -272,6 +287,7 @@ public class BuiltInQProfileUpdateImplTest {
assertThatRuleIsActivated(profile, rule, changes, rule.getSeverityString(), null, emptyMap());
assertThatRuleIsActivated(childProfile, rule, changes, rule.getSeverityString(), INHERITED, emptyMap());
assertThatRuleIsActivated(grandchildProfile, rule, changes, rule.getSeverityString(), INHERITED, emptyMap());
+ verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(persistedProfile.getLanguage()));
}
// SONAR-14559
@@ -299,6 +315,7 @@ public class BuiltInQProfileUpdateImplTest {
List<ActiveRuleDto> childActiveRules = db.getDbClient().activeRuleDao().selectByRuleProfile(db.getSession(), RulesProfileDto.from(childProfile));
assertThatRuleIsUpdated(childActiveRules, rule, RulePriority.BLOCKER, INHERITED);
+ verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(persistedProfile.getLanguage()));
}
@Test
@@ -327,6 +344,7 @@ public class BuiltInQProfileUpdateImplTest {
assertThatRuleHasParams(db, parentActiveRuleDto, tuple("min", "10"));
assertThatRuleHasParams(db, childActiveRuleDto, tuple("min", "10"));
+ verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(persistedProfile.getLanguage()));
}
@Test
@@ -345,6 +363,7 @@ public class BuiltInQProfileUpdateImplTest {
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) {
@@ -355,8 +374,10 @@ public class BuiltInQProfileUpdateImplTest {
};
}
};
- changes = new BuiltInQProfileUpdateImpl(db.getDbClient(), ruleActivatorWithoutDescendants, activeRuleIndexer).update(db.getSession(), builtIn, RulesProfileDto.from(profile));
+ changes = new BuiltInQProfileUpdateImpl(db.getDbClient(), ruleActivatorWithoutDescendants, activeRuleIndexer, qualityProfileChangeEventService)
+ .update(db.getSession(), builtIn, RulesProfileDto.from(profile));
assertThat(changes).isEmpty();
+ verifyNoMoreInteractions(qualityProfileChangeEventService);
}
@Test
@@ -376,6 +397,7 @@ public class BuiltInQProfileUpdateImplTest {
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(3).extracting(ActiveRuleChange::getType).containsOnly(ActiveRuleChange.Type.ACTIVATED);
+ verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(persistedProfile.getLanguage()));
// second run to deactivate the rule
context = new BuiltInQualityProfilesDefinition.Context();
@@ -384,6 +406,7 @@ public class BuiltInQProfileUpdateImplTest {
builtIn = builtInProfileRepository.create(context.profile(profile.getLanguage(), profile.getName()), rule);
changes = underTest.update(db.getSession(), builtIn, RulesProfileDto.from(profile));
assertThat(changes).hasSize(3).extracting(ActiveRuleChange::getType).containsOnly(ActiveRuleChange.Type.DEACTIVATED);
+ verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(persistedProfile.getLanguage()));
assertThatRuleIsDeactivated(profile, rule);
assertThatRuleIsDeactivated(childProfile, rule);
diff --git a/server/sonar-webserver-auth/src/test/java/org/sonar/server/qualityprofile/RuleActivatorTest.java b/server/sonar-webserver-auth/src/test/java/org/sonar/server/qualityprofile/RuleActivatorTest.java
index 45310b7d9b3..cdcd4fa41e2 100644
--- a/server/sonar-webserver-auth/src/test/java/org/sonar/server/qualityprofile/RuleActivatorTest.java
+++ b/server/sonar-webserver-auth/src/test/java/org/sonar/server/qualityprofile/RuleActivatorTest.java
@@ -50,6 +50,7 @@ import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertThrows;
+import static org.mockito.Mockito.mock;
import static org.sonar.server.qualityprofile.ActiveRuleInheritance.INHERITED;
import static org.sonar.server.qualityprofile.ActiveRuleInheritance.OVERRIDES;
@@ -69,6 +70,7 @@ public class RuleActivatorTest {
private final System2 system2 = new TestSystem2().setNow(NOW);
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);
@Test
diff --git a/server/sonar-webserver-pushapi/build.gradle b/server/sonar-webserver-pushapi/build.gradle
index a2c94714bd0..f3f66fea494 100644
--- a/server/sonar-webserver-pushapi/build.gradle
+++ b/server/sonar-webserver-pushapi/build.gradle
@@ -8,6 +8,7 @@ dependencies {
compile 'javax.servlet:javax.servlet-api'
compile project(':server:sonar-webserver-auth')
compile project(':server:sonar-webserver-ws')
+ compile project(':server:sonar-webserver-webapi')
testCompile 'junit:junit'
testCompile 'org.assertj:assertj-core'
diff --git a/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/ServerPushClient.java b/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/ServerPushClient.java
index 4904b9e7cd1..cc0d3d7f0c7 100644
--- a/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/ServerPushClient.java
+++ b/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/ServerPushClient.java
@@ -20,6 +20,7 @@
package org.sonar.server.pushapi;
import java.io.IOException;
+import java.nio.charset.StandardCharsets;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
@@ -34,6 +35,17 @@ public abstract class ServerPushClient {
private static final Logger LOG = Loggers.get(ServerPushClient.class);
private static final int DEFAULT_HEARTBEAT_PERIOD = 60;
+
+
+
+ private static final byte[] CRLF = new byte[] {'\r', '\n'};
+ private static final byte[] DATA_END = new byte[] {'\n', '\n'};
+ private static final byte[] DATA_FIELD = "data: ".getBytes(StandardCharsets.UTF_8);
+
+
+
+
+
protected final AsyncContext asyncContext;
private final ScheduledExecutorService executorService;
@@ -50,6 +62,11 @@ public abstract class ServerPushClient {
startedHeartbeat = executorService.schedule(heartbeatTask, DEFAULT_HEARTBEAT_PERIOD, TimeUnit.SECONDS);
}
+ public void writeAndFlush(String payload) throws IOException {
+ output().write(payload.getBytes(StandardCharsets.UTF_8));
+ flush();
+ }
+
public void writeAndFlush(char character) {
write(character);
flush();
diff --git a/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/sonarlint/SonarLintClient.java b/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/sonarlint/SonarLintClient.java
index b022cbcb25d..93512a974ec 100644
--- a/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/sonarlint/SonarLintClient.java
+++ b/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/sonarlint/SonarLintClient.java
@@ -39,6 +39,14 @@ public class SonarLintClient extends ServerPushClient {
this.languages = languages;
}
+ public Set<String> getLanguages() {
+ return languages;
+ }
+
+ public Set<String> getClientProjectKeys() {
+ return projectKeys;
+ }
+
@Override
public boolean equals(Object o) {
if (this == o) {
diff --git a/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/sonarlint/SonarLintClientsRegistry.java b/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/sonarlint/SonarLintClientsRegistry.java
index e93bf5f9073..4b72f6de66d 100644
--- a/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/sonarlint/SonarLintClientsRegistry.java
+++ b/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/sonarlint/SonarLintClientsRegistry.java
@@ -19,25 +19,46 @@
*/
package org.sonar.server.pushapi.sonarlint;
+import java.io.IOException;
+import java.util.Collections;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.function.Predicate;
import javax.servlet.AsyncEvent;
import javax.servlet.AsyncListener;
+import org.json.JSONArray;
+import org.json.JSONObject;
import org.sonar.api.server.ServerSide;
import org.sonar.api.utils.log.Logger;
import org.sonar.api.utils.log.Loggers;
+import org.sonar.core.util.ParamChange;
+import org.sonar.core.util.RuleActivationListener;
+import org.sonar.core.util.RuleChange;
+import org.sonar.core.util.RuleSetChangeEvent;
+import org.sonar.server.qualityprofile.RuleActivatorEventsDistributor;
+
+import static java.util.Arrays.asList;
@ServerSide
-public class SonarLintClientsRegistry {
+public class SonarLintClientsRegistry implements RuleActivationListener {
private static final Logger LOG = Loggers.get(SonarLintClientsRegistry.class);
+ private final RuleActivatorEventsDistributor ruleActivatorEventsDistributor;
+
+ public SonarLintClientsRegistry(RuleActivatorEventsDistributor ruleActivatorEventsDistributor) {
+ this.ruleActivatorEventsDistributor = ruleActivatorEventsDistributor;
+ }
+
+
private final List<SonarLintClient> clients = new CopyOnWriteArrayList<>();
public void registerClient(SonarLintClient sonarLintClient) {
clients.add(sonarLintClient);
sonarLintClient.scheduleHeartbeat();
sonarLintClient.addListener(new SonarLintClientEventsListener(sonarLintClient));
+ ruleActivatorEventsDistributor.subscribe(this);
+
LOG.debug("Registering new SonarLint client");
}
@@ -50,6 +71,71 @@ public class SonarLintClientsRegistry {
return clients.size();
}
+ @Override
+ public void listen(RuleSetChangeEvent ruleChangeEvent) {
+ LOG.info("Generating a RuleSetChangeEvent");
+ // TODO filter on languages here as well
+ broadcastMessage(ruleChangeEvent, f -> f.getClientProjectKeys().isEmpty() || !Collections.disjoint(f.getClientProjectKeys(), asList(ruleChangeEvent.getProjects())));
+ }
+
+
+ public void broadcastMessage(RuleSetChangeEvent message, Predicate<SonarLintClient> filter) {
+ String jsonString = getJSONString(message);
+ clients.stream().filter(filter).forEach(c -> {
+ try {
+ c.writeAndFlush(jsonString);
+ } catch (IOException e) {
+ LOG.error("Unable to send message to a client: " + e.getMessage());
+ }
+ });
+ }
+
+
+ public String getJSONString(RuleSetChangeEvent ruleSetChangeEvent) {
+ JSONObject result = new JSONObject();
+ result.put("event", ruleSetChangeEvent.getEvent());
+
+ JSONObject data = new JSONObject();
+ data.put("projects", ruleSetChangeEvent.getProjects());
+
+ JSONArray activatedRulesJson = new JSONArray();
+ for (RuleChange rule : ruleSetChangeEvent.getActivatedRules()) {
+ activatedRulesJson.put(toJson(rule));
+ }
+ data.put("activatedRules", activatedRulesJson);
+
+ JSONArray deactivatedRulesJson = new JSONArray();
+ for (RuleChange rule : ruleSetChangeEvent.getDeactivatedRules()) {
+ deactivatedRulesJson.put(toJson(rule));
+ }
+ data.put("deactivatedRules", deactivatedRulesJson);
+
+ result.put("data", data);
+ return result.toString();
+ }
+
+ private JSONObject toJson(RuleChange rule) {
+ JSONObject ruleJson = new JSONObject();
+ ruleJson.put("key", rule.getKey());
+ ruleJson.put("language", rule.getLanguage());
+ ruleJson.put("severity", rule.getSeverity());
+ ruleJson.put("templateKey", rule.getTemplateKey());
+
+ JSONArray params = new JSONArray();
+ for (ParamChange paramChange : rule.getParams()) {
+ params.put(toJson(paramChange));
+ }
+ ruleJson.put("params", params);
+ return ruleJson;
+ }
+
+ private JSONObject toJson(ParamChange paramChange) {
+ JSONObject param = new JSONObject();
+ param.put("key", paramChange.getKey());
+ param.put("value", paramChange.getValue());
+ return param;
+ }
+
class SonarLintClientEventsListener implements AsyncListener {
private final SonarLintClient client;
diff --git a/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/sonarlint/SonarLintPushAction.java b/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/sonarlint/SonarLintPushAction.java
index 5bcce895562..2c27c48cda1 100644
--- a/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/sonarlint/SonarLintPushAction.java
+++ b/server/sonar-webserver-pushapi/src/main/java/org/sonar/server/pushapi/sonarlint/SonarLintPushAction.java
@@ -84,7 +84,7 @@ public class SonarLintPushAction extends ServerPushAction {
if (!isServerSideEventsRequest(servletRequest)) {
servletResponse.stream().setStatus(HttpServletResponse.SC_NOT_ACCEPTABLE);
- return;
+ return; // TODO fixme this is not closing the connexion properly
}
setHeadersForResponse(servletResponse);
@@ -92,7 +92,7 @@ public class SonarLintPushAction extends ServerPushAction {
AsyncContext asyncContext = servletRequest.startAsync();
asyncContext.setTimeout(0);
- var sonarLintClient = new SonarLintClient(asyncContext, params.getProjectKeys(), params.getLanguages());
+ SonarLintClient sonarLintClient = new SonarLintClient(asyncContext, params.getProjectKeys(), params.getLanguages());
clientsRegistry.registerClient(sonarLintClient);
}
diff --git a/server/sonar-webserver-pushapi/src/test/java/org/sonar/server/pushapi/sonarlint/SonarLintClientsRegistryTest.java b/server/sonar-webserver-pushapi/src/test/java/org/sonar/server/pushapi/sonarlint/SonarLintClientsRegistryTest.java
index 450245c13c0..49076199685 100644
--- a/server/sonar-webserver-pushapi/src/test/java/org/sonar/server/pushapi/sonarlint/SonarLintClientsRegistryTest.java
+++ b/server/sonar-webserver-pushapi/src/test/java/org/sonar/server/pushapi/sonarlint/SonarLintClientsRegistryTest.java
@@ -23,6 +23,7 @@ import java.util.Set;
import javax.servlet.AsyncContext;
import org.junit.Before;
import org.junit.Test;
+import org.sonar.server.qualityprofile.StandaloneRuleActivatorEventsDistributor;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
@@ -39,7 +40,7 @@ public class SonarLintClientsRegistryTest {
@Before
public void before() {
- underTest = new SonarLintClientsRegistry();
+ underTest = new SonarLintClientsRegistry(mock(StandaloneRuleActivatorEventsDistributor.class));
}
@Test
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/QProfileResetImpl.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/QProfileResetImpl.java
index e4e0cab8bf1..7deadcbbfee 100644
--- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/QProfileResetImpl.java
+++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/QProfileResetImpl.java
@@ -41,11 +41,13 @@ public class QProfileResetImpl implements QProfileReset {
private final DbClient db;
private final RuleActivator activator;
private final ActiveRuleIndexer activeRuleIndexer;
+ private final QualityProfileChangeEventService qualityProfileChangeEventService;
- public QProfileResetImpl(DbClient db, RuleActivator activator, ActiveRuleIndexer activeRuleIndexer) {
+ public QProfileResetImpl(DbClient db, RuleActivator activator, ActiveRuleIndexer activeRuleIndexer, QualityProfileChangeEventService qualityProfileChangeEventService) {
this.db = db;
this.activator = activator;
this.activeRuleIndexer = activeRuleIndexer;
+ this.qualityProfileChangeEventService = qualityProfileChangeEventService;
}
@Override
@@ -87,6 +89,7 @@ public class QProfileResetImpl implements QProfileReset {
// ignore, probably a rule inherited from parent that can't be deactivated
}
}
+ qualityProfileChangeEventService.distributeRuleChangeEvent(List.of(profile), changes, profile.getLanguage());
activeRuleIndexer.commitAndIndex(dbSession, changes);
return result;
}
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/QProfileRulesImpl.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/QProfileRulesImpl.java
index dca861c597e..db891a34ecc 100644
--- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/QProfileRulesImpl.java
+++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/QProfileRulesImpl.java
@@ -44,12 +44,15 @@ public class QProfileRulesImpl implements QProfileRules {
private final RuleActivator ruleActivator;
private final RuleIndex ruleIndex;
private final ActiveRuleIndexer activeRuleIndexer;
+ private final QualityProfileChangeEventService qualityProfileChangeEventService;
- public QProfileRulesImpl(DbClient db, RuleActivator ruleActivator, RuleIndex ruleIndex, ActiveRuleIndexer activeRuleIndexer) {
+ public QProfileRulesImpl(DbClient db, RuleActivator ruleActivator, RuleIndex ruleIndex, ActiveRuleIndexer activeRuleIndexer,
+ QualityProfileChangeEventService qualityProfileChangeEventService) {
this.db = db;
this.ruleActivator = ruleActivator;
this.ruleIndex = ruleIndex;
this.activeRuleIndexer = activeRuleIndexer;
+ this.qualityProfileChangeEventService = qualityProfileChangeEventService;
}
@Override
@@ -63,6 +66,7 @@ public class QProfileRulesImpl implements QProfileRules {
for (RuleActivation activation : activations) {
changes.addAll(ruleActivator.activate(dbSession, activation, context));
}
+ qualityProfileChangeEventService.distributeRuleChangeEvent(List.of(profile), changes, profile.getLanguage());
activeRuleIndexer.commitAndIndex(dbSession, changes);
return changes;
}
@@ -70,10 +74,12 @@ public class QProfileRulesImpl implements QProfileRules {
@Override
public BulkChangeResult bulkActivateAndCommit(DbSession dbSession, QProfileDto profile, RuleQuery ruleQuery, @Nullable String severity) {
verifyNotBuiltIn(profile);
- return doBulk(dbSession, profile, ruleQuery, (context, ruleDefinition) -> {
+ BulkChangeResult bulkChangeResult = doBulk(dbSession, profile, ruleQuery, (context, ruleDefinition) -> {
RuleActivation activation = RuleActivation.create(ruleDefinition.getUuid(), severity, null);
return ruleActivator.activate(dbSession, activation, context);
});
+ qualityProfileChangeEventService.distributeRuleChangeEvent(List.of(profile), bulkChangeResult.getChanges(), profile.getLanguage());
+ return bulkChangeResult;
}
@Override
@@ -85,6 +91,9 @@ public class QProfileRulesImpl implements QProfileRules {
for (String ruleUuid : ruleUuids) {
changes.addAll(ruleActivator.deactivate(dbSession, context, ruleUuid, false));
}
+
+ qualityProfileChangeEventService.distributeRuleChangeEvent(List.of(profile), changes, profile.getLanguage());
+
activeRuleIndexer.commitAndIndex(dbSession, changes);
return changes;
}
@@ -92,7 +101,12 @@ public class QProfileRulesImpl implements QProfileRules {
@Override
public BulkChangeResult bulkDeactivateAndCommit(DbSession dbSession, QProfileDto profile, RuleQuery ruleQuery) {
verifyNotBuiltIn(profile);
- return doBulk(dbSession, profile, ruleQuery, (context, ruleDefinition) -> ruleActivator.deactivate(dbSession, context, ruleDefinition.getUuid(), false));
+ BulkChangeResult bulkChangeResult = doBulk(dbSession, profile, ruleQuery, (context, ruleDefinition) ->
+ ruleActivator.deactivate(dbSession, context, ruleDefinition.getUuid(), false));
+
+ qualityProfileChangeEventService.distributeRuleChangeEvent(List.of(profile), bulkChangeResult.getChanges(), profile.getLanguage());
+
+ return bulkChangeResult;
}
@Override
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/QProfileTreeImpl.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/QProfileTreeImpl.java
index b38f695d8f8..d315e15b2cd 100644
--- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/QProfileTreeImpl.java
+++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/QProfileTreeImpl.java
@@ -41,12 +41,14 @@ public class QProfileTreeImpl implements QProfileTree {
private final RuleActivator ruleActivator;
private final System2 system2;
private final ActiveRuleIndexer activeRuleIndexer;
+ private final QualityProfileChangeEventService qualityProfileChangeEventService;
- public QProfileTreeImpl(DbClient db, RuleActivator ruleActivator, System2 system2, ActiveRuleIndexer activeRuleIndexer) {
+ public QProfileTreeImpl(DbClient db, RuleActivator ruleActivator, System2 system2, ActiveRuleIndexer activeRuleIndexer, QualityProfileChangeEventService qualityProfileChangeEventService) {
this.db = db;
this.ruleActivator = ruleActivator;
this.system2 = system2;
this.activeRuleIndexer = activeRuleIndexer;
+ this.qualityProfileChangeEventService = qualityProfileChangeEventService;
}
@Override
@@ -92,6 +94,7 @@ public class QProfileTreeImpl implements QProfileTree {
// TODO return errors
}
}
+ qualityProfileChangeEventService.distributeRuleChangeEvent(List.of(profile), changes, profile.getLanguage());
return changes;
}
@@ -120,6 +123,8 @@ public class QProfileTreeImpl implements QProfileTree {
changes.add(new ActiveRuleChange(ActiveRuleChange.Type.UPDATED, activeRule, context.getRule().get()).setInheritance(null));
}
}
+
+ qualityProfileChangeEventService.distributeRuleChangeEvent(List.of(profile), changes, profile.getLanguage());
return changes;
}
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/ws/AddProjectAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/ws/AddProjectAction.java
index 60646999ff5..202758dc446 100644
--- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/ws/AddProjectAction.java
+++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/ws/AddProjectAction.java
@@ -19,6 +19,7 @@
*/
package org.sonar.server.qualityprofile.ws;
+import java.util.Optional;
import org.sonar.api.resources.Languages;
import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.Response;
@@ -30,8 +31,11 @@ import org.sonar.db.DbSession;
import org.sonar.db.project.ProjectDto;
import org.sonar.db.qualityprofile.QProfileDto;
import org.sonar.server.component.ComponentFinder;
+import org.sonar.server.qualityprofile.QualityProfileChangeEventService;
import org.sonar.server.user.UserSession;
+import static java.util.Optional.empty;
+import static java.util.Optional.of;
import static org.sonar.server.user.AbstractUserSession.insufficientPrivilegesException;
import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;
import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.ACTION_ADD_PROJECT;
@@ -44,13 +48,16 @@ public class AddProjectAction implements QProfileWsAction {
private final Languages languages;
private final ComponentFinder componentFinder;
private final QProfileWsSupport wsSupport;
+ private final QualityProfileChangeEventService qualityProfileChangeEventService;
- public AddProjectAction(DbClient dbClient, UserSession userSession, Languages languages, ComponentFinder componentFinder, QProfileWsSupport wsSupport) {
+ public AddProjectAction(DbClient dbClient, UserSession userSession, Languages languages, ComponentFinder componentFinder,
+ QProfileWsSupport wsSupport, QualityProfileChangeEventService qualityProfileChangeEventService) {
this.dbClient = dbClient;
this.userSession = userSession;
this.languages = languages;
this.componentFinder = componentFinder;
this.wsSupport = wsSupport;
+ this.qualityProfileChangeEventService = qualityProfileChangeEventService;
}
@Override
@@ -80,24 +87,36 @@ public class AddProjectAction implements QProfileWsAction {
userSession.checkLoggedIn();
try (DbSession dbSession = dbClient.openSession(false)) {
+
ProjectDto project = loadProject(dbSession, request);
QProfileDto profile = wsSupport.getProfile(dbSession, QProfileReference.fromName(request));
checkPermissions(dbSession, profile, project);
-
QProfileDto currentProfile = dbClient.qualityProfileDao().selectAssociatedToProjectAndLanguage(dbSession, project, profile.getLanguage());
+
+ Optional<QProfileDto> deactivatedProfile = empty();
+
if (currentProfile == null) {
+ QProfileDto defaultProfile = dbClient.qualityProfileDao().selectDefaultProfile(dbSession, profile.getLanguage());
+ if (defaultProfile != null) {
+ deactivatedProfile = of(defaultProfile);
+ }
+
// project uses the default profile
dbClient.qualityProfileDao().insertProjectProfileAssociation(dbSession, project, profile);
dbSession.commit();
} else if (!profile.getKee().equals(currentProfile.getKee())) {
+ deactivatedProfile = of(currentProfile);
dbClient.qualityProfileDao().updateProjectProfileAssociation(dbSession, project, profile.getKee(), currentProfile.getKee());
dbSession.commit();
}
+ Optional<QProfileDto> activatedProfile = of(profile);
+ qualityProfileChangeEventService.publishRuleActivationToSonarLintClients(project, activatedProfile, deactivatedProfile);
}
response.noContent();
}
+
private ProjectDto loadProject(DbSession dbSession, Request request) {
String projectKey = request.mandatoryParam(PARAM_PROJECT);
return componentFinder.getProjectByKey(dbSession, projectKey);
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/ws/RemoveProjectAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/ws/RemoveProjectAction.java
index 4ddf8ced7a4..81c7987dc5b 100644
--- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/ws/RemoveProjectAction.java
+++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/ws/RemoveProjectAction.java
@@ -19,6 +19,7 @@
*/
package org.sonar.server.qualityprofile.ws;
+import java.util.Optional;
import org.sonar.api.resources.Languages;
import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.Response;
@@ -30,8 +31,11 @@ import org.sonar.db.DbSession;
import org.sonar.db.project.ProjectDto;
import org.sonar.db.qualityprofile.QProfileDto;
import org.sonar.server.component.ComponentFinder;
+import org.sonar.server.qualityprofile.QualityProfileChangeEventService;
import org.sonar.server.user.UserSession;
+import static java.util.Optional.empty;
+import static java.util.Optional.of;
import static org.sonar.server.user.AbstractUserSession.insufficientPrivilegesException;
import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;
import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.ACTION_REMOVE_PROJECT;
@@ -44,13 +48,16 @@ public class RemoveProjectAction implements QProfileWsAction {
private final Languages languages;
private final ComponentFinder componentFinder;
private final QProfileWsSupport wsSupport;
+ private final QualityProfileChangeEventService qualityProfileChangeEventService;
- public RemoveProjectAction(DbClient dbClient, UserSession userSession, Languages languages, ComponentFinder componentFinder, QProfileWsSupport wsSupport) {
+ public RemoveProjectAction(DbClient dbClient, UserSession userSession, Languages languages, ComponentFinder componentFinder,
+ QProfileWsSupport wsSupport, QualityProfileChangeEventService qualityProfileChangeEventService) {
this.dbClient = dbClient;
this.userSession = userSession;
this.languages = languages;
this.componentFinder = componentFinder;
this.wsSupport = wsSupport;
+ this.qualityProfileChangeEventService = qualityProfileChangeEventService;
}
@Override
@@ -86,6 +93,17 @@ public class RemoveProjectAction implements QProfileWsAction {
dbClient.qualityProfileDao().deleteProjectProfileAssociation(dbSession, project, profile);
dbSession.commit();
+ Optional<QProfileDto> deactivatedProfile = of(profile);
+ Optional<QProfileDto> activatedProfile = empty();
+
+ // publish change for rules in the default quality profile
+ QProfileDto defaultProfile = dbClient.qualityProfileDao().selectDefaultProfile(dbSession, profile.getLanguage());
+ if (defaultProfile != null) {
+ activatedProfile = of(defaultProfile);
+ }
+
+ qualityProfileChangeEventService.publishRuleActivationToSonarLintClients(project, activatedProfile, deactivatedProfile);
+
response.noContent();
}
}
diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/QProfileComparisonTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/QProfileComparisonTest.java
index 2f410c9e96b..3254844f7be 100644
--- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/QProfileComparisonTest.java
+++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/QProfileComparisonTest.java
@@ -49,6 +49,7 @@ import org.sonar.server.util.TypeValidations;
import static java.util.Collections.singleton;
import static java.util.Collections.singletonList;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
public class QProfileComparisonTest {
@@ -74,8 +75,9 @@ public class QProfileComparisonTest {
dbSession = db.openSession(false);
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);
- qProfileRules = new QProfileRulesImpl(db, ruleActivator, ruleIndex, activeRuleIndexer);
+ qProfileRules = new QProfileRulesImpl(db, ruleActivator, ruleIndex, activeRuleIndexer, qualityProfileChangeEventService);
comparison = new QProfileComparison(db);
xooRule1 = RuleTesting.newXooX1().setSeverity("MINOR").getDefinition();
diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/QProfileResetImplTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/QProfileResetImplTest.java
index ef1df8f5be6..08f2b22255a 100644
--- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/QProfileResetImplTest.java
+++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/QProfileResetImplTest.java
@@ -41,7 +41,12 @@ import static java.util.Collections.singletonList;
import static org.assertj.core.api.Assertions.assertThat;
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.verify;
+import static org.mockito.Mockito.verifyNoInteractions;
+import static org.mockito.internal.verification.VerificationModeFactory.times;
import static org.sonar.server.qualityprofile.ActiveRuleChange.Type.ACTIVATED;
public class QProfileResetImplTest {
@@ -55,11 +60,12 @@ public class QProfileResetImplTest {
private System2 system2 = new AlwaysIncreasingSystem2();
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 QProfileTree qProfileTree = new QProfileTreeImpl(db.getDbClient(), ruleActivator, system2, activeRuleIndexer);
- private QProfileRules qProfileRules = new QProfileRulesImpl(db.getDbClient(), ruleActivator, null, activeRuleIndexer);
- private QProfileResetImpl underTest = new QProfileResetImpl(db.getDbClient(), ruleActivator, activeRuleIndexer);
+ 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));
@Test
public void reset() {
@@ -77,6 +83,7 @@ public class QProfileResetImplTest {
assertThat(result.getChanges())
.extracting(ActiveRuleChange::getKey, ActiveRuleChange::getType)
.containsExactlyInAnyOrder(tuple(ActiveRuleKey.of(profile, newRule.getKey()), ACTIVATED));
+ verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), any(), eq(profile.getLanguage()));
}
@Test
@@ -94,6 +101,7 @@ public class QProfileResetImplTest {
assertThat(db.getDbClient().activeRuleDao().selectByProfile(db.getSession(), childProfile))
.extracting(OrgActiveRuleDto::getRuleKey)
.containsExactlyInAnyOrder(newRule.getKey(), existingRule.getKey());
+ verify(qualityProfileChangeEventService, times(2)).distributeRuleChangeEvent(any(), any(), eq(childProfile.getLanguage()));
}
@Test
@@ -106,6 +114,7 @@ public class QProfileResetImplTest {
})
.isInstanceOf(IllegalArgumentException.class)
.hasMessage(String.format("Operation forbidden for built-in Quality Profile '%s'", profile.getKee()));
+ verifyNoInteractions(qualityProfileChangeEventService);
}
@Test
@@ -118,5 +127,7 @@ public class QProfileResetImplTest {
})
.isInstanceOf(NullPointerException.class)
.hasMessage("Quality profile must be persisted");
+
+ verifyNoInteractions(qualityProfileChangeEventService);
}
}
diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/QProfileRuleImplTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/QProfileRuleImplTest.java
index 21e8a820c8c..e1479fcb1e4 100644
--- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/QProfileRuleImplTest.java
+++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/QProfileRuleImplTest.java
@@ -61,6 +61,11 @@ import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+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.sonar.api.rule.Severity.BLOCKER;
import static org.sonar.api.rule.Severity.CRITICAL;
import static org.sonar.api.rule.Severity.MAJOR;
@@ -81,9 +86,10 @@ public class QProfileRuleImplTest {
private ActiveRuleIndexer activeRuleIndexer = new ActiveRuleIndexer(db.getDbClient(), es.client());
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 RuleActivator ruleActivator = new RuleActivator(system2, db.getDbClient(), typeValidations, userSession);
- private QProfileRules underTest = new QProfileRulesImpl(db.getDbClient(), ruleActivator, ruleIndex, activeRuleIndexer);
+ private QProfileRules underTest = new QProfileRulesImpl(db.getDbClient(), ruleActivator, ruleIndex, activeRuleIndexer, qualityProfileChangeEventService);
@Test
public void system_activates_rule_without_parameters() {
@@ -94,6 +100,7 @@ public class QProfileRuleImplTest {
assertThatRuleIsActivated(profile, rule, changes, BLOCKER, null, emptyMap());
assertThatProfileIsUpdatedBySystem(profile);
+ verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), any(), eq(profile.getLanguage()));
}
@Test
@@ -106,6 +113,7 @@ public class QProfileRuleImplTest {
assertThatRuleIsActivated(profile, rule, changes, BLOCKER, null, emptyMap());
assertThatProfileIsUpdatedByUser(profile);
+ verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), any(), eq(profile.getLanguage()));
}
@Test
@@ -119,6 +127,7 @@ public class QProfileRuleImplTest {
assertThatRuleIsActivated(profile, rule, changes, rule.getSeverityString(), null, of("min", "10"));
assertThatProfileIsUpdatedBySystem(profile);
+ verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), any(), eq(profile.getLanguage()));
}
@Test
@@ -132,6 +141,7 @@ public class QProfileRuleImplTest {
assertThatRuleIsActivated(profile, rule, changes, rule.getSeverityString(), null, of("min", "15"));
assertThatProfileIsUpdatedBySystem(profile);
+ verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), any(), eq(profile.getLanguage()));
}
@Test
@@ -144,6 +154,7 @@ public class QProfileRuleImplTest {
assertThatRuleIsActivated(profile, rule, changes, rule.getSeverityString(), null, emptyMap());
assertThatProfileIsUpdatedBySystem(profile);
+ verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), any(), eq(profile.getLanguage()));
}
/**
@@ -160,6 +171,7 @@ public class QProfileRuleImplTest {
assertThatRuleIsActivated(profile, rule, changes, rule.getSeverityString(), null, of("min", "10"));
assertThatProfileIsUpdatedBySystem(profile);
+ verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), any(), eq(profile.getLanguage()));
}
/**
@@ -179,6 +191,7 @@ public class QProfileRuleImplTest {
assertThatRuleIsActivated(profile, rule, changes, rule.getSeverityString(), null,
of(paramWithoutDefault.getName(), "-10", paramWithDefault.getName(), paramWithDefault.getDefaultValue()));
assertThatProfileIsUpdatedBySystem(profile);
+ verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), any(), eq(profile.getLanguage()));
}
@Test
@@ -192,6 +205,7 @@ public class QProfileRuleImplTest {
assertThatRuleIsActivated(profile, rule, changes, rule.getSeverityString(), null, of(param.getName(), param.getDefaultValue()));
assertThatProfileIsUpdatedBySystem(profile);
+ verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), any(), eq(profile.getLanguage()));
}
@Test
@@ -202,14 +216,16 @@ public class QProfileRuleImplTest {
// initial activation
RuleActivation activation = RuleActivation.create(rule.getUuid(), MAJOR, null);
- activate(profile, activation);
+ List<ActiveRuleChange> changes = activate(profile, activation);
+ verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(profile.getLanguage()));
// update
RuleActivation updateActivation = RuleActivation.create(rule.getUuid(), CRITICAL, of(param.getName(), "20"));
- List<ActiveRuleChange> changes = activate(profile, updateActivation);
+ changes = activate(profile, updateActivation);
assertThatRuleIsUpdated(profile, rule, CRITICAL, null, of(param.getName(), "20"));
assertThatProfileIsUpdatedBySystem(profile);
+ verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(profile.getLanguage()));
}
@Test
@@ -221,14 +237,16 @@ public class QProfileRuleImplTest {
// initial activation -> param "max" has a default value
RuleActivation activation = RuleActivation.create(rule.getUuid());
- activate(profile, activation);
+ List<ActiveRuleChange> changes = activate(profile, activation);
// update param "min", which has no default value
RuleActivation updateActivation = RuleActivation.create(rule.getUuid(), MAJOR, of(paramWithoutDefault.getName(), "3"));
- List<ActiveRuleChange> changes = activate(profile, updateActivation);
+ 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"));
assertThatProfileIsUpdatedBySystem(profile);
+ verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(profile.getLanguage()));
}
@Test
@@ -239,14 +257,16 @@ public class QProfileRuleImplTest {
// initial activation -> param "max" has a default value
RuleActivation activation = RuleActivation.create(rule.getUuid(), null, of(paramWithDefault.getName(), "20"));
- activate(profile, activation);
+ List<ActiveRuleChange> changes = activate(profile, activation);
+ verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(profile.getLanguage()));
// reset to default_value
RuleActivation updateActivation = RuleActivation.create(rule.getUuid(), null, of(paramWithDefault.getName(), ""));
- List<ActiveRuleChange> changes = activate(profile, updateActivation);
+ changes = activate(profile, updateActivation);
assertThatRuleIsUpdated(profile, rule, rule.getSeverityString(), null, of(paramWithDefault.getName(), "10"));
assertThat(changes).hasSize(1);
+ verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(profile.getLanguage()));
}
@Test
@@ -258,14 +278,16 @@ public class QProfileRuleImplTest {
// initial activation -> param "max" has a default value
RuleActivation activation = RuleActivation.create(rule.getUuid(), null, of(paramWithoutDefault.getName(), "20"));
- activate(profile, activation);
+ List<ActiveRuleChange> changes = activate(profile, activation);
+ verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(profile.getLanguage()));
// remove parameter
RuleActivation updateActivation = RuleActivation.create(rule.getUuid(), null, of(paramWithoutDefault.getName(), ""));
- List<ActiveRuleChange> changes = activate(profile, updateActivation);
+ changes = activate(profile, updateActivation);
assertThatRuleIsUpdated(profile, rule, rule.getSeverityString(), null, of(paramWithDefault.getName(), paramWithDefault.getDefaultValue()));
assertThat(changes).hasSize(1);
+ verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(profile.getLanguage()));
}
@Test
@@ -279,6 +301,7 @@ public class QProfileRuleImplTest {
List<ActiveRuleChange> changes = activate(profile, activation);
db.getDbClient().activeRuleDao().deleteParametersByRuleProfileUuids(db.getSession(), asList(profile.getRulesProfileUuid()));
assertThatRuleIsActivated(profile, rule, changes, rule.getSeverityString(), 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(), ""));
@@ -286,6 +309,7 @@ public class QProfileRuleImplTest {
assertThatRuleIsUpdated(profile, rule, rule.getSeverityString(), null, of(param.getName(), param.getDefaultValue()));
assertThat(changes).hasSize(1);
+ verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(profile.getLanguage()));
}
@Test
@@ -295,13 +319,15 @@ public class QProfileRuleImplTest {
// initial activation
RuleActivation activation = RuleActivation.create(rule.getUuid());
- activate(profile, activation);
+ List<ActiveRuleChange> changes = activate(profile, activation);
+ verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(profile.getLanguage()));
// update with exactly the same severity and params
activation = RuleActivation.create(rule.getUuid());
- List<ActiveRuleChange> changes = activate(profile, activation);
+ changes = activate(profile, activation);
assertThat(changes).isEmpty();
+ verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(profile.getLanguage()));
}
@Test
@@ -312,13 +338,16 @@ public class QProfileRuleImplTest {
// initial activation -> param "max" has a default value
RuleActivation activation = RuleActivation.create(rule.getUuid(), BLOCKER, of(param.getName(), "20"));
- activate(profile, activation);
+ 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());
- List<ActiveRuleChange> changes = activate(profile, update);
+ changes = activate(profile, update);
assertThat(changes).isEmpty();
+ verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(profile.getLanguage()));
}
@Test
@@ -328,6 +357,7 @@ public class QProfileRuleImplTest {
RuleActivation activation = RuleActivation.create(rule.getUuid());
expectFailure("java rule " + rule.getKey() + " cannot be activated on js profile " + profile.getKee(), () -> activate(profile, activation));
+ verifyNoInteractions(qualityProfileChangeEventService);
}
@Test
@@ -337,6 +367,7 @@ public class QProfileRuleImplTest {
RuleActivation activation = RuleActivation.create(rule.getUuid());
expectFailure("Rule was removed: " + rule.getKey(), () -> activate(profile, activation));
+ verifyNoInteractions(qualityProfileChangeEventService);
}
@Test
@@ -346,6 +377,7 @@ public class QProfileRuleImplTest {
RuleActivation activation = RuleActivation.create(rule.getUuid());
expectFailure("Rule template can't be activated on a Quality profile: " + rule.getKey(), () -> activate(profile, activation));
+ verifyNoInteractions(qualityProfileChangeEventService);
}
@Test
@@ -356,6 +388,7 @@ public class QProfileRuleImplTest {
RuleActivation activation = RuleActivation.create(rule.getUuid(), null, of(param.getName(), "foo"));
expectFailure("Value 'foo' must be an integer.", () -> activate(profile, activation));
+ verifyNoInteractions(qualityProfileChangeEventService);
}
@Test
@@ -368,13 +401,15 @@ public class QProfileRuleImplTest {
// initial activation
RuleActivation activation = RuleActivation.create(customRule.getUuid(), MAJOR, emptyMap());
- activate(profile, activation);
+ List<ActiveRuleChange> changes = activate(profile, activation);
assertThatRuleIsActivated(profile, customRule, null, MAJOR, 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"));
- activate(profile, updateActivation);
+ changes = activate(profile, updateActivation);
assertThatRuleIsActivated(profile, customRule, null, BLOCKER, null, of("format", "txt"));
+ verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(profile.getLanguage()));
}
@Test
@@ -383,13 +418,15 @@ public class QProfileRuleImplTest {
RuleDefinitionDto rule = createRule();
QProfileDto profile = createProfile(rule);
RuleActivation activation = RuleActivation.create(rule.getUuid());
- activate(profile, activation);
+ List<ActiveRuleChange> changes = activate(profile, activation);
+ verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(profile.getLanguage()));
- List<ActiveRuleChange> changes = deactivate(profile, rule);
+ changes = deactivate(profile, rule);
verifyNoActiveRules();
assertThatProfileIsUpdatedByUser(profile);
assertThat(changes).hasSize(1);
assertThat(changes.get(0).getType()).isEqualTo(ActiveRuleChange.Type.DEACTIVATED);
+ verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(profile.getLanguage()));
}
@Test
@@ -397,12 +434,14 @@ public class QProfileRuleImplTest {
RuleDefinitionDto rule = createRule();
QProfileDto profile = createProfile(rule);
RuleActivation activation = RuleActivation.create(rule.getUuid());
- activate(profile, activation);
+ List<ActiveRuleChange> changes = activate(profile, activation);
+ verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(profile.getLanguage()));
- List<ActiveRuleChange> changes = deactivate(profile, rule);
+ changes = deactivate(profile, rule);
verifyNoActiveRules();
assertThatProfileIsUpdatedBySystem(profile);
assertThatChangeIsDeactivation(changes, rule);
+ verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(profile.getLanguage()));
}
private void assertThatChangeIsDeactivation(List<ActiveRuleChange> changes, RuleDefinitionDto rule) {
@@ -420,6 +459,7 @@ public class QProfileRuleImplTest {
List<ActiveRuleChange> changes = deactivate(profile, rule);
verifyNoActiveRules();
assertThat(changes).isEmpty();
+ verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), any(), eq(profile.getLanguage()));
}
@Test
@@ -427,14 +467,16 @@ public class QProfileRuleImplTest {
RuleDefinitionDto rule = createRule();
QProfileDto profile = createProfile(rule);
RuleActivation activation = RuleActivation.create(rule.getUuid());
- activate(profile, activation);
+ List<ActiveRuleChange> changes = activate(profile, activation);
+ verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(profile.getLanguage()));
rule.setStatus(RuleStatus.REMOVED);
db.getDbClient().ruleDao().update(db.getSession(), rule);
- List<ActiveRuleChange> changes = deactivate(profile, rule);
+ changes = deactivate(profile, rule);
verifyNoActiveRules();
assertThatChangeIsDeactivation(changes, rule);
+ verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(profile.getLanguage()));
}
@Test
@@ -448,6 +490,7 @@ public class QProfileRuleImplTest {
assertThatProfileHasNoActiveRules(parentProfile);
assertThatRuleIsActivated(childProfile, rule, changes, rule.getSeverityString(), null, emptyMap());
assertThatRuleIsActivated(grandChildProfile, rule, changes, rule.getSeverityString(), INHERITED, emptyMap());
+ verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), any(), eq(childProfile.getLanguage()));
}
@Test
@@ -460,17 +503,20 @@ public class QProfileRuleImplTest {
System.out.println("ACTIVATE ON " + childProfile.getName());
RuleActivation initialActivation = RuleActivation.create(rule.getUuid(), MAJOR, of(param.getName(), "foo"));
- activate(childProfile, initialActivation);
+ List<ActiveRuleChange> changes = activate(childProfile, initialActivation);
+ verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(childProfile.getLanguage()));
System.out.println("---------------");
System.out.println("ACTIVATE ON " + childProfile.getName());
RuleActivation updateActivation = RuleActivation.create(rule.getUuid(), CRITICAL, of(param.getName(), "bar"));
- List<ActiveRuleChange> changes = activate(childProfile, updateActivation);
+ changes = activate(childProfile, updateActivation);
+ 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"));
assertThat(changes).hasSize(2);
+ verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(childProfile.getLanguage()));
}
@Test
@@ -482,15 +528,17 @@ public class QProfileRuleImplTest {
QProfileDto grandChildProfile = createChildProfile(childProfile);
RuleActivation initialActivation = RuleActivation.create(rule.getUuid(), MAJOR, of(param.getName(), "foo"));
- activate(childProfile, initialActivation);
+ List<ActiveRuleChange> changes = activate(childProfile, initialActivation);
+ verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(childProfile.getLanguage()));
RuleActivation overrideActivation = RuleActivation.create(rule.getUuid(), CRITICAL, of(param.getName(), "bar"));
- List<ActiveRuleChange> changes = activate(grandChildProfile, overrideActivation);
+ changes = activate(grandChildProfile, overrideActivation);
assertThatProfileHasNoActiveRules(parentProfile);
assertThatRuleIsUpdated(childProfile, rule, MAJOR, null, of(param.getName(), "foo"));
assertThatRuleIsUpdated(grandChildProfile, rule, CRITICAL, ActiveRuleInheritance.OVERRIDES, of(param.getName(), "bar"));
assertThat(changes).hasSize(1);
+ verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(childProfile.getLanguage()));
}
@Test
@@ -502,19 +550,22 @@ public class QProfileRuleImplTest {
QProfileDto grandChildProfile = createChildProfile(childProfile);
RuleActivation initialActivation = RuleActivation.create(rule.getUuid(), MAJOR, of(param.getName(), "foo"));
- activate(childProfile, initialActivation);
+ List<ActiveRuleChange> changes = activate(childProfile, initialActivation);
+ verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(childProfile.getLanguage()));
RuleActivation overrideActivation = RuleActivation.create(rule.getUuid(), CRITICAL, of(param.getName(), "bar"));
- activate(grandChildProfile, overrideActivation);
+ changes = activate(grandChildProfile, overrideActivation);
+ verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(grandChildProfile.getLanguage()));
// update child --> do not touch grandChild
RuleActivation updateActivation = RuleActivation.create(rule.getUuid(), BLOCKER, of(param.getName(), "baz"));
- List<ActiveRuleChange> changes = activate(childProfile, updateActivation);
+ changes = activate(childProfile, updateActivation);
assertThatProfileHasNoActiveRules(parentProfile);
assertThatRuleIsUpdated(childProfile, rule, BLOCKER, null, of(param.getName(), "baz"));
assertThatRuleIsUpdated(grandChildProfile, rule, CRITICAL, ActiveRuleInheritance.OVERRIDES, of(param.getName(), "bar"));
assertThat(changes).hasSize(1);
+ verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(childProfile.getLanguage()));
}
@Test
@@ -526,19 +577,22 @@ public class QProfileRuleImplTest {
QProfileDto grandChildProfile = createChildProfile(childProfile);
RuleActivation initialActivation = RuleActivation.create(rule.getUuid(), MAJOR, of(param.getName(), "foo"));
- activate(parentProfile, initialActivation);
+ List<ActiveRuleChange> changes = activate(parentProfile, initialActivation);
+ verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(parentProfile.getLanguage()));
RuleActivation overrideActivation = RuleActivation.create(rule.getUuid(), CRITICAL, of(param.getName(), "bar"));
- activate(grandChildProfile, overrideActivation);
+ changes = activate(grandChildProfile, overrideActivation);
+ verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(grandChildProfile.getLanguage()));
// reset parent --> touch child but not grandChild
RuleActivation updateActivation = RuleActivation.createReset(rule.getUuid());
- List<ActiveRuleChange> changes = activate(parentProfile, updateActivation);
+ changes = activate(parentProfile, updateActivation);
assertThatRuleIsUpdated(parentProfile, rule, rule.getSeverityString(), null, of(param.getName(), param.getDefaultValue()));
assertThatRuleIsUpdated(childProfile, rule, rule.getSeverityString(), INHERITED, of(param.getName(), param.getDefaultValue()));
assertThatRuleIsUpdated(grandChildProfile, rule, CRITICAL, ActiveRuleInheritance.OVERRIDES, of(param.getName(), "bar"));
assertThat(changes).hasSize(2);
+ verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(parentProfile.getLanguage()));
}
@Test
@@ -549,14 +603,16 @@ public class QProfileRuleImplTest {
QProfileDto childProfile = createChildProfile(parentProfile);
RuleActivation childActivation = RuleActivation.create(rule.getUuid(), MAJOR, of(param.getName(), "foo"));
- activate(childProfile, childActivation);
+ 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"));
- List<ActiveRuleChange> changes = activate(parentProfile, parentActivation);
+ changes = activate(parentProfile, parentActivation);
assertThatRuleIsUpdated(parentProfile, rule, CRITICAL, null, of(param.getName(), "bar"));
assertThatRuleIsUpdated(childProfile, rule, MAJOR, ActiveRuleInheritance.OVERRIDES, of(param.getName(), "foo"));
assertThat(changes).hasSize(2);
+ verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(parentProfile.getLanguage()));
}
@Test
@@ -567,13 +623,15 @@ public class QProfileRuleImplTest {
QProfileDto childProfile = createChildProfile(parentProfile);
RuleActivation parentActivation = RuleActivation.create(rule.getUuid(), MAJOR, of(param.getName(), "foo"));
- activate(parentProfile, parentActivation);
+ List<ActiveRuleChange> changes = activate(parentProfile, parentActivation);
+ verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(parentProfile.getLanguage()));
RuleActivation overrideActivation = RuleActivation.create(rule.getUuid(), MAJOR, of(param.getName(), "foo"));
- List<ActiveRuleChange> changes = activate(childProfile, overrideActivation);
+ changes = activate(childProfile, overrideActivation);
assertThatRuleIsUpdated(childProfile, rule, MAJOR, INHERITED, of(param.getName(), "foo"));
assertThat(changes).isEmpty();
+ verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(childProfile.getLanguage()));
}
@Test
@@ -586,11 +644,13 @@ public class QProfileRuleImplTest {
List<ActiveRuleChange> changes = activate(parentProfile, activation);
assertThatRuleIsActivated(parentProfile, rule, changes, rule.getSeverityString(), null, emptyMap());
assertThatRuleIsActivated(childProfile, rule, changes, rule.getSeverityString(), INHERITED, emptyMap());
+ verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(parentProfile.getLanguage()));
changes = deactivate(parentProfile, rule);
assertThatProfileHasNoActiveRules(parentProfile);
assertThatProfileHasNoActiveRules(childProfile);
assertThat(changes).hasSize(2);
+ verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(parentProfile.getLanguage()));
}
@Test
@@ -603,14 +663,17 @@ public class QProfileRuleImplTest {
List<ActiveRuleChange> changes = activate(parentProfile, activation);
assertThatRuleIsActivated(parentProfile, rule, changes, rule.getSeverityString(), null, emptyMap());
assertThatRuleIsActivated(childProfile, rule, changes, rule.getSeverityString(), INHERITED, emptyMap());
+ verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(parentProfile.getLanguage()));
activation = RuleActivation.create(rule.getUuid(), CRITICAL, null);
- activate(childProfile, activation);
+ changes = activate(childProfile, activation);
+ verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(childProfile.getLanguage()));
changes = deactivate(parentProfile, rule);
assertThatProfileHasNoActiveRules(parentProfile);
assertThatProfileHasNoActiveRules(childProfile);
assertThat(changes).hasSize(2);
+ verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(parentProfile.getLanguage()));
}
@Test
@@ -627,6 +690,7 @@ public class QProfileRuleImplTest {
assertThatThrownBy(() -> deactivate(childProfile, rule))
.isInstanceOf(BadRequestException.class)
.hasMessageContaining("Cannot deactivate inherited rule");
+ verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), any(), eq(parentProfile.getLanguage()));
}
@Test
@@ -651,6 +715,7 @@ public class QProfileRuleImplTest {
assertThatRuleIsUpdated(childProfile, rule, CRITICAL, INHERITED, emptyMap());
assertThatRuleIsUpdated(parentProfile, rule, CRITICAL, null, emptyMap());
assertThat(changes).hasSize(1);
+
}
@Test
diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/QProfileRulesImplTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/QProfileRulesImplTest.java
index 7d08990ec71..1384f1d6705 100644
--- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/QProfileRulesImplTest.java
+++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/QProfileRulesImplTest.java
@@ -43,6 +43,10 @@ import static java.util.Collections.singleton;
import static java.util.Collections.singletonList;
import static org.assertj.core.api.Assertions.assertThat;
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.verify;
public class QProfileRulesImplTest {
@@ -55,9 +59,11 @@ public class QProfileRulesImplTest {
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);
+ private RuleActivator ruleActivator = new RuleActivator(System2.INSTANCE, db.getDbClient(), new TypeValidations(singletonList(new IntegerTypeValidation())),
+ userSession);
+ private QualityProfileChangeEventService qualityProfileChangeEventService = mock(QualityProfileChangeEventService.class);
- private QProfileRules qProfileRules = new QProfileRulesImpl(db.getDbClient(), ruleActivator, ruleIndex, activeRuleIndexer);
+ private QProfileRules qProfileRules = new QProfileRulesImpl(db.getDbClient(), ruleActivator, ruleIndex, activeRuleIndexer, qualityProfileChangeEventService);
@Test
public void activate_one_rule() {
@@ -70,6 +76,7 @@ public class QProfileRulesImplTest {
assertThat(db.getDbClient().activeRuleDao().selectByProfile(db.getSession(), qProfile))
.extracting(ActiveRuleDto::getRuleKey, ActiveRuleDto::getSeverityString)
.containsExactlyInAnyOrder(tuple(rule.getKey(), Severity.CRITICAL));
+ verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), any(), eq(qProfile.getLanguage()));
}
@Test
@@ -85,5 +92,6 @@ public class QProfileRulesImplTest {
assertThat(db.getDbClient().qProfileChangeDao().selectByQuery(db.getSession(), new QProfileChangeQuery(qProfile.getKee())))
.extracting(QProfileChangeDto::getUserUuid, QProfileChangeDto::getDataAsMap)
.containsExactlyInAnyOrder(tuple(user.getUuid(), ImmutableMap.of("ruleUuid", rule.getUuid(), "severity", Severity.CRITICAL)));
+ verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), any(), eq(qProfile.getLanguage()));
}
}
diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/QProfileTreeImplTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/QProfileTreeImplTest.java
index 513c1def2e4..ad5d09abcef 100644
--- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/QProfileTreeImplTest.java
+++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/QProfileTreeImplTest.java
@@ -47,6 +47,12 @@ import static java.util.Collections.emptyMap;
import static java.util.Collections.singleton;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyList;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
import static org.sonar.api.rule.Severity.BLOCKER;
import static org.sonar.server.qualityprofile.ActiveRuleInheritance.INHERITED;
@@ -61,9 +67,10 @@ public class QProfileTreeImplTest {
public UserSessionRule userSession = UserSessionRule.standalone();
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 QProfileRules qProfileRules = new QProfileRulesImpl(db.getDbClient(), ruleActivator, null, activeRuleIndexer);
- private QProfileTree underTest = new QProfileTreeImpl(db.getDbClient(), ruleActivator, System2.INSTANCE, activeRuleIndexer);
+ 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));
@Test
public void set_itself_as_parent_fails() {
@@ -133,11 +140,13 @@ public class QProfileTreeImplTest {
assertThat(changes).hasSize(1);
assertThatRuleIsActivated(profile2, rule1, changes, rule1.getSeverityString(), INHERITED, emptyMap());
assertThatRuleIsActivated(profile2, rule2, null, rule2.getSeverityString(), null, emptyMap());
+ verify(qualityProfileChangeEventService, times(2)).distributeRuleChangeEvent(any(), any(), eq(profile2.getLanguage()));
changes = underTest.removeParentAndCommit(db.getSession(), profile2);
assertThat(changes).hasSize(1);
assertThatRuleIsActivated(profile2, rule2, null, rule2.getSeverityString(), null, emptyMap());
assertThatRuleIsNotPresent(profile2, rule1);
+ verify(qualityProfileChangeEventService, times(2)).distributeRuleChangeEvent(any(), any(), eq(profile2.getLanguage()));
}
@Test
@@ -156,6 +165,7 @@ public class QProfileTreeImplTest {
assertThat(changes).hasSize(1);
assertThatRuleIsActivated(profile2, rule1, changes, rule1.getSeverityString(), INHERITED, emptyMap());
assertThatRuleIsActivated(profile2, rule2, null, rule2.getSeverityString(), null, emptyMap());
+ verify(qualityProfileChangeEventService, times(2)).distributeRuleChangeEvent(any(), any(), eq(profile2.getLanguage()));
RuleActivation activation = RuleActivation.create(rule1.getUuid(), BLOCKER, null);
changes = activate(profile2, activation);
@@ -168,6 +178,7 @@ public class QProfileTreeImplTest {
// Not testing changes here since severity is not set in changelog
assertThatRuleIsActivated(profile2, rule1, null, BLOCKER, null, emptyMap());
assertThatRuleIsActivated(profile2, rule2, null, rule2.getSeverityString(), null, emptyMap());
+ verify(qualityProfileChangeEventService, times(3)).distributeRuleChangeEvent(anyList(), any(), eq(profile2.getLanguage()));
}
@Test
@@ -183,6 +194,7 @@ public class QProfileTreeImplTest {
QProfileDto childProfile = createProfile(rule1);
List<ActiveRuleChange> changes = underTest.setParentAndCommit(db.getSession(), childProfile, parentProfile);
+ verify(qualityProfileChangeEventService, times(2)).distributeRuleChangeEvent(any(), any(), eq(childProfile.getLanguage()));
assertThatRuleIsNotPresent(childProfile, rule1);
assertThatRuleIsActivated(childProfile, rule2, changes, rule2.getSeverityString(), INHERITED, emptyMap());
diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/RegisterQualityProfilesNotificationTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/RegisterQualityProfilesNotificationTest.java
index cf3d32c7810..5a49e3e5b79 100644
--- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/RegisterQualityProfilesNotificationTest.java
+++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/RegisterQualityProfilesNotificationTest.java
@@ -90,12 +90,13 @@ public class RegisterQualityProfilesNotificationTest {
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);
private BuiltInQProfileInsert builtInQProfileInsert = new BuiltInQProfileInsertImpl(dbClient, ruleFinder, system2, UuidFactoryFast.getInstance(),
typeValidations, activeRuleIndexer);
private RuleActivator ruleActivator = new RuleActivator(system2, dbClient, typeValidations, userSessionRule);
- private QProfileRules qProfileRules = new QProfileRulesImpl(dbClient, ruleActivator, mock(RuleIndex.class), activeRuleIndexer);
- private BuiltInQProfileUpdate builtInQProfileUpdate = new BuiltInQProfileUpdateImpl(dbClient, ruleActivator, activeRuleIndexer);
+ 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 RegisterQualityProfiles underTest = new RegisterQualityProfiles(builtInQProfileRepositoryRule, dbClient,
builtInQProfileInsert, builtInQProfileUpdate, builtInQualityProfilesNotification, system2);
diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/ws/AddProjectActionTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/ws/AddProjectActionTest.java
index 010ee2997d0..ffed401c241 100644
--- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/ws/AddProjectActionTest.java
+++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/ws/AddProjectActionTest.java
@@ -36,14 +36,19 @@ import org.sonar.server.exceptions.ForbiddenException;
import org.sonar.server.exceptions.NotFoundException;
import org.sonar.server.exceptions.UnauthorizedException;
import org.sonar.server.language.LanguageTesting;
+import org.sonar.server.qualityprofile.QualityProfileChangeEventService;
import org.sonar.server.tester.UserSessionRule;
import org.sonar.server.ws.TestRequest;
import org.sonar.server.ws.TestResponse;
import org.sonar.server.ws.WsActionTester;
import static java.lang.String.format;
+import static java.util.Optional.empty;
+import static java.util.Optional.of;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
import static org.sonar.db.permission.GlobalPermission.ADMINISTER_QUALITY_PROFILES;
public class AddProjectActionTest {
@@ -59,7 +64,8 @@ public class AddProjectActionTest {
private final DbClient dbClient = db.getDbClient();
private final Languages languages = LanguageTesting.newLanguages(LANGUAGE_1, LANGUAGE_2);
private final QProfileWsSupport wsSupport = new QProfileWsSupport(dbClient, userSession);
- private final AddProjectAction underTest = new AddProjectAction(dbClient, userSession, languages, TestComponentFinder.from(db), wsSupport);
+ private QualityProfileChangeEventService qualityProfileChangeEventService = mock(QualityProfileChangeEventService.class);
+ private final AddProjectAction underTest = new AddProjectAction(dbClient, userSession, languages, TestComponentFinder.from(db), wsSupport, qualityProfileChangeEventService);
private final WsActionTester tester = new WsActionTester(underTest);
@Test
@@ -88,6 +94,7 @@ public class AddProjectActionTest {
assertThat(response.getStatus()).isEqualTo(HttpURLConnection.HTTP_NO_CONTENT);
assertProjectIsAssociatedToProfile(project, profile);
+ verify(qualityProfileChangeEventService).publishRuleActivationToSonarLintClients(project, of(profile), empty());
}
@Test
@@ -101,6 +108,7 @@ public class AddProjectActionTest {
call(project, qualityProfile);
assertProjectIsAssociatedToProfile(project, qualityProfile);
+ verify(qualityProfileChangeEventService).publishRuleActivationToSonarLintClients(project, of(qualityProfile), empty());
}
@Test
@@ -117,6 +125,7 @@ public class AddProjectActionTest {
assertProjectIsNotAssociatedToProfile(project, profile1);
assertProjectIsAssociatedToProfile(project, profile2);
+ verify(qualityProfileChangeEventService).publishRuleActivationToSonarLintClients(project, of(profile2), of(profile1));
}
@Test
@@ -132,6 +141,7 @@ public class AddProjectActionTest {
assertProjectIsAssociatedToProfile(project, profile3Language1);
assertProjectIsAssociatedToProfile(project, profile2Language2);
+ verify(qualityProfileChangeEventService).publishRuleActivationToSonarLintClients(project, of(profile3Language1), of(profile1Language1));
}
@Test
@@ -143,6 +153,7 @@ public class AddProjectActionTest {
call(project, profile);
assertProjectIsAssociatedToProfile(project, profile);
+ verify(qualityProfileChangeEventService).publishRuleActivationToSonarLintClients(project, of(profile), empty());
}
@Test
diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/ws/ChangeParentActionTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/ws/ChangeParentActionTest.java
index 7caf79e84ea..f2c2046719d 100644
--- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/ws/ChangeParentActionTest.java
+++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/ws/ChangeParentActionTest.java
@@ -49,6 +49,7 @@ import org.sonar.server.exceptions.BadRequestException;
import org.sonar.server.exceptions.ForbiddenException;
import org.sonar.server.language.LanguageTesting;
import org.sonar.server.qualityprofile.QProfileTreeImpl;
+import org.sonar.server.qualityprofile.QualityProfileChangeEventService;
import org.sonar.server.qualityprofile.RuleActivator;
import org.sonar.server.qualityprofile.index.ActiveRuleIndexer;
import org.sonar.server.rule.index.RuleIndex;
@@ -64,6 +65,7 @@ import static java.util.Arrays.asList;
import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.mockito.Mockito.mock;
import static org.sonar.db.permission.GlobalPermission.ADMINISTER_QUALITY_PROFILES;
import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_LANGUAGE;
import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_PARENT_QUALITY_PROFILE;
@@ -98,7 +100,7 @@ public class ChangeParentActionTest {
activeRuleIndexer = new ActiveRuleIndexer(dbClient, esClient);
TypeValidations typeValidations = new TypeValidations(Collections.emptyList());
RuleActivator ruleActivator = new RuleActivator(System2.INSTANCE, dbClient, typeValidations, userSession);
- qProfileTree = new QProfileTreeImpl(dbClient, ruleActivator, System2.INSTANCE, activeRuleIndexer);
+ qProfileTree = new QProfileTreeImpl(dbClient, ruleActivator, System2.INSTANCE, activeRuleIndexer, mock(QualityProfileChangeEventService.class));
ChangeParentAction underTest = new ChangeParentAction(
dbClient,
qProfileTree,
diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/ws/CreateActionTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/ws/CreateActionTest.java
index d71fe8bc706..51800d9ed99 100644
--- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/ws/CreateActionTest.java
+++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/ws/CreateActionTest.java
@@ -46,6 +46,7 @@ import org.sonar.server.qualityprofile.QProfileExporters;
import org.sonar.server.qualityprofile.QProfileFactoryImpl;
import org.sonar.server.qualityprofile.QProfileRules;
import org.sonar.server.qualityprofile.QProfileRulesImpl;
+import org.sonar.server.qualityprofile.QualityProfileChangeEventService;
import org.sonar.server.qualityprofile.RuleActivator;
import org.sonar.server.qualityprofile.index.ActiveRuleIndexer;
import org.sonar.server.rule.index.RuleIndex;
@@ -62,6 +63,7 @@ import org.sonarqube.ws.Qualityprofiles.CreateWsResponse.QualityProfile;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.mockito.Mockito.mock;
import static org.sonar.db.permission.GlobalPermission.ADMINISTER_QUALITY_PROFILES;
import static org.sonar.db.permission.GlobalPermission.SCAN;
import static org.sonar.server.language.LanguageTesting.newLanguages;
@@ -87,8 +89,9 @@ public class CreateActionTest {
private RuleIndexer ruleIndexer = new RuleIndexer(es.client(), dbClient);
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 QProfileRules qProfileRules = new QProfileRulesImpl(dbClient, ruleActivator, ruleIndex, activeRuleIndexer);
+ private QProfileRules qProfileRules = new QProfileRulesImpl(dbClient, ruleActivator, ruleIndex, activeRuleIndexer, qualityProfileChangeEventService);
private QProfileExporters qProfileExporters = new QProfileExporters(dbClient, null, qProfileRules, profileImporters);
private CreateAction underTest = new CreateAction(dbClient, new QProfileFactoryImpl(dbClient, UuidFactoryFast.getInstance(), System2.INSTANCE, activeRuleIndexer),
diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/ws/InheritanceActionTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/ws/InheritanceActionTest.java
index ff77547505b..8204ffa7563 100644
--- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/ws/InheritanceActionTest.java
+++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/ws/InheritanceActionTest.java
@@ -45,6 +45,7 @@ import org.sonar.server.qualityprofile.QProfileRules;
import org.sonar.server.qualityprofile.QProfileRulesImpl;
import org.sonar.server.qualityprofile.QProfileTree;
import org.sonar.server.qualityprofile.QProfileTreeImpl;
+import org.sonar.server.qualityprofile.QualityProfileChangeEventService;
import org.sonar.server.qualityprofile.RuleActivation;
import org.sonar.server.qualityprofile.RuleActivator;
import org.sonar.server.qualityprofile.index.ActiveRuleIndexer;
@@ -59,6 +60,7 @@ import static java.util.Arrays.asList;
import static java.util.Collections.singleton;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.mockito.Mockito.mock;
import static org.sonar.test.JsonAssert.assertJson;
import static org.sonarqube.ws.MediaTypes.PROTOBUF;
import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_LANGUAGE;
@@ -80,9 +82,10 @@ public class InheritanceActionTest {
private ActiveRuleIndexer activeRuleIndexer = new ActiveRuleIndexer(dbClient, esClient);
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 QProfileRules qProfileRules = new QProfileRulesImpl(dbClient, ruleActivator, ruleIndex, activeRuleIndexer);
- private QProfileTree qProfileTree = new QProfileTreeImpl(dbClient, ruleActivator, System2.INSTANCE, activeRuleIndexer);
+ private QProfileRules qProfileRules = new QProfileRulesImpl(dbClient, ruleActivator, ruleIndex, activeRuleIndexer, qualityProfileChangeEventService);
+ private QProfileTree qProfileTree = new QProfileTreeImpl(dbClient, ruleActivator, System2.INSTANCE, activeRuleIndexer, mock(QualityProfileChangeEventService.class));
private WsActionTester ws = new WsActionTester(new InheritanceAction(
dbClient,
diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/ws/QProfilesWsMediumTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/ws/QProfilesWsMediumTest.java
index 779ec6cbc8a..fc1a2256dc7 100644
--- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/ws/QProfilesWsMediumTest.java
+++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/ws/QProfilesWsMediumTest.java
@@ -43,6 +43,7 @@ import org.sonar.server.es.SearchOptions;
import org.sonar.server.exceptions.BadRequestException;
import org.sonar.server.qualityprofile.QProfileRules;
import org.sonar.server.qualityprofile.QProfileRulesImpl;
+import org.sonar.server.qualityprofile.QualityProfileChangeEventService;
import org.sonar.server.qualityprofile.RuleActivator;
import org.sonar.server.qualityprofile.index.ActiveRuleIndexer;
import org.sonar.server.rule.index.RuleIndex;
@@ -56,6 +57,7 @@ import org.sonar.server.ws.WsActionTester;
import static java.util.Collections.emptyList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_LANGUAGES;
import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_QPROFILE;
import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_KEY;
@@ -81,8 +83,9 @@ public class QProfilesWsMediumTest {
private final RuleIndexer ruleIndexer = new RuleIndexer(es.client(), dbClient);
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 QProfileRules qProfileRules = new QProfileRulesImpl(dbClient, ruleActivator, ruleIndex, activeRuleIndexer);
+ 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);
diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/ws/RemoveProjectActionTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/ws/RemoveProjectActionTest.java
index 2ace97fb69e..48d36e29b85 100644
--- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/ws/RemoveProjectActionTest.java
+++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/ws/RemoveProjectActionTest.java
@@ -22,6 +22,7 @@ package org.sonar.server.qualityprofile.ws;
import java.net.HttpURLConnection;
import org.junit.Rule;
import org.junit.Test;
+import org.mockito.Mockito;
import org.sonar.api.resources.Languages;
import org.sonar.api.resources.Qualifiers;
import org.sonar.api.server.ws.WebService;
@@ -38,14 +39,18 @@ import org.sonar.server.exceptions.ForbiddenException;
import org.sonar.server.exceptions.NotFoundException;
import org.sonar.server.exceptions.UnauthorizedException;
import org.sonar.server.language.LanguageTesting;
+import org.sonar.server.qualityprofile.QualityProfileChangeEventService;
import org.sonar.server.tester.UserSessionRule;
import org.sonar.server.ws.TestRequest;
import org.sonar.server.ws.TestResponse;
import org.sonar.server.ws.WsActionTester;
import static java.lang.String.format;
+import static java.util.Optional.empty;
+import static java.util.Optional.of;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.mockito.Mockito.verify;
import static org.sonar.db.permission.GlobalPermission.ADMINISTER_QUALITY_PROFILES;
public class RemoveProjectActionTest {
@@ -61,8 +66,9 @@ public class RemoveProjectActionTest {
private final Languages languages = LanguageTesting.newLanguages(LANGUAGE_1, LANGUAGE_2);
private final QProfileWsSupport wsSupport = new QProfileWsSupport(dbClient, userSession);
+ private final QualityProfileChangeEventService qualityProfileChangeEventService = Mockito.mock(QualityProfileChangeEventService.class);
private final RemoveProjectAction underTest = new RemoveProjectAction(dbClient, userSession, languages,
- new ComponentFinder(dbClient, new ResourceTypesRule().setRootQualifiers(Qualifiers.PROJECT)), wsSupport);
+ new ComponentFinder(dbClient, new ResourceTypesRule().setRootQualifiers(Qualifiers.PROJECT)), wsSupport, qualityProfileChangeEventService);
private final WsActionTester ws = new WsActionTester(underTest);
@Test
@@ -97,6 +103,7 @@ public class RemoveProjectActionTest {
assertProjectIsNotAssociatedToProfile(project, profileLang1);
assertProjectIsAssociatedToProfile(project, profileLang2);
+ verify(qualityProfileChangeEventService).publishRuleActivationToSonarLintClients(project, empty(), of(profileLang1));
}
@Test
@@ -110,6 +117,7 @@ public class RemoveProjectActionTest {
assertThat(response.getStatus()).isEqualTo(HttpURLConnection.HTTP_NO_CONTENT);
assertProjectIsNotAssociatedToProfile(project, profile);
+ verify(qualityProfileChangeEventService).publishRuleActivationToSonarLintClients(project, empty(), of(profile));
}
@Test
@@ -122,6 +130,7 @@ public class RemoveProjectActionTest {
call(project, profile);
assertProjectIsNotAssociatedToProfile(project, profile);
+ verify(qualityProfileChangeEventService).publishRuleActivationToSonarLintClients(project, empty(), of(profile));
}
@Test
@@ -136,6 +145,7 @@ public class RemoveProjectActionTest {
call(project, profile);
assertProjectIsNotAssociatedToProfile(project, profile);
+ verify(qualityProfileChangeEventService).publishRuleActivationToSonarLintClients(project, empty(), of(profile));
}
@Test
diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/rule/ws/SearchActionTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/rule/ws/SearchActionTest.java
index 61c46293d17..d0026ee115f 100644
--- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/rule/ws/SearchActionTest.java
+++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/rule/ws/SearchActionTest.java
@@ -48,6 +48,7 @@ import org.sonar.server.language.LanguageTesting;
import org.sonar.server.qualityprofile.ActiveRuleChange;
import org.sonar.server.qualityprofile.QProfileRules;
import org.sonar.server.qualityprofile.QProfileRulesImpl;
+import org.sonar.server.qualityprofile.QualityProfileChangeEventService;
import org.sonar.server.qualityprofile.RuleActivation;
import org.sonar.server.qualityprofile.RuleActivator;
import org.sonar.server.qualityprofile.index.ActiveRuleIndexer;
@@ -105,12 +106,14 @@ public class SearchActionTest {
private final ActiveRuleCompleter activeRuleCompleter = new ActiveRuleCompleter(db.getDbClient(), languages);
private final RuleQueryFactory ruleQueryFactory = new RuleQueryFactory(db.getDbClient());
private final MacroInterpreter macroInterpreter = mock(MacroInterpreter.class);
+ private final QualityProfileChangeEventService qualityProfileChangeEventService = mock(QualityProfileChangeEventService.class);
private final RuleMapper ruleMapper = new RuleMapper(languages, macroInterpreter);
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 QProfileRules qProfileRules = new QProfileRulesImpl(db.getDbClient(), ruleActivator, ruleIndex, activeRuleIndexer);
+ private final QProfileRules qProfileRules = new QProfileRulesImpl(db.getDbClient(), ruleActivator, ruleIndex, activeRuleIndexer,
+ qualityProfileChangeEventService);
private final WsActionTester ws = new WsActionTester(underTest);
@Before
diff --git a/server/sonar-webserver-ws/src/main/java/org/sonar/server/ws/ServletRequest.java b/server/sonar-webserver-ws/src/main/java/org/sonar/server/ws/ServletRequest.java
index cfc18bb2969..432f32b605c 100644
--- a/server/sonar-webserver-ws/src/main/java/org/sonar/server/ws/ServletRequest.java
+++ b/server/sonar-webserver-ws/src/main/java/org/sonar/server/ws/ServletRequest.java
@@ -69,6 +69,10 @@ public class ServletRequest extends ValidatingRequest {
MediaTypes.DEFAULT));
}
+ public HttpServletRequest getHttpRequest() {
+ return source;
+ }
+
@Override
public BufferedReader getReader() {
try {
diff --git a/server/sonar-webserver/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java b/server/sonar-webserver/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java
index f5c1bde9d9b..c4504d656c9 100644
--- a/server/sonar-webserver/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java
+++ b/server/sonar-webserver/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java
@@ -189,6 +189,7 @@ import org.sonar.server.qualitygate.ws.QualityGateWsModule;
import org.sonar.server.qualityprofile.BuiltInQPChangeNotificationHandler;
import org.sonar.server.qualityprofile.BuiltInQPChangeNotificationTemplate;
import org.sonar.server.qualityprofile.BuiltInQProfileRepositoryImpl;
+import org.sonar.server.qualityprofile.DistributedRuleActivatorEventsDistributor;
import org.sonar.server.qualityprofile.QProfileBackuperImpl;
import org.sonar.server.qualityprofile.QProfileComparison;
import org.sonar.server.qualityprofile.QProfileCopier;
@@ -198,7 +199,9 @@ import org.sonar.server.qualityprofile.QProfileParser;
import org.sonar.server.qualityprofile.QProfileResetImpl;
import org.sonar.server.qualityprofile.QProfileRulesImpl;
import org.sonar.server.qualityprofile.QProfileTreeImpl;
+import org.sonar.server.qualityprofile.QualityProfileChangeEventServiceImpl;
import org.sonar.server.qualityprofile.RuleActivator;
+import org.sonar.server.qualityprofile.StandaloneRuleActivatorEventsDistributor;
import org.sonar.server.qualityprofile.index.ActiveRuleIndexer;
import org.sonar.server.qualityprofile.ws.QProfilesWsModule;
import org.sonar.server.root.ws.RootWsModule;
@@ -274,6 +277,9 @@ public class PlatformLevel4 extends PlatformLevel {
addIfCluster(NodeHealthModule.class);
+ addIfCluster(DistributedRuleActivatorEventsDistributor.class);
+ addIfStandalone(StandaloneRuleActivatorEventsDistributor.class);
+
add(
ClusterVerification.class,
LogServerId.class,
@@ -304,6 +310,7 @@ public class PlatformLevel4 extends PlatformLevel {
QProfileTreeImpl.class,
QProfileRulesImpl.class,
RuleActivator.class,
+ QualityProfileChangeEventServiceImpl.class,
QProfileExporters.class,
QProfileFactoryImpl.class,
QProfileCopier.class,
diff --git a/sonar-core/src/main/java/org/sonar/core/util/ParamChange.java b/sonar-core/src/main/java/org/sonar/core/util/ParamChange.java
new file mode 100644
index 00000000000..cff86851837
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/core/util/ParamChange.java
@@ -0,0 +1,38 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.core.util;
+
+public class ParamChange {
+ String key;
+ String value;
+
+ public ParamChange(String key, String value) {
+ this.key = key;
+ this.value = value;
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public String getValue() {
+ return value;
+ }
+}
diff --git a/sonar-core/src/main/java/org/sonar/core/util/RuleActivationListener.java b/sonar-core/src/main/java/org/sonar/core/util/RuleActivationListener.java
new file mode 100644
index 00000000000..d234d200d71
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/core/util/RuleActivationListener.java
@@ -0,0 +1,25 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.core.util;
+
+public interface RuleActivationListener {
+
+ void listen(RuleSetChangeEvent event);
+} \ No newline at end of file
diff --git a/sonar-core/src/main/java/org/sonar/core/util/RuleChange.java b/sonar-core/src/main/java/org/sonar/core/util/RuleChange.java
new file mode 100644
index 00000000000..016a08ec488
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/core/util/RuleChange.java
@@ -0,0 +1,68 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.core.util;
+
+public class RuleChange {
+ String key;
+ String language;
+ String templateKey;
+ String severity;
+ ParamChange[] params = new ParamChange[0];
+
+ public String getKey() {
+ return key;
+ }
+
+ public void setKey(String key) {
+ this.key = key;
+ }
+
+ public String getLanguage() {
+ return language;
+ }
+
+ public void setLanguage(String language) {
+ this.language = language;
+ }
+
+ public String getTemplateKey() {
+ return templateKey;
+ }
+
+ public void setTemplateKey(String templateKey) {
+ this.templateKey = templateKey;
+ }
+
+ public String getSeverity() {
+ return severity;
+ }
+
+ public void setSeverity(String severity) {
+ this.severity = severity;
+ }
+
+ public ParamChange[] getParams() {
+ return params;
+ }
+
+ public void setParams(ParamChange[] params) {
+ this.params = params;
+ }
+}
diff --git a/sonar-core/src/main/java/org/sonar/core/util/RuleSetChangeEvent.java b/sonar-core/src/main/java/org/sonar/core/util/RuleSetChangeEvent.java
new file mode 100644
index 00000000000..2a7d4873836
--- /dev/null
+++ b/sonar-core/src/main/java/org/sonar/core/util/RuleSetChangeEvent.java
@@ -0,0 +1,65 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.core.util;
+
+import java.io.Serializable;
+
+public class RuleSetChangeEvent implements Serializable {
+
+ private final String event = "RuleSetChange";
+
+ private String[] projects;
+ private RuleChange[] activatedRules;
+ private RuleChange[] deactivatedRules;
+
+ public RuleSetChangeEvent(String[] projects, RuleChange[] activatedRules, RuleChange[] deactivatedRules) {
+ this.projects = projects;
+ this.activatedRules = activatedRules;
+ this.deactivatedRules = deactivatedRules;
+ }
+
+ public void setProjects(String[] projects) {
+ this.projects = projects;
+ }
+
+ public void setActivatedRules(RuleChange[] activatedRules) {
+ this.activatedRules = activatedRules;
+ }
+
+ public void setDeactivatedRules(RuleChange[] deactivatedRules) {
+ this.deactivatedRules = deactivatedRules;
+ }
+
+ public String getEvent() {
+ return event;
+ }
+
+ public String[] getProjects() {
+ return projects;
+ }
+
+ public RuleChange[] getActivatedRules() {
+ return activatedRules;
+ }
+
+ public RuleChange[] getDeactivatedRules() {
+ return deactivatedRules;
+ }
+}