]> source.dussan.org Git - sonarqube.git/blob
2e06e4ff9d863aa1121eab3f9f4e574af61605eb
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2023 SonarSource SA
4  * mailto:info AT sonarsource DOT com
5  *
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.
10  *
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.
15  *
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.
19  */
20 package org.sonar.server.v2.api.rule.converter;
21
22 import java.util.ArrayList;
23 import java.util.List;
24 import java.util.Locale;
25 import java.util.Optional;
26 import java.util.Set;
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.ressource.Parameter;
54 import org.sonar.server.v2.api.rule.response.RuleRestResponse;
55
56 import static java.util.Optional.ofNullable;
57 import static org.sonar.db.rule.RuleDto.Format.MARKDOWN;
58
59 public class RuleRestResponseGenerator {
60
61   private final Languages languages;
62   private final MacroInterpreter macroInterpreter;
63   private final RuleDescriptionFormatter ruleDescriptionFormatter;
64
65   public RuleRestResponseGenerator(Languages languages, MacroInterpreter macroInterpreter, RuleDescriptionFormatter ruleDescriptionFormatter) {
66
67     this.languages = languages;
68     this.macroInterpreter = macroInterpreter;
69     this.ruleDescriptionFormatter = ruleDescriptionFormatter;
70   }
71
72   public RuleRestResponse toRuleRestResponse(RuleInformation ruleInformation) {
73
74     RuleRestResponse.Builder builder = RuleRestResponse.Builder.builder();
75     RuleDto ruleDto = ruleInformation.ruleDto();
76     builder
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)
88         .orElse(null))
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()));
103
104     setDescriptionFields(builder, ruleDto);
105     setRemediationFunctionFields(builder, ruleDto);
106
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))));
114     }
115     return builder.build();
116   }
117
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);
124       });
125   }
126
127   private static List<Parameter> toRuleParameterResponse(List<RuleParamDto> ruleParamDtos) {
128     return ruleParamDtos.stream()
129       .map(p -> new Parameter(p.getName(), Markdown.convertToHtml(p.getDescription()), p.getDefaultValue(), p.getType()))
130       .toList();
131   }
132
133   @CheckForNull
134   private String getLanguageName(@Nullable String languageKey) {
135     if (languageKey == null) {
136       return null;
137     }
138     Language language = languages.get(languageKey);
139     return language == null ? languageKey : language.getName();
140   }
141
142   private void setDescriptionFields(RuleRestResponse.Builder builder, RuleDto ruleDto) {
143     builder.setDescriptionSections(ruleDto.getRuleDescriptionSectionDtos().stream()
144       .map(sectionDto -> toDescriptionSectionResponse(ruleDto, sectionDto))
145       .toList());
146
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));
154     }
155   }
156
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()))
163         .orElse(null));
164   }
165
166   private RuleDescriptionSectionRestResponse toDescriptionSectionResponse(String description) {
167     return new RuleDescriptionSectionRestResponse(RuleDescriptionSectionDto.DEFAULT_KEY, macroInterpreter.interpret(description), null);
168   }
169
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())))
173       .toList();
174   }
175
176   @CheckForNull
177   private static DebtRemediationFunction defaultDebtRemediationFunction(final RuleDto ruleDto) {
178     final String function = ruleDto.getDefRemediationFunction();
179     if (function == null || function.isEmpty()) {
180       return null;
181     } else {
182       return new DefaultDebtRemediationFunction(
183         DebtRemediationFunction.Type.valueOf(function.toUpperCase(Locale.ENGLISH)),
184         ruleDto.getDefRemediationGapMultiplier(),
185         ruleDto.getDefRemediationBaseEffort());
186     }
187   }
188
189   @CheckForNull
190   private static DebtRemediationFunction debtRemediationFunction(RuleDto ruleDto) {
191     final String function = ruleDto.getRemediationFunction();
192     if (function == null || function.isEmpty()) {
193       return defaultDebtRemediationFunction(ruleDto);
194     } else {
195       return new DefaultDebtRemediationFunction(
196         DebtRemediationFunction.Type.valueOf(function.toUpperCase(Locale.ENGLISH)),
197         ruleDto.getRemediationGapMultiplier(),
198         ruleDto.getRemediationBaseEffort());
199     }
200   }
201
202   private static String toDateTime(@Nullable Long dateTimeMs) {
203     return Optional.ofNullable(dateTimeMs).map(DateUtils::formatDateTime).orElse(null);
204   }
205
206 }