3 * Copyright (C) 2009-2022 SonarSource SA
4 * mailto:info AT sonarsource DOT com
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 3 of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 package org.sonar.server.pushapi.qualityprofile;
22 import java.util.ArrayList;
23 import java.util.Collection;
24 import java.util.HashSet;
25 import java.util.List;
27 import java.util.Objects;
28 import java.util.Optional;
30 import java.util.stream.Collectors;
31 import javax.annotation.Nullable;
32 import org.apache.commons.lang.StringUtils;
33 import org.jetbrains.annotations.NotNull;
34 import org.sonar.api.rule.RuleKey;
35 import org.sonar.api.server.ServerSide;
36 import org.sonar.core.util.ParamChange;
37 import org.sonar.core.util.RuleChange;
38 import org.sonar.core.util.RuleSetChangedEvent;
39 import org.sonar.db.DbClient;
40 import org.sonar.db.DbSession;
41 import org.sonar.db.project.ProjectDto;
42 import org.sonar.db.qualityprofile.ActiveRuleDto;
43 import org.sonar.db.qualityprofile.ActiveRuleParamDto;
44 import org.sonar.db.qualityprofile.OrgActiveRuleDto;
45 import org.sonar.db.qualityprofile.ProjectQprofileAssociationDto;
46 import org.sonar.db.qualityprofile.QProfileDto;
47 import org.sonar.db.rule.RuleDto;
48 import org.sonar.server.qualityprofile.ActiveRuleChange;
50 import static java.util.function.Predicate.not;
51 import static org.sonar.server.qualityprofile.ActiveRuleChange.Type.ACTIVATED;
52 import static org.sonar.server.qualityprofile.ActiveRuleChange.Type.DEACTIVATED;
53 import static org.sonar.server.qualityprofile.ActiveRuleChange.Type.UPDATED;
56 public class QualityProfileChangeEventServiceImpl implements QualityProfileChangeEventService {
58 private final DbClient dbClient;
59 private final RuleActivatorEventsDistributor eventsDistributor;
61 public QualityProfileChangeEventServiceImpl(DbClient dbClient, RuleActivatorEventsDistributor eventsDistributor) {
62 this.dbClient = dbClient;
63 this.eventsDistributor = eventsDistributor;
67 public void publishRuleActivationToSonarLintClients(ProjectDto project, @Nullable QProfileDto activatedProfile, @Nullable QProfileDto deactivatedProfile) {
68 List<RuleChange> activatedRules = new ArrayList<>();
69 Set<String> deactivatedRules = new HashSet<>();
71 if (activatedProfile != null) {
72 activatedRules.addAll(createRuleChanges(activatedProfile));
75 if (deactivatedProfile != null) {
76 deactivatedRules.addAll(getRuleKeys(deactivatedProfile));
79 if (activatedRules.isEmpty() && deactivatedRules.isEmpty()) {
83 String language = activatedProfile != null ? activatedProfile.getLanguage() : deactivatedProfile.getLanguage();
84 RuleSetChangedEvent event = new RuleSetChangedEvent(new String[] {project.getKey()}, activatedRules.toArray(new RuleChange[0]), deactivatedRules.toArray(new String[0]),
86 eventsDistributor.pushEvent(event);
89 private List<RuleChange> createRuleChanges(@NotNull QProfileDto profileDto) {
90 List<RuleChange> ruleChanges = new ArrayList<>();
92 try (DbSession dbSession = dbClient.openSession(false)) {
93 List<OrgActiveRuleDto> activeRuleDtos = dbClient.activeRuleDao().selectByProfile(dbSession, profileDto);
94 List<String> activeRuleUuids = activeRuleDtos.stream().map(ActiveRuleDto::getUuid).collect(Collectors.toList());
96 Map<String, List<ActiveRuleParamDto>> paramsByActiveRuleUuid = dbClient.activeRuleDao().selectParamsByActiveRuleUuids(dbSession, activeRuleUuids)
97 .stream().collect(Collectors.groupingBy(ActiveRuleParamDto::getActiveRuleUuid));
99 Map<String, String> activeRuleUuidByRuleUuid = activeRuleDtos.stream().collect(Collectors.toMap(ActiveRuleDto::getRuleUuid, ActiveRuleDto::getUuid));
101 List<String> ruleUuids = activeRuleDtos.stream().map(ActiveRuleDto::getRuleUuid).collect(Collectors.toList());
102 List<RuleDto> ruleDtos = dbClient.ruleDao().selectByUuids(dbSession, ruleUuids);
104 for (RuleDto ruleDto : ruleDtos) {
105 String activeRuleUuid = activeRuleUuidByRuleUuid.get(ruleDto.getUuid());
106 List<ActiveRuleParamDto> params = paramsByActiveRuleUuid.getOrDefault(activeRuleUuid, new ArrayList<>());
107 RuleChange ruleChange = toRuleChange(ruleDto, params);
108 ruleChanges.add(ruleChange);
114 private Set<String> getRuleKeys(@NotNull QProfileDto profileDto) {
115 Set<String> ruleKeys = new HashSet<>();
117 try (DbSession dbSession = dbClient.openSession(false)) {
118 List<OrgActiveRuleDto> activeRuleDtos = dbClient.activeRuleDao().selectByProfile(dbSession, profileDto);
120 List<String> ruleUuids = activeRuleDtos.stream().map(ActiveRuleDto::getRuleUuid).collect(Collectors.toList());
121 List<RuleDto> ruleDtos = dbClient.ruleDao().selectByUuids(dbSession, ruleUuids);
123 for (RuleDto ruleDto : ruleDtos) {
124 ruleKeys.add(ruleDto.getKey().toString());
131 private RuleChange toRuleChange(RuleDto ruleDto, List<ActiveRuleParamDto> activeRuleParamDtos) {
132 RuleChange ruleChange = new RuleChange();
133 ruleChange.setKey(ruleDto.getKey().toString());
134 ruleChange.setLanguage(ruleDto.getLanguage());
135 ruleChange.setSeverity(ruleDto.getSeverityString());
137 List<ParamChange> paramChanges = new ArrayList<>();
138 for (ActiveRuleParamDto activeRuleParam : activeRuleParamDtos) {
139 paramChanges.add(new ParamChange(activeRuleParam.getKey(), activeRuleParam.getValue()));
141 ruleChange.setParams(paramChanges.toArray(new ParamChange[0]));
143 String templateUuid = ruleDto.getTemplateUuid();
144 if (templateUuid != null && !"".equals(templateUuid)) {
145 try (DbSession dbSession = dbClient.openSession(false)) {
146 RuleDto templateRule = dbClient.ruleDao().selectByUuid(templateUuid, dbSession)
147 .orElseThrow(() -> new IllegalStateException(String.format("Unknown Template Rule '%s'", templateUuid)));
148 ruleChange.setTemplateKey(templateRule.getKey().toString());
155 public void distributeRuleChangeEvent(Collection<QProfileDto> profiles, List<ActiveRuleChange> activeRuleChanges, String language) {
156 if (activeRuleChanges.isEmpty()) {
160 Set<RuleChange> activatedRules = new HashSet<>();
162 for (ActiveRuleChange arc : activeRuleChanges) {
163 ActiveRuleDto activeRule = arc.getActiveRule();
164 if (activeRule == null) {
168 RuleChange ruleChange = new RuleChange();
169 ruleChange.setKey(activeRule.getRuleKey().toString());
170 ruleChange.setSeverity(arc.getSeverity());
171 ruleChange.setLanguage(language);
173 Optional<String> templateKey = templateKey(arc);
174 templateKey.ifPresent(ruleChange::setTemplateKey);
177 List<ParamChange> paramChanges = new ArrayList<>();
178 for (Map.Entry<String, String> entry : arc.getParameters().entrySet()) {
179 paramChanges.add(new ParamChange(entry.getKey(), entry.getValue()));
181 ruleChange.setParams(paramChanges.toArray(new ParamChange[0]));
183 if (ACTIVATED.equals(arc.getType()) || UPDATED.equals(arc.getType())) {
184 activatedRules.add(ruleChange);
188 Set<String> deactivatedRules = activeRuleChanges.stream()
189 .filter(r -> DEACTIVATED.equals(r.getType()))
190 .map(ActiveRuleChange::getActiveRule)
191 .filter(not(Objects::isNull))
192 .map(ActiveRuleDto::getRuleKey)
193 .map(RuleKey::toString)
194 .collect(Collectors.toSet());
196 Set<String> projectKeys = getProjectKeys(profiles);
198 if (activatedRules.isEmpty() && deactivatedRules.isEmpty()) {
202 RuleSetChangedEvent event = new RuleSetChangedEvent(projectKeys.toArray(new String[0]), activatedRules.toArray(new RuleChange[0]), deactivatedRules.toArray(new String[0]),
204 eventsDistributor.pushEvent(event);
207 private Optional<String> templateKey(ActiveRuleChange arc) {
208 try (DbSession dbSession = dbClient.openSession(false)) {
209 String ruleUuid = arc.getRuleUuid();
210 RuleDto rule = dbClient.ruleDao().selectByUuid(ruleUuid, dbSession).orElseThrow(() -> new IllegalStateException("unknow rule"));
211 String templateUuid = rule.getTemplateUuid();
213 if (StringUtils.isNotEmpty(templateUuid)) {
214 RuleDto templateRule = dbClient.ruleDao().selectByUuid(templateUuid, dbSession)
215 .orElseThrow(() -> new IllegalStateException(String.format("Unknown Template Rule '%s'", templateUuid)));
216 return Optional.of(templateRule.getKey().toString());
219 return Optional.empty();
222 private Set<String> getProjectKeys(Collection<QProfileDto> profiles) {
223 Set<String> projectKeys = new HashSet<>();
224 try (DbSession dbSession = dbClient.openSession(false)) {
225 for (QProfileDto profileDto : profiles) {
226 List<ProjectQprofileAssociationDto> associationDtos = dbClient.qualityProfileDao().selectSelectedProjects(dbSession, profileDto, null);
227 for (ProjectQprofileAssociationDto associationDto : associationDtos) {
228 projectKeys.add(associationDto.getProjectKey());