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.v2.api.rule.converter;
22 import java.util.ArrayList;
23 import java.util.List;
24 import java.util.Locale;
25 import java.util.Optional;
27 import javax.annotation.CheckForNull;
28 import org.jetbrains.annotations.Nullable;
29 import org.sonar.api.resources.Language;
30 import org.sonar.api.resources.Languages;
31 import org.sonar.api.rules.CleanCodeAttribute;
32 import org.sonar.api.rules.RuleType;
33 import org.sonar.api.server.debt.DebtRemediationFunction;
34 import org.sonar.api.server.debt.internal.DefaultDebtRemediationFunction;
35 import org.sonar.api.utils.DateUtils;
36 import org.sonar.db.issue.ImpactDto;
37 import org.sonar.db.rule.RuleDescriptionSectionDto;
38 import org.sonar.db.rule.RuleDto;
39 import org.sonar.db.rule.RuleParamDto;
40 import org.sonar.markdown.Markdown;
41 import org.sonar.server.common.rule.service.RuleInformation;
42 import org.sonar.server.common.text.MacroInterpreter;
43 import org.sonar.server.rule.RuleDescriptionFormatter;
44 import org.sonar.server.v2.api.rule.enums.CleanCodeAttributeCategoryRestEnum;
45 import org.sonar.server.v2.api.rule.enums.CleanCodeAttributeRestEnum;
46 import org.sonar.server.v2.api.rule.enums.ImpactSeverityRestEnum;
47 import org.sonar.server.v2.api.rule.enums.RuleStatusRestEnum;
48 import org.sonar.server.v2.api.rule.enums.RuleTypeRestEnum;
49 import org.sonar.server.v2.api.rule.enums.SoftwareQualityRestEnum;
50 import org.sonar.server.v2.api.rule.response.RuleDescriptionSectionContextRestResponse;
51 import org.sonar.server.v2.api.rule.response.RuleDescriptionSectionRestResponse;
52 import org.sonar.server.v2.api.rule.response.RuleImpactRestResponse;
53 import org.sonar.server.v2.api.rule.response.RuleParameterRestResponse;
54 import org.sonar.server.v2.api.rule.response.RuleRestResponse;
56 import static java.util.Optional.ofNullable;
57 import static org.sonar.db.rule.RuleDto.Format.MARKDOWN;
59 public class RuleRestResponseGenerator {
61 private final Languages languages;
62 private final MacroInterpreter macroInterpreter;
63 private final RuleDescriptionFormatter ruleDescriptionFormatter;
65 public RuleRestResponseGenerator(Languages languages, MacroInterpreter macroInterpreter, RuleDescriptionFormatter ruleDescriptionFormatter) {
67 this.languages = languages;
68 this.macroInterpreter = macroInterpreter;
69 this.ruleDescriptionFormatter = ruleDescriptionFormatter;
72 public RuleRestResponse toRuleRestResponse(RuleInformation ruleInformation) {
74 RuleRestResponse.Builder builder = RuleRestResponse.Builder.builder();
75 RuleDto ruleDto = ruleInformation.ruleDto();
77 .setId(ruleDto.getUuid())
78 .setKey(ruleDto.getKey().toString())
79 .setRepositoryKey(ruleDto.getRepositoryKey())
80 .setName(ruleDto.getName())
81 .setSeverity(ruleDto.getSeverityString())
82 .setType(RuleTypeRestEnum.from(RuleType.valueOf(ruleDto.getType())))
83 .setImpacts(toImpactRestResponse(ruleDto.getDefaultImpacts()))
84 .setCleanCodeAttribute(CleanCodeAttributeRestEnum.from(ruleDto.getCleanCodeAttribute()))
85 .setCleanCodeAttributeCategory(ofNullable(ruleDto.getCleanCodeAttribute())
86 .map(CleanCodeAttribute::getAttributeCategory)
87 .map(CleanCodeAttributeCategoryRestEnum::from)
89 .setStatus(RuleStatusRestEnum.from(ruleDto.getStatus()))
90 .setExternal(ruleDto.isExternal())
91 .setCreatedAt(toDateTime(ruleDto.getCreatedAt()))
92 .setGapDescription(ruleDto.getGapDescription())
93 .setHtmlNote(ofNullable(ruleDto.getNoteData()).map(n -> macroInterpreter.interpret(Markdown.convertToHtml(n))).orElse(null))
94 .setMarkdownNote(ruleDto.getNoteData())
95 .setEducationPrinciples(new ArrayList<>(ruleDto.getEducationPrinciples()))
96 .setTemplate(ruleDto.isTemplate())
97 .setTemplateId(ruleDto.getTemplateUuid())
98 .setTags(new ArrayList<>(ruleDto.getTags()))
99 .setSystemTags(new ArrayList<>(ruleDto.getSystemTags()))
100 .setLanguageKey(ruleDto.getLanguage())
101 .setLanguageName(getLanguageName(ruleDto.getLanguage()))
102 .setParameters(toRuleParameterResponse(ruleInformation.params()));
104 setDescriptionFields(builder, ruleDto);
105 setRemediationFunctionFields(builder, ruleDto);
107 if (ruleDto.isAdHoc()) {
108 ofNullable(ruleDto.getAdHocName()).ifPresent(builder::setName);
109 ofNullable(ruleDto.getAdHocDescription())
110 .map(this::toDescriptionSectionResponse)
111 .ifPresent(section -> builder.setDescriptionSections(List.of(section)));
112 ofNullable(ruleDto.getAdHocSeverity()).ifPresent(builder::setSeverity);
113 ofNullable(ruleDto.getAdHocType()).ifPresent(type -> builder.setType(RuleTypeRestEnum.from(RuleType.valueOf(type))));
115 return builder.build();
118 private static void setRemediationFunctionFields(RuleRestResponse.Builder builder, RuleDto ruleDto) {
119 ofNullable(debtRemediationFunction(ruleDto))
120 .ifPresent(function -> {
121 builder.setRemediationFunctionBaseEffort(function.baseEffort());
122 builder.setRemediationFunctionGapMultiplier(function.gapMultiplier());
123 ofNullable(function.type()).map(Enum::name).ifPresent(builder::setRemediationFunctionType);
127 private static List<RuleParameterRestResponse> toRuleParameterResponse(List<RuleParamDto> ruleParamDtos) {
128 return ruleParamDtos.stream()
129 .map(p -> new RuleParameterRestResponse(p.getName(), Markdown.convertToHtml(p.getDescription()), p.getDefaultValue(), p.getType()))
134 private String getLanguageName(@Nullable String languageKey) {
135 if (languageKey == null) {
138 Language language = languages.get(languageKey);
139 return language == null ? languageKey : language.getName();
142 private void setDescriptionFields(RuleRestResponse.Builder builder, RuleDto ruleDto) {
143 builder.setDescriptionSections(ruleDto.getRuleDescriptionSectionDtos().stream()
144 .map(sectionDto -> toDescriptionSectionResponse(ruleDto, sectionDto))
147 String htmlDescription = ruleDescriptionFormatter.getDescriptionAsHtml(ruleDto);
148 if (MARKDOWN.equals(ruleDto.getDescriptionFormat())) {
149 Optional.ofNullable(ruleDto.getDefaultRuleDescriptionSection())
150 .map(RuleDescriptionSectionDto::getContent)
151 .ifPresent(builder::setMarkdownDescription);
152 } else if (htmlDescription != null) {
153 builder.setMarkdownDescription(macroInterpreter.interpret(htmlDescription));
157 private RuleDescriptionSectionRestResponse toDescriptionSectionResponse(RuleDto ruleDto, RuleDescriptionSectionDto section) {
158 String htmlContent = ruleDescriptionFormatter.toHtml(ruleDto.getDescriptionFormat(), section);
159 String interpretedHtmlContent = macroInterpreter.interpret(htmlContent);
160 return new RuleDescriptionSectionRestResponse(section.getKey(), interpretedHtmlContent,
161 ofNullable(section.getContext())
162 .map(c -> new RuleDescriptionSectionContextRestResponse(c.getKey(), c.getDisplayName()))
166 private RuleDescriptionSectionRestResponse toDescriptionSectionResponse(String description) {
167 return new RuleDescriptionSectionRestResponse(RuleDescriptionSectionDto.DEFAULT_KEY, macroInterpreter.interpret(description), null);
170 private static List<RuleImpactRestResponse> toImpactRestResponse(Set<ImpactDto> defaultImpacts) {
171 return defaultImpacts.stream()
172 .map(i -> new RuleImpactRestResponse(SoftwareQualityRestEnum.from(i.getSoftwareQuality()), ImpactSeverityRestEnum.from(i.getSeverity())))
177 private static DebtRemediationFunction defaultDebtRemediationFunction(final RuleDto ruleDto) {
178 final String function = ruleDto.getDefRemediationFunction();
179 if (function == null || function.isEmpty()) {
182 return new DefaultDebtRemediationFunction(
183 DebtRemediationFunction.Type.valueOf(function.toUpperCase(Locale.ENGLISH)),
184 ruleDto.getDefRemediationGapMultiplier(),
185 ruleDto.getDefRemediationBaseEffort());
190 private static DebtRemediationFunction debtRemediationFunction(RuleDto ruleDto) {
191 final String function = ruleDto.getRemediationFunction();
192 if (function == null || function.isEmpty()) {
193 return defaultDebtRemediationFunction(ruleDto);
195 return new DefaultDebtRemediationFunction(
196 DebtRemediationFunction.Type.valueOf(function.toUpperCase(Locale.ENGLISH)),
197 ruleDto.getRemediationGapMultiplier(),
198 ruleDto.getRemediationBaseEffort());
202 private static String toDateTime(@Nullable Long dateTimeMs) {
203 return Optional.ofNullable(dateTimeMs).map(DateUtils::formatDateTime).orElse(null);