3 * Copyright (C) 2009-2021 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 java.io.Reader;
23 import java.io.Writer;
24 import java.util.ArrayList;
25 import java.util.Collections;
26 import java.util.Comparator;
27 import java.util.List;
29 import java.util.Objects;
30 import java.util.function.Function;
31 import java.util.stream.Collectors;
32 import javax.annotation.Nullable;
33 import org.apache.commons.lang.builder.CompareToBuilder;
34 import org.sonar.api.rule.RuleKey;
35 import org.sonar.api.rule.RuleStatus;
36 import org.sonar.api.rules.RuleType;
37 import org.sonar.api.server.ServerSide;
38 import org.sonar.core.util.stream.MoreCollectors;
39 import org.sonar.db.DbClient;
40 import org.sonar.db.DbSession;
41 import org.sonar.db.qualityprofile.ExportRuleDto;
42 import org.sonar.db.qualityprofile.ExportRuleParamDto;
43 import org.sonar.db.qualityprofile.QProfileDto;
44 import org.sonar.db.rule.RuleDefinitionDto;
45 import org.sonar.server.rule.NewCustomRule;
46 import org.sonar.server.rule.RuleCreator;
48 import static com.google.common.base.Preconditions.checkArgument;
51 public class QProfileBackuperImpl implements QProfileBackuper {
53 private final DbClient db;
54 private final QProfileReset profileReset;
55 private final QProfileFactory profileFactory;
56 private final RuleCreator ruleCreator;
57 private final QProfileParser qProfileParser;
59 public QProfileBackuperImpl(DbClient db, QProfileReset profileReset, QProfileFactory profileFactory,
60 RuleCreator ruleCreator, QProfileParser qProfileParser) {
62 this.profileReset = profileReset;
63 this.profileFactory = profileFactory;
64 this.ruleCreator = ruleCreator;
65 this.qProfileParser = qProfileParser;
69 public void backup(DbSession dbSession, QProfileDto profile, Writer writer) {
70 List<ExportRuleDto> rulesToExport = db.qualityProfileExportDao().selectRulesByProfile(dbSession, profile);
71 rulesToExport.sort(BackupActiveRuleComparator.INSTANCE);
72 qProfileParser.writeXml(writer, profile, rulesToExport.iterator());
76 public QProfileRestoreSummary copy(DbSession dbSession, QProfileDto from, QProfileDto to) {
77 List<ExportRuleDto> rulesToExport = db.qualityProfileExportDao().selectRulesByProfile(dbSession, from);
78 rulesToExport.sort(BackupActiveRuleComparator.INSTANCE);
80 ImportedQProfile qProfile = toImportedQProfile(rulesToExport, to.getName(), to.getLanguage());
81 return restore(dbSession, qProfile, name -> to);
84 private static ImportedQProfile toImportedQProfile(List<ExportRuleDto> exportRules, String profileName, String profileLang) {
85 List<ImportedRule> importedRules = new ArrayList<>(exportRules.size());
87 for (ExportRuleDto exportRuleDto : exportRules) {
88 ImportedRule importedRule = new ImportedRule();
89 importedRule.setName(exportRuleDto.getName());
90 importedRule.setDescription(exportRuleDto.getDescription());
91 importedRule.setRuleKey(exportRuleDto.getRuleKey());
92 importedRule.setSeverity(exportRuleDto.getSeverityString());
93 if (importedRule.isCustomRule()) {
94 importedRule.setTemplateKey(exportRuleDto.getTemplateRuleKey());
96 importedRule.setType(exportRuleDto.getRuleType().name());
97 importedRule.setParameters(exportRuleDto.getParams().stream().collect(Collectors.toMap(ExportRuleParamDto::getKey, ExportRuleParamDto::getValue)));
98 importedRules.add(importedRule);
101 return new ImportedQProfile(profileName, profileLang, importedRules);
105 public QProfileRestoreSummary restore(DbSession dbSession, Reader backup, @Nullable String overriddenProfileName) {
106 return restore(dbSession, backup, nameInBackup -> {
107 QProfileName targetName = nameInBackup;
108 if (overriddenProfileName != null) {
109 targetName = new QProfileName(nameInBackup.getLanguage(), overriddenProfileName);
111 return profileFactory.getOrCreateCustom(dbSession, targetName);
116 public QProfileRestoreSummary restore(DbSession dbSession, Reader backup, QProfileDto profile) {
117 return restore(dbSession, backup, nameInBackup -> {
118 checkArgument(profile.getLanguage().equals(nameInBackup.getLanguage()),
119 "Can't restore %s backup on %s profile with key [%s]. Languages are different.", nameInBackup.getLanguage(), profile.getLanguage(), profile.getKee());
124 private QProfileRestoreSummary restore(DbSession dbSession, Reader backup, Function<QProfileName, QProfileDto> profileLoader) {
125 ImportedQProfile qProfile = qProfileParser.readXml(backup);
126 return restore(dbSession, qProfile, profileLoader);
129 private QProfileRestoreSummary restore(DbSession dbSession, ImportedQProfile qProfile, Function<QProfileName, QProfileDto> profileLoader) {
130 QProfileName targetName = new QProfileName(qProfile.getProfileLang(), qProfile.getProfileName());
131 QProfileDto targetProfile = profileLoader.apply(targetName);
133 List<ImportedRule> importedRules = qProfile.getRules();
135 Map<RuleKey, RuleDefinitionDto> ruleDefinitionsByKey = getImportedRulesDefinitions(dbSession, importedRules);
136 checkIfRulesFromExternalEngines(ruleDefinitionsByKey);
138 Map<RuleKey, RuleDefinitionDto> customRulesDefinitions = createCustomRulesIfNotExist(dbSession, importedRules, ruleDefinitionsByKey);
139 ruleDefinitionsByKey.putAll(customRulesDefinitions);
141 List<RuleActivation> ruleActivations = toRuleActivations(importedRules, ruleDefinitionsByKey);
143 BulkChangeResult changes = profileReset.reset(dbSession, targetProfile, ruleActivations);
144 return new QProfileRestoreSummary(targetProfile, changes);
147 private Map<RuleKey, RuleDefinitionDto> getImportedRulesDefinitions(DbSession dbSession, List<ImportedRule> rules) {
148 List<RuleKey> ruleKeys = rules.stream()
149 .map(ImportedRule::getRuleKey)
150 .collect(MoreCollectors.toList());
151 return db.ruleDao().selectDefinitionByKeys(dbSession, ruleKeys)
153 .collect(Collectors.toMap(RuleDefinitionDto::getKey, Function.identity()));
156 private static void checkIfRulesFromExternalEngines(Map<RuleKey, RuleDefinitionDto> ruleDefinitionsByKey) {
157 List<RuleDefinitionDto> externalRules = ruleDefinitionsByKey.values().stream()
158 .filter(RuleDefinitionDto::isExternal)
159 .collect(Collectors.toList());
161 if (!externalRules.isEmpty()) {
162 throw new IllegalArgumentException("The quality profile cannot be restored as it contains rules from external rule engines: "
163 + externalRules.stream().map(r -> r.getKey().toString()).collect(Collectors.joining(", ")));
167 private Map<RuleKey, RuleDefinitionDto> createCustomRulesIfNotExist(DbSession dbSession, List<ImportedRule> rules, Map<RuleKey, RuleDefinitionDto> ruleDefinitionsByKey) {
168 List<NewCustomRule> customRulesToCreate = rules.stream()
169 .filter(r -> ruleDefinitionsByKey.get(r.getRuleKey()) == null && r.isCustomRule())
170 .map(QProfileBackuperImpl::importedRuleToNewCustomRule)
171 .collect(Collectors.toList());
173 if (!customRulesToCreate.isEmpty()) {
174 return db.ruleDao().selectDefinitionByKeys(dbSession, ruleCreator.create(dbSession, customRulesToCreate))
176 .collect(Collectors.toMap(RuleDefinitionDto::getKey, Function.identity()));
178 return Collections.emptyMap();
181 private static NewCustomRule importedRuleToNewCustomRule(ImportedRule r) {
182 return NewCustomRule.createForCustomRule(r.getRuleKey().rule(), r.getTemplateKey())
183 .setName(r.getName())
184 .setMarkdownDescription(r.getDescription())
185 .setSeverity(r.getSeverity())
186 .setStatus(RuleStatus.READY)
187 .setPreventReactivation(true)
188 .setType(RuleType.valueOf(r.getType()))
189 .setParameters(r.getParameters());
192 private static List<RuleActivation> toRuleActivations(List<ImportedRule> rules, Map<RuleKey, RuleDefinitionDto> ruleDefinitionsByKey) {
193 return rules.stream()
195 RuleDefinitionDto ruleDefinition = ruleDefinitionsByKey.get(r.getRuleKey());
196 if (ruleDefinition == null) {
199 return RuleActivation.create(ruleDefinition.getUuid(), r.getSeverity(), r.getParameters());
201 .filter(Objects::nonNull)
202 .collect(MoreCollectors.toList(rules.size()));
205 private enum BackupActiveRuleComparator implements Comparator<ExportRuleDto> {
209 public int compare(ExportRuleDto o1, ExportRuleDto o2) {
210 RuleKey rk1 = o1.getRuleKey();
211 RuleKey rk2 = o2.getRuleKey();
212 return new CompareToBuilder()
213 .append(rk1.repository(), rk2.repository())
214 .append(rk1.rule(), rk2.rule())