testCompile 'org.assertj:assertj-core'
testCompile 'org.mockito:mockito-core'
testCompile testFixtures(project(':server:sonar-webserver-ws'))
+ testCompile testFixtures(project(':server:sonar-db-dao'))
testFixturesApi project(':sonar-testing-harness')
testFixturesCompileOnly testFixtures(project(':server:sonar-webserver-ws'))
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 javax.annotation.Nullable;
+import org.apache.commons.lang.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.sonar.api.server.ServerSide;
import org.sonar.core.util.ParamChange;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.project.ProjectDto;
+import org.sonar.db.qualityprofile.ActiveRuleDto;
import org.sonar.db.qualityprofile.ActiveRuleParamDto;
+import org.sonar.db.qualityprofile.OrgActiveRuleDto;
import org.sonar.db.qualityprofile.ProjectQprofileAssociationDto;
import org.sonar.db.qualityprofile.QProfileDto;
import org.sonar.db.rule.RuleDto;
import org.sonar.server.qualityprofile.ActiveRuleChange;
-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) {
+ public QualityProfileChangeEventServiceImpl(DbClient dbClient, RuleActivatorEventsDistributor eventsDistributor) {
this.dbClient = dbClient;
- this.ruleIndex = ruleIndex;
this.eventsDistributor = eventsDistributor;
}
List<RuleChange> activatedRules = new ArrayList<>();
List<RuleChange> deactivatedRules = new ArrayList<>();
- try (DbSession dbSession = dbClient.openSession(false)) {
+ if (activatedProfile != null) {
+ activatedRules.addAll(createRuleChanges(activatedProfile));
+ }
- if (activatedProfile != null) {
- RuleQuery query = new RuleQuery().setQProfile(activatedProfile).setActivation(true).setIncludeExternal(true);
- Iterator<String> searchIdResult = ruleIndex.searchAll(query);
- List<String> uuids = new ArrayList<>();
- while (searchIdResult.hasNext()) {
- uuids.add(searchIdResult.next());
- }
+ if (deactivatedProfile != null) {
+ deactivatedRules.addAll(createRuleChanges(deactivatedProfile));
+ }
- List<RuleDto> ruleDtos = dbClient.ruleDao().selectByUuids(dbSession, uuids);
- Map<String, List<ActiveRuleParamDto>> paramsByRuleUuid = dbClient.activeRuleDao().selectParamsByActiveRuleUuids(dbSession, uuids)
- .stream().collect(Collectors.groupingBy(ActiveRuleParamDto::getActiveRuleUuid));
+ if (activatedRules.isEmpty() && deactivatedRules.isEmpty()) {
+ return;
+ }
- for (RuleDto ruleDto : ruleDtos) {
- RuleChange ruleChange = toRuleChange(ruleDto, paramsByRuleUuid);
- activatedRules.add(ruleChange);
- }
- }
+ RuleSetChangeEvent event = new RuleSetChangeEvent(new String[] {project.getKey()}, activatedRules.toArray(new RuleChange[0]), deactivatedRules.toArray(new RuleChange[0]));
+ eventsDistributor.pushEvent(event);
+ }
- if (deactivatedProfile != null) {
- RuleQuery query = new RuleQuery().setQProfile(deactivatedProfile).setActivation(true).setIncludeExternal(true);
- // .setLanguages() ?
- Iterator<String> searchIdResult = ruleIndex.searchAll(query);
- List<String> uuids = new ArrayList<>();
- while (searchIdResult.hasNext()) {
- uuids.add(searchIdResult.next());
- }
+ private List<RuleChange> createRuleChanges(@NotNull QProfileDto profileDto) {
+ List<RuleChange> ruleChanges = new ArrayList<>();
- List<RuleDto> ruleDtos = dbClient.ruleDao().selectByUuids(dbSession, uuids);
- Map<String, List<ActiveRuleParamDto>> paramsByRuleUuid = dbClient.activeRuleDao().selectParamsByActiveRuleUuids(dbSession, uuids)
- .stream().collect(Collectors.groupingBy(ActiveRuleParamDto::getActiveRuleUuid));
+ try (DbSession dbSession = dbClient.openSession(false)) {
+ List<OrgActiveRuleDto> activeRuleDtos = dbClient.activeRuleDao().selectByProfile(dbSession, profileDto);
+ List<String> activeRuleUuids = activeRuleDtos.stream().map(ActiveRuleDto::getUuid).collect(Collectors.toList());
- for (RuleDto ruleDto : ruleDtos) {
- RuleChange ruleChange = toRuleChange(ruleDto, paramsByRuleUuid);
- deactivatedRules.add(ruleChange);
- }
- }
+ Map<String, List<ActiveRuleParamDto>> paramsByActiveRuleUuid = dbClient.activeRuleDao().selectParamsByActiveRuleUuids(dbSession, activeRuleUuids)
+ .stream().collect(Collectors.groupingBy(ActiveRuleParamDto::getActiveRuleUuid));
- }
+ Map<String, String> activeRuleUuidByRuleUuid = activeRuleDtos.stream().collect(Collectors.toMap(ActiveRuleDto::getRuleUuid, ActiveRuleDto::getUuid));
- if (activatedRules.isEmpty() && deactivatedRules.isEmpty()) {
- return;
- }
+ List<String> ruleUuids = activeRuleDtos.stream().map(ActiveRuleDto::getRuleUuid).collect(Collectors.toList());
+ List<RuleDto> ruleDtos = dbClient.ruleDao().selectByUuids(dbSession, ruleUuids);
- RuleSetChangeEvent event = new RuleSetChangeEvent(new String[]{project.getKey()}, activatedRules.toArray(new RuleChange[0]), deactivatedRules.toArray(new RuleChange[0]));
- eventsDistributor.pushEvent(event);
+ for (RuleDto ruleDto : ruleDtos) {
+ String activeRuleUuid = activeRuleUuidByRuleUuid.get(ruleDto.getUuid());
+ List<ActiveRuleParamDto> params = paramsByActiveRuleUuid.getOrDefault(activeRuleUuid, new ArrayList<>());
+ RuleChange ruleChange = toRuleChange(ruleDto, params);
+ ruleChanges.add(ruleChange);
+ }
+ }
+ return ruleChanges;
}
@NotNull
- private RuleChange toRuleChange(RuleDto ruleDto, Map<String, List<ActiveRuleParamDto>> paramsByRuleUuid) {
+ private RuleChange toRuleChange(RuleDto ruleDto, List<ActiveRuleParamDto> activeRuleParamDtos) {
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()));
}
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)));
+ .orElseThrow(() -> new IllegalStateException(String.format("Unknown Template Rule '%s'", templateUuid)));
ruleChange.setTemplateKey(templateRule.getRuleKey());
}
}
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)) {
+
+ if (StringUtils.isNotEmpty(templateUuid)) {
RuleDto templateRule = dbClient.ruleDao().selectByUuid(templateUuid, dbSession)
- .orElseThrow(() -> new IllegalStateException(String.format("unknow Template Rule '%s'", templateUuid)));
+ .orElseThrow(() -> new IllegalStateException(String.format("Unknown Template Rule '%s'", templateUuid)));
return Optional.of(templateRule.getRuleKey());
}
}
return result.toString();
}
- private JSONObject toJson(RuleChange rule) {
+ private static JSONObject toJson(RuleChange rule) {
JSONObject ruleJson = new JSONObject();
ruleJson.put("key", rule.getKey());
ruleJson.put("language", rule.getLanguage());
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
+import org.sonar.api.rule.Severity;
+import org.sonar.core.util.ParamChange;
import org.sonar.core.util.RuleChange;
import org.sonar.core.util.RuleSetChangeEvent;
import org.sonar.server.exceptions.ForbiddenException;
import org.sonar.server.pushapi.qualityprofile.StandaloneRuleActivatorEventsDistributor;
import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anySet;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.when;
+import static org.sonar.test.JsonAssert.assertJson;
public class SonarLintClientsRegistryTest {
underTest.registerClient(sonarLintClient);
- RuleChange javaRule = createRuleChange("java");
+ RuleChange javaRule = createRuleChange();
- RuleChange[] activatedRules = {};
+ RuleChange[] activatedRules = {javaRule};
RuleChange[] deactivatedRules = {javaRule};
RuleSetChangeEvent ruleChangeEvent = new RuleSetChangeEvent(exampleKeys.toArray(String[]::new), activatedRules, deactivatedRules);
underTest.listen(ruleChangeEvent);
ArgumentCaptor<byte[]> captor = ArgumentCaptor.forClass(byte[].class);
verify(outputStream).write(captor.capture());
- String message = new String(captor.getValue());
- assertThat(message).contains("java");
+ String json = new String(captor.getValue());
+ assertJson(json)
+ .withStrictArrayOrder()
+ .isSimilarTo(getClass().getResource("rule-change-event.json"));
}
@Test
underTest.registerClient(sonarLintClient);
- RuleChange javaRuleChange = createRuleChange("java");
+ RuleChange javaRuleChange = createRuleChange();
RuleChange[] activatedRules = {};
RuleChange[] deactivatedRules = {javaRuleChange};
underTest.registerClient(sonarLintClient);
- RuleChange javaRuleChange = createRuleChange("java");
+ RuleChange javaRuleChange = createRuleChange();
RuleChange[] activatedRules = {};
RuleChange[] deactivatedRules = {javaRuleChange};
@Test
public void listen_givenUserNotPermittedToReceiveEvent_closeConnection() {
- RuleChange javaRuleChange = createRuleChange("java");
+ RuleChange javaRuleChange = createRuleChange();
RuleChange[] activatedRules = {};
RuleChange[] deactivatedRules = {javaRuleChange};
RuleSetChangeEvent ruleChangeEvent = new RuleSetChangeEvent(exampleKeys.toArray(String[]::new), activatedRules, deactivatedRules);
return mock;
}
- private RuleChange createRuleChange(String language) {
+ private RuleChange createRuleChange() {
RuleChange javaRule = new RuleChange();
- javaRule.setLanguage(language);
-
+ javaRule.setLanguage("java");
+ javaRule.setParams(new ParamChange[]{new ParamChange("param-key", "param-value")});
+ javaRule.setTemplateKey("template-key");
+ javaRule.setSeverity(Severity.CRITICAL);
+ javaRule.setKey("rule-key");
return javaRule;
}
--- /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.builtin;
+
+import java.util.Collection;
+import java.util.Collections;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.core.util.ParamChange;
+import org.sonar.core.util.RuleChange;
+import org.sonar.core.util.RuleSetChangeEvent;
+import org.sonar.db.DbTester;
+import org.sonar.db.project.ProjectDto;
+import org.sonar.db.qualityprofile.ActiveRuleDto;
+import org.sonar.db.qualityprofile.ActiveRuleParamDto;
+import org.sonar.db.qualityprofile.QProfileDto;
+import org.sonar.db.qualityprofile.QualityProfileTesting;
+import org.sonar.db.rule.RuleDefinitionDto;
+import org.sonar.db.rule.RuleDto;
+import org.sonar.db.rule.RuleParamDto;
+import org.sonar.server.pushapi.qualityprofile.QualityProfileChangeEventServiceImpl;
+import org.sonar.server.pushapi.qualityprofile.RuleActivatorEventsDistributor;
+import org.sonar.server.qualityprofile.ActiveRuleChange;
+
+import static java.util.List.of;
+import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.tuple;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.sonar.db.rule.RuleDto.Format.MARKDOWN;
+import static org.sonar.db.rule.RuleTesting.newCustomRule;
+import static org.sonar.db.rule.RuleTesting.newTemplateRule;
+import static org.sonar.server.qualityprofile.ActiveRuleChange.Type.ACTIVATED;
+
+public class QualityProfileChangeEventServiceImplTest {
+
+ @Rule
+ public DbTester db = DbTester.create();
+
+ RuleActivatorEventsDistributor eventsDistributor = mock(RuleActivatorEventsDistributor.class);
+
+ public final QualityProfileChangeEventServiceImpl underTest = new QualityProfileChangeEventServiceImpl(db.getDbClient(), eventsDistributor);
+
+ @Test
+ public void distributeRuleChangeEvent() {
+ QProfileDto qualityProfileDto = QualityProfileTesting.newQualityProfileDto();
+
+ // Template rule
+ RuleDto templateRule = newTemplateRule(RuleKey.of("xoo", "template-key"));
+ db.rules().insert(templateRule.getDefinition());
+ // Custom rule
+ RuleDefinitionDto rule1 = newCustomRule(templateRule.getDefinition())
+ .setLanguage("xoo")
+ .setDescription("<div>line1\nline2</div>")
+ .setDescriptionFormat(MARKDOWN);
+ db.rules().insert(rule1);
+
+ ActiveRuleDto activeRuleDto = ActiveRuleDto.createFor(qualityProfileDto, rule1);
+
+ ActiveRuleChange activeRuleChange = new ActiveRuleChange(ACTIVATED, activeRuleDto, rule1);
+ activeRuleChange.setParameter("paramChangeKey", "paramChangeValue");
+
+ Collection<QProfileDto> profiles = Collections.singleton(qualityProfileDto);
+
+ ProjectDto project = db.components().insertPrivateProjectDto();
+ db.qualityProfiles().associateWithProject(project, qualityProfileDto);
+
+ underTest.distributeRuleChangeEvent(profiles, of(activeRuleChange), "xoo");
+
+ ArgumentCaptor<RuleSetChangeEvent> eventCaptor = ArgumentCaptor.forClass(RuleSetChangeEvent.class);
+ verify(eventsDistributor).pushEvent(eventCaptor.capture());
+
+ RuleSetChangeEvent ruleSetChangeEvent = eventCaptor.getValue();
+ assertThat(ruleSetChangeEvent).isNotNull();
+ assertThat(ruleSetChangeEvent).extracting(RuleSetChangeEvent::getEvent,
+ RuleSetChangeEvent::getLanguage, RuleSetChangeEvent::getProjects)
+ .containsExactly("RuleSetChange", "xoo", new String[]{project.getKey()});
+
+ assertThat(ruleSetChangeEvent.getActivatedRules())
+ .extracting(RuleChange::getKey, RuleChange::getLanguage,
+ RuleChange::getSeverity, RuleChange::getTemplateKey)
+ .containsExactly(tuple(rule1.getRuleKey(), "xoo", null, "template-key"));
+
+ assertThat(ruleSetChangeEvent.getActivatedRules()[0].getParams()).hasSize(1);
+ ParamChange actualParamChange = ruleSetChangeEvent.getActivatedRules()[0].getParams()[0];
+ assertThat(actualParamChange)
+ .extracting(ParamChange::getKey, ParamChange::getValue)
+ .containsExactly("paramChangeKey", "paramChangeValue");
+
+ assertThat(ruleSetChangeEvent.getDeactivatedRules()).isEmpty();
+
+ }
+
+ @Test
+ public void publishRuleActivationToSonarLintClients() {
+ ProjectDto projectDao = new ProjectDto();
+ QProfileDto activatedQualityProfile = QualityProfileTesting.newQualityProfileDto();
+ db.qualityProfiles().insert(activatedQualityProfile);
+ RuleDefinitionDto rule1 = db.rules().insert(r -> r.setLanguage("xoo"));
+ RuleParamDto rule1Param = db.rules().insertRuleParam(rule1);
+
+ ActiveRuleDto activeRule1 = db.qualityProfiles().activateRule(activatedQualityProfile, rule1);
+ ActiveRuleParamDto activeRuleParam1 = ActiveRuleParamDto.createFor(rule1Param).setValue(randomAlphanumeric(20));
+ db.getDbClient().activeRuleDao().insertParam(db.getSession(), activeRule1, activeRuleParam1);
+ db.getSession().commit();
+
+ QProfileDto deactivatedQualityProfile = QualityProfileTesting.newQualityProfileDto();
+ db.qualityProfiles().insert(deactivatedQualityProfile);
+ RuleDefinitionDto rule2 = db.rules().insert(r -> r.setLanguage("xoo"));
+ RuleParamDto rule2Param = db.rules().insertRuleParam(rule2);
+
+ ActiveRuleDto activeRule2 = db.qualityProfiles().activateRule(deactivatedQualityProfile, rule2);
+ ActiveRuleParamDto activeRuleParam2 = ActiveRuleParamDto.createFor(rule2Param).setValue(randomAlphanumeric(20));
+ db.getDbClient().activeRuleDao().insertParam(db.getSession(), activeRule2, activeRuleParam2);
+ db.getSession().commit();
+
+ underTest.publishRuleActivationToSonarLintClients(projectDao, activatedQualityProfile, deactivatedQualityProfile);
+
+ ArgumentCaptor<RuleSetChangeEvent> eventCaptor = ArgumentCaptor.forClass(RuleSetChangeEvent.class);
+ verify(eventsDistributor).pushEvent(eventCaptor.capture());
+
+ RuleSetChangeEvent ruleSetChangeEvent = eventCaptor.getValue();
+ assertThat(ruleSetChangeEvent).isNotNull();
+ assertThat(ruleSetChangeEvent).extracting(RuleSetChangeEvent::getEvent,
+ RuleSetChangeEvent::getLanguage, RuleSetChangeEvent::getProjects)
+ .containsExactly("RuleSetChange", "xoo", new String[]{null});
+
+ // activated rule
+ assertThat(ruleSetChangeEvent.getActivatedRules())
+ .extracting(RuleChange::getKey, RuleChange::getLanguage,
+ RuleChange::getSeverity, RuleChange::getTemplateKey)
+ .containsExactly(tuple(rule1.getRuleKey(), "xoo", rule1.getSeverityString(), null));
+
+ assertThat(ruleSetChangeEvent.getActivatedRules()[0].getParams()).hasSize(1);
+ ParamChange actualParamChange = ruleSetChangeEvent.getActivatedRules()[0].getParams()[0];
+ assertThat(actualParamChange)
+ .extracting(ParamChange::getKey, ParamChange::getValue)
+ .containsExactly(activeRuleParam1.getKey(), activeRuleParam1.getValue());
+
+ // deactivated rule
+ assertThat(ruleSetChangeEvent.getDeactivatedRules())
+ .extracting(RuleChange::getKey, RuleChange::getLanguage,
+ RuleChange::getSeverity, RuleChange::getTemplateKey)
+ .containsExactly(tuple(rule2.getRuleKey(), "xoo", rule2.getSeverityString(), null));
+
+ assertThat(ruleSetChangeEvent.getDeactivatedRules()[0].getParams()).hasSize(1);
+ ParamChange actualParamChangeDeactivated = ruleSetChangeEvent.getDeactivatedRules()[0].getParams()[0];
+ assertThat(actualParamChangeDeactivated)
+ .extracting(ParamChange::getKey, ParamChange::getValue)
+ .containsExactly(activeRuleParam2.getKey(), activeRuleParam2.getValue());
+ }
+
+}
--- /dev/null
+{
+ "data": {
+ "projects": [
+ "project2",
+ "project1",
+ "project3"
+ ],
+ "activatedRules": [
+ {
+ "key": "rule-key",
+ "templateKey": "template-key",
+ "severity": "CRITICAL",
+ "language": "java",
+ "params": [
+ {
+ "value": "param-value",
+ "key": "param-key"
+ }
+ ]
+ }
+ ],
+ "deactivatedRules": [
+ {
+ "key": "rule-key",
+ "templateKey": "template-key",
+ "severity": "CRITICAL",
+ "language": "java",
+ "params": [
+ {
+ "value": "param-value",
+ "key": "param-key"
+ }
+ ]
+ }
+ ]
+ },
+ "event": "RuleSetChange"
+}
*/
package org.sonar.core.util;
-public class ParamChange {
+import java.io.Serializable;
+
+public class ParamChange implements Serializable {
String key;
String value;
return key;
}
- public void setKey(String key) {
+ public RuleChange setKey(String key) {
this.key = key;
+ return this;
}
@CheckForNull
return language;
}
- public void setLanguage(@Nullable String language) {
+ public RuleChange setLanguage(@Nullable String language) {
this.language = language;
+ return this;
}
public String getTemplateKey() {
return templateKey;
}
- public void setTemplateKey(String templateKey) {
+ public RuleChange setTemplateKey(String templateKey) {
this.templateKey = templateKey;
+ return this;
}
@CheckForNull
return severity;
}
- public void setSeverity(@Nullable String severity) {
+ public RuleChange setSeverity(@Nullable String severity) {
this.severity = severity;
+ return this;
}
public ParamChange[] getParams() {
return params;
}
- public void setParams(ParamChange[] params) {
+ public RuleChange setParams(ParamChange[] params) {
this.params = params;
+ return this;
}
}