diff options
author | Zipeng WU <zipeng.wu@sonarsource.com> | 2022-05-05 14:52:04 +0200 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2022-05-09 20:02:59 +0000 |
commit | 41af7cd13eed44de4c3941669bdb5968347f454a (patch) | |
tree | 050518f3725ee991fb0998f85129562693cffd7e /server/sonar-server-common | |
parent | 3637ca1ca97c03bcc110007f9ec52c4cdef60632 (diff) | |
download | sonarqube-41af7cd13eed44de4c3941669bdb5968347f454a.tar.gz sonarqube-41af7cd13eed44de4c3941669bdb5968347f454a.zip |
SONAR-16364 Update Rule API to support multiple description sections
Diffstat (limited to 'server/sonar-server-common')
2 files changed, 96 insertions, 23 deletions
diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/rule/RuleDescriptionFormatter.java b/server/sonar-server-common/src/main/java/org/sonar/server/rule/RuleDescriptionFormatter.java index 2de9e1fca83..13db9a72a63 100644 --- a/server/sonar-server-common/src/main/java/org/sonar/server/rule/RuleDescriptionFormatter.java +++ b/server/sonar-server-common/src/main/java/org/sonar/server/rule/RuleDescriptionFormatter.java @@ -20,49 +20,93 @@ package org.sonar.server.rule; import java.util.Collection; +import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Optional; +import javax.annotation.CheckForNull; +import org.sonar.api.rules.RuleType; import org.sonar.db.rule.RuleDescriptionSectionDto; import org.sonar.db.rule.RuleDto; import org.sonar.markdown.Markdown; import static com.google.common.collect.MoreCollectors.toOptional; -import static java.lang.String.format; +import static java.util.stream.Collectors.toMap; +import static org.sonar.api.server.rule.RuleDescriptionSection.RuleDescriptionSectionKeys.ASSESS_THE_PROBLEM_SECTION_KEY; +import static org.sonar.api.server.rule.RuleDescriptionSection.RuleDescriptionSectionKeys.HOW_TO_FIX_SECTION_KEY; +import static org.sonar.api.server.rule.RuleDescriptionSection.RuleDescriptionSectionKeys.INTRODUCTION_SECTION_KEY; +import static org.sonar.api.server.rule.RuleDescriptionSection.RuleDescriptionSectionKeys.RESOURCES_SECTION_KEY; +import static org.sonar.api.server.rule.RuleDescriptionSection.RuleDescriptionSectionKeys.ROOT_CAUSE_SECTION_KEY; +import static org.sonar.db.rule.RuleDescriptionSectionDto.DEFAULT_KEY; +import static org.sonar.db.rule.RuleDto.Format.MARKDOWN; public class RuleDescriptionFormatter { + public static final List<String> SECTION_KEYS = List.of( + INTRODUCTION_SECTION_KEY, + ROOT_CAUSE_SECTION_KEY, + ASSESS_THE_PROBLEM_SECTION_KEY, + HOW_TO_FIX_SECTION_KEY, + RESOURCES_SECTION_KEY); + + public static final Map<String, String> HOTSPOT_SECTION_TITLES = Map.of( + ROOT_CAUSE_SECTION_KEY, "What's the risk ?", + ASSESS_THE_PROBLEM_SECTION_KEY, "Assess the risk", + HOW_TO_FIX_SECTION_KEY, "How can you fix it ?" + ); + + public static final Map<String, String> RULE_SECTION_TITLES = Map.of( + ROOT_CAUSE_SECTION_KEY, "Why is this an issue ?", + HOW_TO_FIX_SECTION_KEY, "How to fix it ?", + RESOURCES_SECTION_KEY, "Resources" + ); + private RuleDescriptionFormatter() { /* static helpers */ } + @CheckForNull public static String getDescriptionAsHtml(RuleDto ruleDto) { if (ruleDto.getDescriptionFormat() == null) { return null; } Collection<RuleDescriptionSectionDto> ruleDescriptionSectionDtos = ruleDto.getRuleDescriptionSectionDtos(); - return retrieveDescription(ruleDescriptionSectionDtos, ruleDto.getRuleKey(), Objects.requireNonNull(ruleDto.getDescriptionFormat())); + return retrieveDescription(ruleDescriptionSectionDtos, RuleType.valueOf(ruleDto.getType()), Objects.requireNonNull(ruleDto.getDescriptionFormat())); } + @CheckForNull private static String retrieveDescription(Collection<RuleDescriptionSectionDto> ruleDescriptionSectionDtos, - String ruleKey, RuleDto.Format descriptionFormat) { - Optional<RuleDescriptionSectionDto> ruleDescriptionSectionDto = findDefaultDescription(ruleDescriptionSectionDtos); - return ruleDescriptionSectionDto - .map(ruleDescriptionSection -> toHtml(ruleKey, descriptionFormat, ruleDescriptionSection)) - .orElse(null); + RuleType ruleType, RuleDto.Format descriptionFormat) { + if (ruleDescriptionSectionDtos.isEmpty()) { + return null; + } + Map<String, String> sectionKeyToHtml = ruleDescriptionSectionDtos.stream() + .collect(toMap(RuleDescriptionSectionDto::getKey, section -> toHtml(descriptionFormat, section))); + if (sectionKeyToHtml.containsKey(DEFAULT_KEY)) { + return sectionKeyToHtml.get(DEFAULT_KEY); + } else { + return concatHtmlSections(sectionKeyToHtml, ruleType); + } } - private static Optional<RuleDescriptionSectionDto> findDefaultDescription(Collection<RuleDescriptionSectionDto> ruleDescriptionSectionDtos) { - return ruleDescriptionSectionDtos.stream() - .filter(RuleDescriptionSectionDto::isDefault) - .collect(toOptional()); + private static String concatHtmlSections(Map<String, String> sectionKeyToHtml, RuleType ruleType) { + Map<String, String> titleMapping = ruleType.equals(RuleType.SECURITY_HOTSPOT) ? HOTSPOT_SECTION_TITLES : RULE_SECTION_TITLES; + var builder = new StringBuilder(); + for (String sectionKey : SECTION_KEYS) { + if (sectionKeyToHtml.containsKey(sectionKey)) { + builder.append("<h2>") + .append(titleMapping.get(sectionKey)) + .append("</h2>") + .append(sectionKeyToHtml.get(sectionKey)) + .append("<br/>"); + } + } + return builder.toString(); } - private static String toHtml(String ruleKey, RuleDto.Format descriptionFormat, RuleDescriptionSectionDto ruleDescriptionSectionDto) { - switch (descriptionFormat) { - case MARKDOWN: - return Markdown.convertToHtml(ruleDescriptionSectionDto.getContent()); - case HTML: - return ruleDescriptionSectionDto.getContent(); - default: - throw new IllegalStateException(format("Rule description section format '%s' is unknown for rule key '%s'", descriptionFormat, ruleKey)); + private static String toHtml(RuleDto.Format descriptionFormat, RuleDescriptionSectionDto ruleDescriptionSectionDto) { + if (MARKDOWN.equals(descriptionFormat)) { + return Markdown.convertToHtml(ruleDescriptionSectionDto.getContent()); + } else { + return ruleDescriptionSectionDto.getContent(); } } diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/rule/RuleDescriptionFormatterTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/rule/RuleDescriptionFormatterTest.java index 7c0879424c0..260bb5ed4f6 100644 --- a/server/sonar-server-common/src/test/java/org/sonar/server/rule/RuleDescriptionFormatterTest.java +++ b/server/sonar-server-common/src/test/java/org/sonar/server/rule/RuleDescriptionFormatterTest.java @@ -20,10 +20,14 @@ package org.sonar.server.rule; import org.junit.Test; +import org.sonar.api.rules.RuleType; import org.sonar.db.rule.RuleDescriptionSectionDto; import org.sonar.db.rule.RuleDto; import static org.assertj.core.api.Assertions.assertThat; +import static org.sonar.api.server.rule.RuleDescriptionSection.RuleDescriptionSectionKeys.ASSESS_THE_PROBLEM_SECTION_KEY; +import static org.sonar.api.server.rule.RuleDescriptionSection.RuleDescriptionSectionKeys.HOW_TO_FIX_SECTION_KEY; +import static org.sonar.api.server.rule.RuleDescriptionSection.RuleDescriptionSectionKeys.ROOT_CAUSE_SECTION_KEY; import static org.sonar.db.rule.RuleDescriptionSectionDto.createDefaultRuleDescriptionSection; public class RuleDescriptionFormatterTest { @@ -33,21 +37,43 @@ public class RuleDescriptionFormatterTest { @Test public void getMarkdownDescriptionAsHtml() { - RuleDto rule = new RuleDto().setDescriptionFormat(RuleDto.Format.MARKDOWN).addRuleDescriptionSectionDto(MARKDOWN_SECTION); + RuleDto rule = new RuleDto().setDescriptionFormat(RuleDto.Format.MARKDOWN).addRuleDescriptionSectionDto(MARKDOWN_SECTION).setType(RuleType.BUG); String html = RuleDescriptionFormatter.getDescriptionAsHtml(rule); assertThat(html).isEqualTo("<strong>md</strong> <code>description</code>"); } @Test public void getHtmlDescriptionAsIs() { - RuleDto rule = new RuleDto().setDescriptionFormat(RuleDto.Format.HTML).addRuleDescriptionSectionDto(HTML_SECTION); + RuleDto rule = new RuleDto().setDescriptionFormat(RuleDto.Format.HTML).addRuleDescriptionSectionDto(HTML_SECTION).setType(RuleType.BUG); String html = RuleDescriptionFormatter.getDescriptionAsHtml(rule); assertThat(html).isEqualTo(HTML_SECTION.getContent()); } @Test + public void concatHtmlDescriptionSections() { + var section1 = createRuleDescriptionSection(ROOT_CAUSE_SECTION_KEY, "<div>Root is Root</div>"); + var section2 = createRuleDescriptionSection(ASSESS_THE_PROBLEM_SECTION_KEY, "<div>This is not a problem</div>"); + var section3 = createRuleDescriptionSection(HOW_TO_FIX_SECTION_KEY, "<div>I don't want to fix</div>"); + RuleDto rule = new RuleDto().setDescriptionFormat(RuleDto.Format.HTML) + .setType(RuleType.SECURITY_HOTSPOT) + .addRuleDescriptionSectionDto(section1) + .addRuleDescriptionSectionDto(section2) + .addRuleDescriptionSectionDto(section3); + String html = RuleDescriptionFormatter.getDescriptionAsHtml(rule); + assertThat(html) + .contains( + "<h2>What's the risk ?</h2>" + + "<div>Root is Root</div><br/>" + + "<h2>Assess the risk</h2>" + + "<div>This is not a problem</div><br/>" + + "<h2>How can you fix it ?</h2>" + + "<div>I don't want to fix</div><br/>" + ); + } + + @Test public void handleEmptyDescription() { - RuleDto rule = new RuleDto().setDescriptionFormat(RuleDto.Format.HTML); + RuleDto rule = new RuleDto().setDescriptionFormat(RuleDto.Format.HTML).setType(RuleType.BUG); String result = RuleDescriptionFormatter.getDescriptionAsHtml(rule); assertThat(result).isNull(); } @@ -55,9 +81,12 @@ public class RuleDescriptionFormatterTest { @Test public void handleNullDescriptionFormat() { RuleDescriptionSectionDto sectionWithNullFormat = createDefaultRuleDescriptionSection("uuid", "whatever"); - RuleDto rule = new RuleDto().addRuleDescriptionSectionDto(sectionWithNullFormat); + RuleDto rule = new RuleDto().addRuleDescriptionSectionDto(sectionWithNullFormat).setType(RuleType.BUG); String result = RuleDescriptionFormatter.getDescriptionAsHtml(rule); assertThat(result).isNull(); } + private static RuleDescriptionSectionDto createRuleDescriptionSection(String key, String content) { + return RuleDescriptionSectionDto.builder().key(key).content(content).build(); + } } |