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;
callback.onComplete((Map<Member, T>) hostsPerMember);
}
+ @Override
+ public void subscribeRuleActivationTopic(RuleActivationListener listener) {
+
+ }
+
+ @Override
+ public void publishEvent(RuleSetChangeEvent event) {
+
+ }
+
@Override
public void close() {
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 {
*/
<T> void callAsync(DistributedCall<T> callable, MemberSelector memberSelector, DistributedCallback<T> callback);
+ void subscribeRuleActivationTopic(RuleActivationListener listener);
+
+ void publishEvent(RuleSetChangeEvent event);
+
@Override
void close();
}
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;
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 {
});
}
+ @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 {
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) {
// 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<>();
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;
}
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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;
+ }
+ }
+
+}
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;
/**
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) {
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) {
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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));
+ }
+}
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;
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;
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
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
assertThat(activeRules).hasSize(1);
assertThatRuleIsUntouched(activeRules, rule, CRITICAL);
assertThatProfileIsNotMarkedAsUpdated(persistedProfile);
+ verifyNoInteractions(qualityProfileChangeEventService);
}
@Test
// 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
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);
assertThatRuleIsNewlyActivated(activeRules, rule2, MAJOR);
assertThatRuleIsDeactivated(activeRules, rule3);
assertThatProfileIsMarkedAsUpdated(persistedProfile);
+ verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(persistedProfile.getLanguage()));
}
// SONAR-10473
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
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
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
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
assertThatRuleHasParams(db, parentActiveRuleDto, tuple("min", "10"));
assertThatRuleHasParams(db, childActiveRuleDto, tuple("min", "10"));
+ verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), eq(changes), eq(persistedProfile.getLanguage()));
}
@Test
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) {
};
}
};
- 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
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();
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);
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;
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
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'
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;
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;
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();
this.languages = languages;
}
+ public Set<String> getLanguages() {
+ return languages;
+ }
+
+ public Set<String> getClientProjectKeys() {
+ return projectKeys;
+ }
+
@Override
public boolean equals(Object o) {
if (this == o) {
*/
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");
}
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;
if (!isServerSideEventsRequest(servletRequest)) {
servletResponse.stream().setStatus(HttpServletResponse.SC_NOT_ACCEPTABLE);
- return;
+ return; // TODO fixme this is not closing the connexion properly
}
setHeadersForResponse(servletResponse);
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);
}
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;
@Before
public void before() {
- underTest = new SonarLintClientsRegistry();
+ underTest = new SonarLintClientsRegistry(mock(StandaloneRuleActivatorEventsDistributor.class));
}
@Test
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
// 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;
}
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
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;
}
@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
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;
}
@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
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
// TODO return errors
}
}
+ qualityProfileChangeEventService.distributeRuleChangeEvent(List.of(profile), changes, profile.getLanguage());
return changes;
}
changes.add(new ActiveRuleChange(ActiveRuleChange.Type.UPDATED, activeRule, context.getRule().get()).setInheritance(null));
}
}
+
+ qualityProfileChangeEventService.distributeRuleChangeEvent(List.of(profile), changes, profile.getLanguage());
return changes;
}
*/
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;
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;
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
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);
*/
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;
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;
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
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();
}
}
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 {
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();
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 {
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() {
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
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
})
.isInstanceOf(IllegalArgumentException.class)
.hasMessage(String.format("Operation forbidden for built-in Quality Profile '%s'", profile.getKee()));
+ verifyNoInteractions(qualityProfileChangeEventService);
}
@Test
})
.isInstanceOf(NullPointerException.class)
.hasMessage("Quality profile must be persisted");
+
+ verifyNoInteractions(qualityProfileChangeEventService);
}
}
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;
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() {
assertThatRuleIsActivated(profile, rule, changes, BLOCKER, null, emptyMap());
assertThatProfileIsUpdatedBySystem(profile);
+ verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), any(), eq(profile.getLanguage()));
}
@Test
assertThatRuleIsActivated(profile, rule, changes, BLOCKER, null, emptyMap());
assertThatProfileIsUpdatedByUser(profile);
+ verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), any(), eq(profile.getLanguage()));
}
@Test
assertThatRuleIsActivated(profile, rule, changes, rule.getSeverityString(), null, of("min", "10"));
assertThatProfileIsUpdatedBySystem(profile);
+ verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), any(), eq(profile.getLanguage()));
}
@Test
assertThatRuleIsActivated(profile, rule, changes, rule.getSeverityString(), null, of("min", "15"));
assertThatProfileIsUpdatedBySystem(profile);
+ verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), any(), eq(profile.getLanguage()));
}
@Test
assertThatRuleIsActivated(profile, rule, changes, rule.getSeverityString(), null, emptyMap());
assertThatProfileIsUpdatedBySystem(profile);
+ verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), any(), eq(profile.getLanguage()));
}
/**
assertThatRuleIsActivated(profile, rule, changes, rule.getSeverityString(), null, of("min", "10"));
assertThatProfileIsUpdatedBySystem(profile);
+ verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), any(), eq(profile.getLanguage()));
}
/**
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
assertThatRuleIsActivated(profile, rule, changes, rule.getSeverityString(), null, of(param.getName(), param.getDefaultValue()));
assertThatProfileIsUpdatedBySystem(profile);
+ verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), any(), eq(profile.getLanguage()));
}
@Test
// 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
// 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
// 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
// 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
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(), ""));
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
// 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
// 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
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
RuleActivation activation = RuleActivation.create(rule.getUuid());
expectFailure("Rule was removed: " + rule.getKey(), () -> activate(profile, activation));
+ verifyNoInteractions(qualityProfileChangeEventService);
}
@Test
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
RuleActivation activation = RuleActivation.create(rule.getUuid(), null, of(param.getName(), "foo"));
expectFailure("Value 'foo' must be an integer.", () -> activate(profile, activation));
+ verifyNoInteractions(qualityProfileChangeEventService);
}
@Test
// 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
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
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) {
List<ActiveRuleChange> changes = deactivate(profile, rule);
verifyNoActiveRules();
assertThat(changes).isEmpty();
+ verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), any(), eq(profile.getLanguage()));
}
@Test
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
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
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
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
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
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
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
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
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
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
assertThatThrownBy(() -> deactivate(childProfile, rule))
.isInstanceOf(BadRequestException.class)
.hasMessageContaining("Cannot deactivate inherited rule");
+ verify(qualityProfileChangeEventService).distributeRuleChangeEvent(any(), any(), eq(parentProfile.getLanguage()));
}
@Test
assertThatRuleIsUpdated(childProfile, rule, CRITICAL, INHERITED, emptyMap());
assertThatRuleIsUpdated(parentProfile, rule, CRITICAL, null, emptyMap());
assertThat(changes).hasSize(1);
+
}
@Test
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 {
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() {
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
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()));
}
}
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;
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() {
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
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);
// 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
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());
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);
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 {
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
assertThat(response.getStatus()).isEqualTo(HttpURLConnection.HTTP_NO_CONTENT);
assertProjectIsAssociatedToProfile(project, profile);
+ verify(qualityProfileChangeEventService).publishRuleActivationToSonarLintClients(project, of(profile), empty());
}
@Test
call(project, qualityProfile);
assertProjectIsAssociatedToProfile(project, qualityProfile);
+ verify(qualityProfileChangeEventService).publishRuleActivationToSonarLintClients(project, of(qualityProfile), empty());
}
@Test
assertProjectIsNotAssociatedToProfile(project, profile1);
assertProjectIsAssociatedToProfile(project, profile2);
+ verify(qualityProfileChangeEventService).publishRuleActivationToSonarLintClients(project, of(profile2), of(profile1));
}
@Test
assertProjectIsAssociatedToProfile(project, profile3Language1);
assertProjectIsAssociatedToProfile(project, profile2Language2);
+ verify(qualityProfileChangeEventService).publishRuleActivationToSonarLintClients(project, of(profile3Language1), of(profile1Language1));
}
@Test
call(project, profile);
assertProjectIsAssociatedToProfile(project, profile);
+ verify(qualityProfileChangeEventService).publishRuleActivationToSonarLintClients(project, of(profile), empty());
}
@Test
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;
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;
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,
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;
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;
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),
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;
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;
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,
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;
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;
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);
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;
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 {
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
assertProjectIsNotAssociatedToProfile(project, profileLang1);
assertProjectIsAssociatedToProfile(project, profileLang2);
+ verify(qualityProfileChangeEventService).publishRuleActivationToSonarLintClients(project, empty(), of(profileLang1));
}
@Test
assertThat(response.getStatus()).isEqualTo(HttpURLConnection.HTTP_NO_CONTENT);
assertProjectIsNotAssociatedToProfile(project, profile);
+ verify(qualityProfileChangeEventService).publishRuleActivationToSonarLintClients(project, empty(), of(profile));
}
@Test
call(project, profile);
assertProjectIsNotAssociatedToProfile(project, profile);
+ verify(qualityProfileChangeEventService).publishRuleActivationToSonarLintClients(project, empty(), of(profile));
}
@Test
call(project, profile);
assertProjectIsNotAssociatedToProfile(project, profile);
+ verify(qualityProfileChangeEventService).publishRuleActivationToSonarLintClients(project, empty(), of(profile));
}
@Test
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;
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
MediaTypes.DEFAULT));
}
+ public HttpServletRequest getHttpRequest() {
+ return source;
+ }
+
@Override
public BufferedReader getReader() {
try {
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;
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;
addIfCluster(NodeHealthModule.class);
+ addIfCluster(DistributedRuleActivatorEventsDistributor.class);
+ addIfStandalone(StandaloneRuleActivatorEventsDistributor.class);
+
add(
ClusterVerification.class,
LogServerId.class,
QProfileTreeImpl.class,
QProfileRulesImpl.class,
RuleActivator.class,
+ QualityProfileChangeEventServiceImpl.class,
QProfileExporters.class,
QProfileFactoryImpl.class,
QProfileCopier.class,
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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;
+ }
+}