3 * Copyright (C) 2009-2023 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.qualityprofile.builtin;
22 import com.google.common.base.Splitter;
23 import java.util.Date;
24 import java.util.HashMap;
25 import java.util.HashSet;
26 import java.util.List;
28 import java.util.Objects;
29 import java.util.Optional;
31 import java.util.stream.Collectors;
32 import javax.annotation.CheckForNull;
33 import javax.annotation.Nullable;
34 import org.sonar.api.rule.RuleKey;
35 import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition;
36 import org.sonar.api.server.rule.RuleParamType;
37 import org.sonar.api.utils.System2;
38 import org.sonar.core.util.UuidFactory;
39 import org.sonar.core.util.stream.MoreCollectors;
40 import org.sonar.db.DbClient;
41 import org.sonar.db.DbSession;
42 import org.sonar.db.qualityprofile.ActiveRuleDto;
43 import org.sonar.db.qualityprofile.ActiveRuleKey;
44 import org.sonar.db.qualityprofile.ActiveRuleParamDto;
45 import org.sonar.db.qualityprofile.DefaultQProfileDto;
46 import org.sonar.db.qualityprofile.OrgQProfileDto;
47 import org.sonar.db.qualityprofile.RulesProfileDto;
48 import org.sonar.db.rule.RuleDto;
49 import org.sonar.db.rule.RuleParamDto;
50 import org.sonar.server.qualityprofile.ActiveRuleChange;
51 import org.sonar.server.qualityprofile.index.ActiveRuleIndexer;
52 import org.sonar.server.rule.ServerRuleFinder;
53 import org.sonar.server.util.TypeValidations;
55 import static com.google.common.base.MoreObjects.firstNonNull;
56 import static com.google.common.collect.Lists.newArrayList;
57 import static java.util.Collections.emptySet;
58 import static java.util.Objects.requireNonNull;
60 public class BuiltInQProfileInsertImpl implements BuiltInQProfileInsert {
61 private final DbClient dbClient;
62 private final ServerRuleFinder ruleFinder;
63 private final System2 system2;
64 private final UuidFactory uuidFactory;
65 private final TypeValidations typeValidations;
66 private final ActiveRuleIndexer activeRuleIndexer;
67 private RuleRepository ruleRepository;
69 public BuiltInQProfileInsertImpl(DbClient dbClient, ServerRuleFinder ruleFinder, System2 system2, UuidFactory uuidFactory,
70 TypeValidations typeValidations, ActiveRuleIndexer activeRuleIndexer) {
71 this.dbClient = dbClient;
72 this.ruleFinder = ruleFinder;
73 this.system2 = system2;
74 this.uuidFactory = uuidFactory;
75 this.typeValidations = typeValidations;
76 this.activeRuleIndexer = activeRuleIndexer;
80 public void create(DbSession batchDbSession, BuiltInQProfile builtInQProfile) {
81 initRuleRepository(batchDbSession);
83 Date now = new Date(system2.now());
84 RulesProfileDto ruleProfile = insertRulesProfile(batchDbSession, builtInQProfile, now);
86 List<ActiveRuleChange> changes = builtInQProfile.getActiveRules().stream()
87 .map(activeRule -> insertActiveRule(batchDbSession, ruleProfile, activeRule, now.getTime()))
88 .collect(MoreCollectors.toList());
90 changes.forEach(change -> dbClient.qProfileChangeDao().insert(batchDbSession, change.toDto(null)));
92 createDefaultAndOrgQProfiles(batchDbSession, builtInQProfile, ruleProfile);
94 activeRuleIndexer.commitAndIndex(batchDbSession, changes);
98 private void createDefaultAndOrgQProfiles(DbSession batchDbSession, BuiltInQProfile builtIn, RulesProfileDto rulesProfileDto) {
99 Optional<String> qProfileUuid = dbClient.defaultQProfileDao().selectDefaultQProfileUuid(batchDbSession, builtIn.getLanguage());
101 OrgQProfileDto dto = new OrgQProfileDto()
102 .setRulesProfileUuid(rulesProfileDto.getUuid())
103 .setUuid(uuidFactory.create());
105 if (builtIn.isDefault() && qProfileUuid.isEmpty()) {
106 DefaultQProfileDto defaultQProfileDto = new DefaultQProfileDto()
107 .setQProfileUuid(dto.getUuid())
108 .setLanguage(builtIn.getLanguage());
109 dbClient.defaultQProfileDao().insert(batchDbSession, defaultQProfileDto);
112 dbClient.qualityProfileDao().insert(batchDbSession, dto);
115 private void initRuleRepository(DbSession dbSession) {
116 if (ruleRepository == null) {
117 ruleRepository = new RuleRepository(dbClient, dbSession, ruleFinder);
121 private RulesProfileDto insertRulesProfile(DbSession dbSession, BuiltInQProfile builtIn, Date now) {
122 RulesProfileDto dto = new RulesProfileDto()
123 .setUuid(uuidFactory.create())
124 .setName(builtIn.getName())
125 .setLanguage(builtIn.getLanguage())
127 .setRulesUpdatedAtAsDate(now);
128 dbClient.qualityProfileDao().insert(dbSession, dto);
132 private ActiveRuleChange insertActiveRule(DbSession batchDbSession, RulesProfileDto rulesProfileDto, BuiltInQProfile.ActiveRule activeRule, long now) {
133 RuleKey ruleKey = activeRule.getRuleKey();
134 RuleDto ruleDefinitionDto = ruleRepository.getDefinition(ruleKey)
135 .orElseThrow(() -> new IllegalStateException("RuleDefinition not found for key " + ruleKey));
137 ActiveRuleDto dto = new ActiveRuleDto();
138 dto.setProfileUuid(rulesProfileDto.getUuid());
139 dto.setRuleUuid(ruleDefinitionDto.getUuid());
140 dto.setKey(ActiveRuleKey.of(rulesProfileDto, ruleDefinitionDto.getKey()));
141 dto.setSeverity(firstNonNull(activeRule.getSeverity(), ruleDefinitionDto.getSeverityString()));
142 dto.setUpdatedAt(now);
143 dto.setCreatedAt(now);
144 dbClient.activeRuleDao().insert(batchDbSession, dto);
146 List<ActiveRuleParamDto> paramDtos = insertActiveRuleParams(batchDbSession, activeRule, dto);
148 ActiveRuleChange change = new ActiveRuleChange(ActiveRuleChange.Type.ACTIVATED, dto, ruleDefinitionDto);
149 change.setSeverity(dto.getSeverityString());
150 paramDtos.forEach(paramDto -> change.setParameter(paramDto.getKey(), paramDto.getValue()));
154 private List<ActiveRuleParamDto> insertActiveRuleParams(DbSession session, BuiltInQProfile.ActiveRule activeRule, ActiveRuleDto activeRuleDto) {
155 Map<String, String> valuesByParamKey = activeRule.getParams().stream()
156 .collect(MoreCollectors.uniqueIndex(BuiltInQualityProfilesDefinition.OverriddenParam::key, BuiltInQualityProfilesDefinition.OverriddenParam::overriddenValue));
157 List<ActiveRuleParamDto> rules = ruleRepository.getRuleParams(activeRule.getRuleKey()).stream()
158 .map(param -> createParamDto(param, Optional.ofNullable(valuesByParamKey.get(param.getName())).orElse(param.getDefaultValue())))
159 .filter(Objects::nonNull)
160 .collect(Collectors.toList());
162 rules.forEach(paramDto -> dbClient.activeRuleDao().insertParam(session, activeRuleDto, paramDto));
167 private ActiveRuleParamDto createParamDto(RuleParamDto param, @Nullable String value) {
171 ActiveRuleParamDto paramDto = ActiveRuleParamDto.createFor(param);
172 paramDto.setValue(validateParam(param, value));
176 private String validateParam(RuleParamDto ruleParam, String value) {
177 RuleParamType ruleParamType = RuleParamType.parse(ruleParam.getType());
178 if (ruleParamType.multiple()) {
179 List<String> values = newArrayList(Splitter.on(",").split(value));
180 typeValidations.validate(values, ruleParamType.type(), ruleParamType.values());
182 typeValidations.validate(value, ruleParamType.type(), ruleParamType.values());
187 private static class RuleRepository {
188 private final Map<RuleKey, Set<RuleParamDto>> params;
189 private final ServerRuleFinder ruleFinder;
191 private RuleRepository(DbClient dbClient, DbSession session, ServerRuleFinder ruleFinder) {
192 this.ruleFinder = ruleFinder;
193 this.params = new HashMap<>();
195 for (RuleParamDto ruleParam : dbClient.ruleDao().selectAllRuleParams(session)) {
196 Optional<RuleKey> ruleKey = ruleFinder.findDtoByUuid(ruleParam.getRuleUuid())
197 .map(r -> RuleKey.of(r.getRepositoryKey(), r.getRuleKey()));
199 if (ruleKey.isPresent()) {
200 params.computeIfAbsent(ruleKey.get(), r -> new HashSet<>()).add(ruleParam);
205 private Optional<RuleDto> getDefinition(RuleKey ruleKey) {
206 return ruleFinder.findDtoByKey(requireNonNull(ruleKey, "RuleKey can't be null"));
209 private Set<RuleParamDto> getRuleParams(RuleKey ruleKey) {
210 return params.getOrDefault(requireNonNull(ruleKey, "RuleKey can't be null"), emptySet());