3 * Copyright (C) 2009-2020 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;
22 import com.google.common.base.Splitter;
23 import com.google.common.collect.ImmutableSet;
24 import com.google.common.collect.Sets;
25 import java.util.Collections;
26 import java.util.Date;
27 import java.util.HashMap;
28 import java.util.List;
30 import java.util.Objects;
31 import java.util.Optional;
33 import java.util.function.Function;
34 import java.util.stream.Collectors;
35 import javax.annotation.CheckForNull;
36 import javax.annotation.Nullable;
37 import org.sonar.api.rule.RuleKey;
38 import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition;
39 import org.sonar.api.server.rule.RuleParamType;
40 import org.sonar.api.utils.System2;
41 import org.sonar.core.util.UuidFactory;
42 import org.sonar.core.util.stream.MoreCollectors;
43 import org.sonar.db.DbClient;
44 import org.sonar.db.DbSession;
45 import org.sonar.db.qualityprofile.ActiveRuleDto;
46 import org.sonar.db.qualityprofile.ActiveRuleKey;
47 import org.sonar.db.qualityprofile.ActiveRuleParamDto;
48 import org.sonar.db.qualityprofile.DefaultQProfileDto;
49 import org.sonar.db.qualityprofile.OrgQProfileDto;
50 import org.sonar.db.qualityprofile.RulesProfileDto;
51 import org.sonar.db.rule.RuleDefinitionDto;
52 import org.sonar.db.rule.RuleParamDto;
53 import org.sonar.server.qualityprofile.index.ActiveRuleIndexer;
54 import org.sonar.server.util.TypeValidations;
56 import static com.google.common.base.MoreObjects.firstNonNull;
57 import static com.google.common.collect.Lists.newArrayList;
58 import static java.util.Objects.requireNonNull;
60 public class BuiltInQProfileInsertImpl implements BuiltInQProfileInsert {
61 private final DbClient dbClient;
62 private final System2 system2;
63 private final UuidFactory uuidFactory;
64 private final TypeValidations typeValidations;
65 private final ActiveRuleIndexer activeRuleIndexer;
66 private RuleRepository ruleRepository;
68 public BuiltInQProfileInsertImpl(DbClient dbClient, System2 system2, UuidFactory uuidFactory, TypeValidations typeValidations, ActiveRuleIndexer activeRuleIndexer) {
69 this.dbClient = dbClient;
70 this.system2 = system2;
71 this.uuidFactory = uuidFactory;
72 this.typeValidations = typeValidations;
73 this.activeRuleIndexer = activeRuleIndexer;
77 public void create(DbSession dbSession, DbSession batchDbSession, BuiltInQProfile builtInQProfile) {
78 initRuleRepository(batchDbSession);
80 Date now = new Date(system2.now());
81 RulesProfileDto ruleProfile = insertRulesProfile(dbSession, builtInQProfile, now);
83 List<ActiveRuleChange> changes = builtInQProfile.getActiveRules()
85 .map(activeRule -> insertActiveRule(dbSession, ruleProfile, activeRule, now.getTime()))
86 .collect(MoreCollectors.toList());
88 changes.forEach(change -> dbClient.qProfileChangeDao().insert(batchDbSession, change.toDto(null)));
90 createDefaultAndOrgQProfiles(dbSession, batchDbSession, builtInQProfile, ruleProfile);
92 // TODO batch statements should be executed through dbSession
93 batchDbSession.commit();
95 activeRuleIndexer.commitAndIndex(dbSession, changes);
98 private void createDefaultAndOrgQProfiles(DbSession dbSession, DbSession batchDbSession, BuiltInQProfile builtIn, RulesProfileDto rulesProfileDto) {
99 Optional<String> qProfileUuid = dbClient.defaultQProfileDao().selectDefaultQProfileUuid(dbSession, builtIn.getLanguage());
101 OrgQProfileDto dto = new OrgQProfileDto()
102 .setRulesProfileUuid(rulesProfileDto.getUuid())
103 .setUuid(uuidFactory.create());
105 if (builtIn.isDefault() && !qProfileUuid.isPresent()) {
106 DefaultQProfileDto defaultQProfileDto = new DefaultQProfileDto()
107 .setQProfileUuid(dto.getUuid())
108 .setLanguage(builtIn.getLanguage());
109 dbClient.defaultQProfileDao().insertOrUpdate(dbSession, defaultQProfileDto);
112 dbClient.qualityProfileDao().insert(batchDbSession, dto);
115 private void initRuleRepository(DbSession dbSession) {
116 if (ruleRepository == null) {
117 ruleRepository = new RuleRepository(dbClient, dbSession);
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 dbSession, RulesProfileDto rulesProfileDto, BuiltInQProfile.ActiveRule activeRule, long now) {
133 RuleKey ruleKey = activeRule.getRuleKey();
134 RuleDefinitionDto 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(dbSession, dto);
146 List<ActiveRuleParamDto> paramDtos = insertActiveRuleParams(dbSession, 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,
155 ActiveRuleDto activeRuleDto) {
156 Map<String, String> valuesByParamKey = activeRule.getParams()
158 .collect(MoreCollectors.uniqueIndex(BuiltInQualityProfilesDefinition.OverriddenParam::key, BuiltInQualityProfilesDefinition.OverriddenParam::overriddenValue));
159 return ruleRepository.getRuleParams(activeRule.getRuleKey())
162 String activeRuleValue = valuesByParamKey.get(param.getName());
163 return createParamDto(param, activeRuleValue == null ? param.getDefaultValue() : activeRuleValue);
165 .filter(Objects::nonNull)
166 .peek(paramDto -> dbClient.activeRuleDao().insertParam(session, activeRuleDto, paramDto))
167 .collect(MoreCollectors.toList());
171 private ActiveRuleParamDto createParamDto(RuleParamDto param, @Nullable String value) {
175 ActiveRuleParamDto paramDto = ActiveRuleParamDto.createFor(param);
176 paramDto.setValue(validateParam(param, value));
180 private String validateParam(RuleParamDto ruleParam, String value) {
181 RuleParamType ruleParamType = RuleParamType.parse(ruleParam.getType());
182 if (ruleParamType.multiple()) {
183 List<String> values = newArrayList(Splitter.on(",").split(value));
184 typeValidations.validate(values, ruleParamType.type(), ruleParamType.values());
186 typeValidations.validate(value, ruleParamType.type(), ruleParamType.values());
191 private static class RuleRepository {
192 private final Map<RuleKey, RuleDefinitionDto> definitions;
193 private final Map<RuleKey, Set<RuleParamDto>> params;
195 private RuleRepository(DbClient dbClient, DbSession session) {
196 this.definitions = dbClient.ruleDao().selectAllDefinitions(session)
198 .collect(Collectors.toMap(RuleDefinitionDto::getKey, Function.identity()));
199 Map<String, RuleKey> ruleUuidsByKey = definitions.values()
201 .collect(MoreCollectors.uniqueIndex(RuleDefinitionDto::getUuid, RuleDefinitionDto::getKey));
202 this.params = new HashMap<>(ruleUuidsByKey.size());
203 dbClient.ruleDao().selectRuleParamsByRuleKeys(session, definitions.keySet())
204 .forEach(ruleParam -> params.compute(
205 ruleUuidsByKey.get(ruleParam.getRuleUuid()),
208 return ImmutableSet.of(ruleParam);
210 return ImmutableSet.copyOf(Sets.union(value, Collections.singleton(ruleParam)));
214 private Optional<RuleDefinitionDto> getDefinition(RuleKey ruleKey) {
215 return Optional.ofNullable(definitions.get(requireNonNull(ruleKey, "RuleKey can't be null")));
218 private Set<RuleParamDto> getRuleParams(RuleKey ruleKey) {
219 Set<RuleParamDto> res = params.get(requireNonNull(ruleKey, "RuleKey can't be null"));
220 return res == null ? Collections.emptySet() : res;