aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-webserver-core
diff options
context:
space:
mode:
authorAurelien Poscia <aurelien.poscia@sonarsource.com>2022-06-24 10:51:35 +0200
committersonartech <sonartech@sonarsource.com>2022-06-30 20:03:09 +0000
commit0eddb29f5b003ee4600bf230c5533215cf7515ff (patch)
tree37fe3bf9f6964dc326145ceb2a6d0fcde373ee18 /server/sonar-webserver-core
parent923ec14b8ef55af36ff9bc45988b5c8fd51043bd (diff)
downloadsonarqube-0eddb29f5b003ee4600bf230c5533215cf7515ff.tar.gz
sonarqube-0eddb29f5b003ee4600bf230c5533215cf7515ff.zip
SONAR-16518 Read contextual descriptions from sonar-plugin-api and pass them to RuleDescriptionSectionDto
Diffstat (limited to 'server/sonar-webserver-core')
-rw-r--r--server/sonar-webserver-core/src/main/java/org/sonar/server/rule/AdvancedRuleDescriptionSectionsGenerator.java28
-rw-r--r--server/sonar-webserver-core/src/test/java/org/sonar/server/rule/AdvancedRuleDescriptionSectionsGeneratorTest.java54
-rw-r--r--server/sonar-webserver-core/src/test/java/org/sonar/server/rule/RegisterRulesTest.java108
3 files changed, 181 insertions, 9 deletions
diff --git a/server/sonar-webserver-core/src/main/java/org/sonar/server/rule/AdvancedRuleDescriptionSectionsGenerator.java b/server/sonar-webserver-core/src/main/java/org/sonar/server/rule/AdvancedRuleDescriptionSectionsGenerator.java
index 1fb1afaf8e2..08652f93ee4 100644
--- a/server/sonar-webserver-core/src/main/java/org/sonar/server/rule/AdvancedRuleDescriptionSectionsGenerator.java
+++ b/server/sonar-webserver-core/src/main/java/org/sonar/server/rule/AdvancedRuleDescriptionSectionsGenerator.java
@@ -19,9 +19,14 @@
*/
package org.sonar.server.rule;
+import java.util.Optional;
import java.util.Set;
+import org.jetbrains.annotations.Nullable;
+import org.sonar.api.server.rule.Context;
+import org.sonar.api.server.rule.RuleDescriptionSection;
import org.sonar.api.server.rule.RulesDefinition;
import org.sonar.core.util.UuidFactory;
+import org.sonar.db.rule.RuleDescriptionSectionContextDto;
import org.sonar.db.rule.RuleDescriptionSectionDto;
import static java.util.stream.Collectors.toSet;
@@ -41,13 +46,24 @@ public class AdvancedRuleDescriptionSectionsGenerator implements RuleDescription
@Override
public Set<RuleDescriptionSectionDto> generateSections(RulesDefinition.Rule rule) {
return rule.ruleDescriptionSections().stream()
- .map(section -> RuleDescriptionSectionDto.builder()
- .uuid(uuidFactory.create())
- .key(section.getKey())
- .content(section.getHtmlContent())
- .build()
- )
+ .map(this::toRuleDescriptionSectionDto)
.collect(toSet());
}
+ private RuleDescriptionSectionDto toRuleDescriptionSectionDto(RuleDescriptionSection section) {
+ return RuleDescriptionSectionDto.builder()
+ .uuid(uuidFactory.create())
+ .key(section.getKey())
+ .content(section.getHtmlContent())
+ .context(toRuleDescriptionSectionContextDto(section.getContext()))
+ .build();
+ }
+
+ @Nullable
+ private static RuleDescriptionSectionContextDto toRuleDescriptionSectionContextDto(Optional<Context> context) {
+ return context
+ .map(c -> RuleDescriptionSectionContextDto.of(c.getKey(), c.getDisplayName()))
+ .orElse(null);
+ }
+
}
diff --git a/server/sonar-webserver-core/src/test/java/org/sonar/server/rule/AdvancedRuleDescriptionSectionsGeneratorTest.java b/server/sonar-webserver-core/src/test/java/org/sonar/server/rule/AdvancedRuleDescriptionSectionsGeneratorTest.java
index e7f6a0229d9..08c4a4f4df8 100644
--- a/server/sonar-webserver-core/src/test/java/org/sonar/server/rule/AdvancedRuleDescriptionSectionsGeneratorTest.java
+++ b/server/sonar-webserver-core/src/test/java/org/sonar/server/rule/AdvancedRuleDescriptionSectionsGeneratorTest.java
@@ -27,16 +27,19 @@ import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
+import org.sonar.api.server.rule.Context;
import org.sonar.api.server.rule.RuleDescriptionSection;
import org.sonar.api.server.rule.RuleDescriptionSectionBuilder;
import org.sonar.api.server.rule.RulesDefinition;
import org.sonar.core.util.UuidFactory;
+import org.sonar.db.rule.RuleDescriptionSectionContextDto;
import org.sonar.db.rule.RuleDescriptionSectionDto;
import static java.util.Collections.emptyList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;
import static org.sonar.api.server.rule.RuleDescriptionSection.RuleDescriptionSectionKeys.HOW_TO_FIX_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;
@RunWith(MockitoJUnitRunner.class)
@@ -49,10 +52,39 @@ public class AdvancedRuleDescriptionSectionsGeneratorTest {
private static final RuleDescriptionSection SECTION_1 = new RuleDescriptionSectionBuilder().sectionKey(HOW_TO_FIX_SECTION_KEY).htmlContent(HTML_CONTENT).build();
private static final RuleDescriptionSection SECTION_2 = new RuleDescriptionSectionBuilder().sectionKey(ROOT_CAUSE_SECTION_KEY).htmlContent(HTML_CONTENT + "2").build();
+ private static final Context CONTEXT_1 = new Context("CTX_1", "ctx 1 display name");
+ private static final RuleDescriptionSection SECTION_3_WITH_CTX_1 = new RuleDescriptionSectionBuilder()
+ .sectionKey(RESOURCES_SECTION_KEY)
+ .htmlContent(HTML_CONTENT)
+ .context(CONTEXT_1)
+ .build();
+
+ private static final Context CONTEXT_2 = new Context("CTX_2", "ctx 2 display name");
+ private static final RuleDescriptionSection SECTION_3_WITH_CTX_2 = new RuleDescriptionSectionBuilder()
+ .sectionKey(RESOURCES_SECTION_KEY)
+ .htmlContent(HTML_CONTENT + "2")
+ .context(CONTEXT_2)
+ .build();
+
private static final RuleDescriptionSectionDto EXPECTED_SECTION_1 = RuleDescriptionSectionDto.builder().uuid(UUID_1).key(HOW_TO_FIX_SECTION_KEY).content(HTML_CONTENT).build();
private static final RuleDescriptionSectionDto EXPECTED_SECTION_2 = RuleDescriptionSectionDto.builder().uuid(UUID_2).key(ROOT_CAUSE_SECTION_KEY)
.content(HTML_CONTENT + "2").build();
+ private static final RuleDescriptionSectionDto EXPECTED_SECTION_3_WITH_CTX_1 = RuleDescriptionSectionDto.builder()
+ .uuid(UUID_1)
+ .key(SECTION_3_WITH_CTX_1.getKey())
+ .content(SECTION_3_WITH_CTX_1.getHtmlContent())
+ .context(RuleDescriptionSectionContextDto.of(CONTEXT_1.getKey(), CONTEXT_1.getDisplayName()))
+ .build();
+
+ private static final RuleDescriptionSectionDto EXPECTED_SECTION_3_WITH_CTX_2 = RuleDescriptionSectionDto.builder()
+ .uuid(UUID_2)
+ .key(SECTION_3_WITH_CTX_2.getKey())
+ .content(SECTION_3_WITH_CTX_2.getHtmlContent())
+ .context(RuleDescriptionSectionContextDto.of(CONTEXT_2.getKey(), CONTEXT_2.getDisplayName()))
+ .build();
+
+
@Mock
private UuidFactory uuidFactory;
@@ -90,6 +122,28 @@ public class AdvancedRuleDescriptionSectionsGeneratorTest {
}
@Test
+ public void generateSections_whenTwoContextSpecificSections_createsTwoSections() {
+ when(rule.ruleDescriptionSections()).thenReturn(List.of(SECTION_3_WITH_CTX_1, SECTION_3_WITH_CTX_2));
+
+ Set<RuleDescriptionSectionDto> ruleDescriptionSectionDtos = generator.generateSections(rule);
+
+ assertThat(ruleDescriptionSectionDtos)
+ .usingRecursiveFieldByFieldElementComparator()
+ .containsExactlyInAnyOrder(EXPECTED_SECTION_3_WITH_CTX_1, EXPECTED_SECTION_3_WITH_CTX_2);
+ }
+
+ @Test
+ public void generateSections_whenContextSpecificSectionsAndNonContextSpecificSection_createsTwoSections() {
+ when(rule.ruleDescriptionSections()).thenReturn(List.of(SECTION_1, SECTION_3_WITH_CTX_2));
+
+ Set<RuleDescriptionSectionDto> ruleDescriptionSectionDtos = generator.generateSections(rule);
+
+ assertThat(ruleDescriptionSectionDtos)
+ .usingRecursiveFieldByFieldElementComparator()
+ .containsExactlyInAnyOrder(EXPECTED_SECTION_1, EXPECTED_SECTION_3_WITH_CTX_2);
+ }
+
+ @Test
public void generateSections_whenNoSections_returnsEmptySet() {
when(rule.ruleDescriptionSections()).thenReturn(emptyList());
diff --git a/server/sonar-webserver-core/src/test/java/org/sonar/server/rule/RegisterRulesTest.java b/server/sonar-webserver-core/src/test/java/org/sonar/server/rule/RegisterRulesTest.java
index 4f7c1add4f9..8dc72061466 100644
--- a/server/sonar-webserver-core/src/test/java/org/sonar/server/rule/RegisterRulesTest.java
+++ b/server/sonar-webserver-core/src/test/java/org/sonar/server/rule/RegisterRulesTest.java
@@ -25,9 +25,14 @@ import com.tngtech.java.junit.dataprovider.UseDataProvider;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;
+import java.util.stream.Collectors;
import java.util.stream.IntStream;
+import org.elasticsearch.common.util.set.Sets;
+import org.jetbrains.annotations.Nullable;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -39,6 +44,8 @@ import org.sonar.api.rule.RuleScope;
import org.sonar.api.rule.RuleStatus;
import org.sonar.api.rules.RuleType;
import org.sonar.api.server.debt.DebtRemediationFunction;
+import org.sonar.api.server.rule.Context;
+import org.sonar.api.server.rule.RuleDescriptionSection;
import org.sonar.api.server.rule.RulesDefinition;
import org.sonar.api.utils.DateUtils;
import org.sonar.api.utils.log.LogTester;
@@ -52,6 +59,7 @@ import org.sonar.db.qualityprofile.QProfileChangeDto;
import org.sonar.db.qualityprofile.QProfileChangeQuery;
import org.sonar.db.qualityprofile.QProfileDto;
import org.sonar.db.rule.DeprecatedRuleKeyDto;
+import org.sonar.db.rule.RuleDescriptionSectionContextDto;
import org.sonar.db.rule.RuleDescriptionSectionDto;
import org.sonar.db.rule.RuleDto;
import org.sonar.db.rule.RuleDto.Scope;
@@ -71,6 +79,7 @@ import org.sonar.server.rule.index.RuleIndexer;
import org.sonar.server.rule.index.RuleQuery;
import static com.google.common.collect.Sets.newHashSet;
+import static java.lang.String.format;
import static java.lang.String.valueOf;
import static java.util.Collections.emptySet;
import static java.util.Collections.singletonList;
@@ -89,7 +98,10 @@ import static org.sonar.api.rule.RuleStatus.READY;
import static org.sonar.api.rule.RuleStatus.REMOVED;
import static org.sonar.api.rule.Severity.BLOCKER;
import static org.sonar.api.rule.Severity.INFO;
-import static org.sonar.api.server.rule.RulesDefinition.Context;
+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.RESOURCES_SECTION_KEY;
+import static org.sonar.api.server.rule.RuleDescriptionSection.RuleDescriptionSectionKeys.ROOT_CAUSE_SECTION_KEY;
import static org.sonar.api.server.rule.RulesDefinition.NewRepository;
import static org.sonar.api.server.rule.RulesDefinition.NewRule;
import static org.sonar.api.server.rule.RulesDefinition.OwaspTop10;
@@ -145,7 +157,17 @@ public class RegisterRulesTest {
when(ruleDescriptionSectionsGenerator.generateSections(any())).thenAnswer(answer -> {
RulesDefinition.Rule rule = answer.getArgument(0, RulesDefinition.Rule.class);
String description = rule.htmlDescription() == null ? rule.markdownDescription() : rule.htmlDescription();
- return Set.of(builder().uuid(UuidFactoryFast.getInstance().create()).key("default").content(description).build());
+
+ Set<RuleDescriptionSectionDto> ruleDescriptionSectionDtos = rule.ruleDescriptionSections().stream() //
+ .map(s -> builder()
+ .uuid(UuidFactoryFast.getInstance().create())
+ .key(s.getKey())
+ .content(s.getHtmlContent())
+ .context(s.getContext().map(c -> RuleDescriptionSectionContextDto.of(c.getKey(), c.getDisplayName())).orElse(null))
+ .build()
+ )
+ .collect(Collectors.toSet());
+ return Sets.union(ruleDescriptionSectionDtos, Set.of(builder().uuid(UuidFactoryFast.getInstance().create()).key("default").content(description).build()));
});
when(ruleDescriptionSectionsGenerator.isGeneratorForRule(any())).thenReturn(true);
@@ -718,6 +740,86 @@ public class RegisterRulesTest {
}
@Test
+ public void update_several_rule_descriptions() {
+ system.setNow(DATE1.getTime());
+
+ RuleDescriptionSection section1context1 = createRuleDescriptionSection(HOW_TO_FIX_SECTION_KEY, "section1 ctx1 content", "CTX_1");
+ RuleDescriptionSection section1context2 = createRuleDescriptionSection(HOW_TO_FIX_SECTION_KEY,"section1 ctx2 content", "CTX_2");
+ RuleDescriptionSection section2context1 = createRuleDescriptionSection(RESOURCES_SECTION_KEY,"section2 content", "CTX_1");
+ RuleDescriptionSection section3noContext = createRuleDescriptionSection(ASSESS_THE_PROBLEM_SECTION_KEY,"section3 content", null);
+ RuleDescriptionSection section4noContext = createRuleDescriptionSection(ROOT_CAUSE_SECTION_KEY,"section4 content", null);
+ execute(context -> {
+ NewRepository repo = context.createRepository("fake", "java");
+ repo.createRule("rule")
+ .setName("Name")
+ .addDescriptionSection(section1context1)
+ .addDescriptionSection(section1context2)
+ .addDescriptionSection(section2context1)
+ .addDescriptionSection(section3noContext)
+ .addDescriptionSection(section4noContext)
+ .setHtmlDescription("Desc1");
+ repo.done();
+ });
+
+ RuleDescriptionSection section1context2updated = createRuleDescriptionSection(HOW_TO_FIX_SECTION_KEY, "section1 ctx2 updated content", "CTX_2");
+ RuleDescriptionSection section2updatedWithoutContext = createRuleDescriptionSection(RESOURCES_SECTION_KEY, section2context1.getHtmlContent(), null);
+ RuleDescriptionSection section4updatedWithContext = createRuleDescriptionSection(ROOT_CAUSE_SECTION_KEY, section4noContext.getHtmlContent(), "CTX_1");
+ system.setNow(DATE2.getTime());
+ execute(context -> {
+ NewRepository repo = context.createRepository("fake", "java");
+ repo.createRule("rule")
+ .setName("Name")
+ .addDescriptionSection(section1context1)
+ .addDescriptionSection(section1context2updated)
+ .addDescriptionSection(section2updatedWithoutContext)
+ .addDescriptionSection(section3noContext)
+ .addDescriptionSection(section4updatedWithContext)
+ .setHtmlDescription("Desc2");
+ repo.done();
+
+ });
+
+ RuleDto rule1 = dbClient.ruleDao().selectOrFailByKey(db.getSession(), RuleKey.of("fake", "rule"));
+ assertThat(rule1.getName()).isEqualTo("Name");
+ assertThat(rule1.getDefaultRuleDescriptionSection().getContent()).isEqualTo("Desc2");
+
+ Set<RuleDescriptionSection> expectedSections = Set.of(section1context1, section1context2updated,
+ section2updatedWithoutContext, section3noContext, section4updatedWithContext);
+ assertThat(rule1.getRuleDescriptionSectionDtos()).hasSize(expectedSections.size() + 1);
+ expectedSections.forEach(apiSection -> assertSectionExists(apiSection, rule1.getRuleDescriptionSectionDtos()));
+ }
+
+ private static RuleDescriptionSection createRuleDescriptionSection(String sectionKey, String description, @Nullable String contextKey) {
+ Context context = Optional.ofNullable(contextKey).map(key -> new Context(contextKey, contextKey + randomAlphanumeric(10))).orElse(null);
+ return RuleDescriptionSection.builder().sectionKey(sectionKey)
+ .htmlContent(description)
+ .context(context)
+ .build();
+ }
+
+ private static void assertSectionExists(RuleDescriptionSection apiSection, Set<RuleDescriptionSectionDto> sectionDtos) {
+ sectionDtos.stream()
+ .filter(sectionDto -> sectionDto.getKey().equals(apiSection.getKey()) && sectionDto.getContent().equals(apiSection.getHtmlContent()))
+ .filter(sectionDto -> isSameContext(apiSection.getContext(), sectionDto.getContext()))
+ .findAny()
+ .orElseThrow(() -> new AssertionError(format("Impossible to find a section dto matching the API section %s", apiSection.getKey())));
+ }
+
+ private static boolean isSameContext(Optional<Context> apiContext, @Nullable RuleDescriptionSectionContextDto contextDto) {
+ if (apiContext.isEmpty() && contextDto == null) {
+ return true;
+ }
+ return apiContext.filter(context -> isSameContext(context, contextDto)).isPresent();
+ }
+
+ private static boolean isSameContext(Context apiContext, @Nullable RuleDescriptionSectionContextDto contextDto) {
+ if (contextDto == null) {
+ return false;
+ }
+ return Objects.equals(apiContext.getKey(), contextDto.getKey()) && Objects.equals(apiContext.getDisplayName(), contextDto.getDisplayName());
+ }
+
+ @Test
public void rule_previously_created_as_adhoc_becomes_none_adhoc() {
RuleDto rule = db.rules().insert(r -> r.setRepositoryKey("external_fake").setIsExternal(true).setIsAdHoc(true));
system.setNow(DATE2.getTime());
@@ -1055,7 +1157,7 @@ public class RegisterRulesTest {
}
@SafeVarargs
- private void createRule(Context context, String language, String repositoryKey, String ruleKey, Consumer<NewRule>... consumers) {
+ private void createRule(RulesDefinition.Context context, String language, String repositoryKey, String ruleKey, Consumer<NewRule>... consumers) {
NewRepository repo = context.createRepository(repositoryKey, language);
NewRule newRule = repo.createRule(ruleKey)
.setName(ruleKey)