From 6e2ed638eeb353c4048575e9ba16c968455d3142 Mon Sep 17 00:00:00 2001 From: =?utf8?q?L=C3=A9o=20Geoffroy?= <99647462+leo-geoffroy-sonarsource@users.noreply.github.com> Date: Fri, 22 Apr 2022 14:13:59 +0200 Subject: [PATCH] SONAR-16302 adapt RuleDto, RuleDefinitionDto and introduce RuleSectionDescriptionDto to prepare for multiple section support --- .../issue/AdHocRuleCreatorTest.java | 8 +- .../org/sonar/db/rule/RuleDefinitionDto.java | 38 ++++++-- .../db/rule/RuleDescriptionSectionDto.java | 96 +++++++++++++++++++ .../main/java/org/sonar/db/rule/RuleDto.java | 24 ++++- .../QualityProfileExportDaoTest.java | 7 +- .../java/org/sonar/db/rule/RuleDaoTest.java | 29 ++++-- .../java/org/sonar/db/rule/RuleTesting.java | 23 +++-- .../sonar/server/rule/DefaultRuleFinder.java | 16 +--- .../server/rule/HotspotRuleDescription.java | 2 +- .../server/rule/RuleDescriptionFormatter.java | 45 ++++++--- .../rule/HotspotRuleDescriptionTest.java | 45 +++++---- .../rule/RuleDescriptionFormatterTest.java | 19 ++-- .../server/rule/index/RuleIndexTest.java | 14 ++- .../server/rule/index/RuleIndexerTest.java | 45 ++++----- .../sonar/server/rule/CachingRuleFinder.java | 15 +-- .../server/rule/CachingRuleFinderTest.java | 3 +- .../org/sonar/server/rule/RegisterRules.java | 43 ++++++--- .../sonar/server/rule/RegisterRulesTest.java | 28 +++--- ...lityProfileChangeEventServiceImplTest.java | 6 +- .../org/sonar/server/rule/RuleCreator.java | 11 ++- .../org/sonar/server/rule/RuleUpdater.java | 5 +- .../org/sonar/server/rule/ws/RuleMapper.java | 13 ++- .../server/hotspot/ws/ShowActionTest.java | 25 +++-- .../server/issue/ws/SearchActionTest.java | 5 +- .../QProfileBackuperImplTest.java | 5 +- .../sonar/server/rule/RuleCreatorTest.java | 9 +- .../sonar/server/rule/RuleUpdaterTest.java | 7 +- .../server/rule/ws/CreateActionTest.java | 3 +- .../server/rule/ws/SearchActionTest.java | 10 +- .../sonar/server/rule/ws/ShowActionTest.java | 6 +- .../server/rule/ws/UpdateActionTest.java | 5 +- 31 files changed, 412 insertions(+), 198 deletions(-) create mode 100644 server/sonar-db-dao/src/main/java/org/sonar/db/rule/RuleDescriptionSectionDto.java diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/AdHocRuleCreatorTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/AdHocRuleCreatorTest.java index bc0391f9f8e..d3e2524d08d 100644 --- a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/AdHocRuleCreatorTest.java +++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/AdHocRuleCreatorTest.java @@ -61,7 +61,7 @@ public class AdHocRuleCreatorTest { assertThat(rule.getUuid()).isNotBlank(); assertThat(rule.getKey()).isEqualTo(RuleKey.of("external_eslint", "no-cond-assign")); assertThat(rule.getName()).isEqualTo("eslint:no-cond-assign"); - assertThat(rule.getDescription()).isNull(); + assertThat(rule.getRuleDescriptionSectionDtos()).isEmpty(); assertThat(rule.getSeverity()).isNull(); assertThat(rule.getType()).isZero(); assertThat(rule.getMetadata().getAdHocName()).isNull(); @@ -89,7 +89,7 @@ public class AdHocRuleCreatorTest { assertThat(rule.getUuid()).isNotBlank(); assertThat(rule.getKey()).isEqualTo(RuleKey.of("external_eslint", "no-cond-assign")); assertThat(rule.getName()).isEqualTo("eslint:no-cond-assign"); - assertThat(rule.getDescription()).isNull(); + assertThat(rule.getRuleDescriptionSectionDtos()).isEmpty(); assertThat(rule.getSeverity()).isNull(); assertThat(rule.getType()).isZero(); assertThat(rule.getMetadata().getAdHocName()).isEqualTo("No condition assigned"); @@ -144,7 +144,7 @@ public class AdHocRuleCreatorTest { assertThat(ruleUpdated.getUuid()).isNotBlank(); assertThat(ruleUpdated.getKey()).isEqualTo(RuleKey.of("external_eslint", "no-cond-assign")); assertThat(ruleUpdated.getName()).isEqualTo("eslint:no-cond-assign"); - assertThat(ruleUpdated.getDescription()).isNull(); + assertThat(ruleUpdated.getRuleDescriptionSectionDtos()).isEmpty(); assertThat(ruleUpdated.getSeverity()).isNull(); assertThat(ruleUpdated.getType()).isZero(); assertThat(ruleUpdated.getMetadata().getAdHocName()).isEqualTo("No condition assigned updated"); @@ -175,7 +175,7 @@ public class AdHocRuleCreatorTest { assertThat(ruleUpdated.isAdHoc()).isTrue(); assertThat(ruleUpdated.getKey()).isEqualTo(rule.getKey()); assertThat(ruleUpdated.getName()).isEqualTo(rule.getName()); - assertThat(ruleUpdated.getDescription()).isEqualTo(rule.getDescription()); + assertThat(ruleUpdated.getRuleDescriptionSectionDtos()).usingRecursiveFieldByFieldElementComparator().isEqualTo(rule.getRuleDescriptionSectionDtos()); assertThat(ruleUpdated.getSeverity()).isEqualTo(rule.getSeverity()); assertThat(ruleUpdated.getType()).isEqualTo(rule.getType()); assertThat(ruleUpdated.getDefinition().getCreatedAt()).isEqualTo(rule.getCreatedAt()); diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/rule/RuleDefinitionDto.java b/server/sonar-db-dao/src/main/java/org/sonar/db/rule/RuleDefinitionDto.java index fcdfe997c2e..1cd974855ff 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/rule/RuleDefinitionDto.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/rule/RuleDefinitionDto.java @@ -21,6 +21,9 @@ package org.sonar.db.rule; import com.google.common.base.Splitter; import com.google.common.collect.ImmutableSet; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; import java.util.Objects; import java.util.Set; import javax.annotation.CheckForNull; @@ -33,6 +36,7 @@ import org.sonar.api.rules.RuleType; import org.sonar.db.rule.RuleDto.Scope; import static com.google.common.base.Preconditions.checkArgument; +import static java.util.Collections.unmodifiableCollection; public class RuleDefinitionDto { @@ -42,10 +46,7 @@ public class RuleDefinitionDto { private String repositoryKey; private String ruleKey; - /** - * Description can be null on external rule, otherwise it should never be null - */ - private String description; + private Map ruleDescriptionSectionDtos = new HashMap<>(); /** * Description format can be null on external rule, otherwise it should never be null @@ -166,13 +167,32 @@ public class RuleDefinitionDto { return this; } + public Collection getRuleDescriptionSectionDtos() { + return unmodifiableCollection(ruleDescriptionSectionDtos.values()); + } + + @CheckForNull + public RuleDescriptionSectionDto getRuleDescriptionSectionDto(String ruleDescriptionSectionKey) { + return ruleDescriptionSectionDtos.get(ruleDescriptionSectionKey); + } + @CheckForNull - public String getDescription() { - return description; + public RuleDescriptionSectionDto getDefaultRuleDescriptionSectionDto() { + return ruleDescriptionSectionDtos.get(RuleDescriptionSectionDto.DEFAULT_KEY); + } + + public RuleDefinitionDto addRuleDescriptionSectionDto(RuleDescriptionSectionDto ruleDescriptionSectionDto) { + checkArgument(!isSectionKeyUsed(ruleDescriptionSectionDto.getKey()), "A section with key %s already exists", ruleDescriptionSectionDto.getKey()); + this.ruleDescriptionSectionDtos.put(ruleDescriptionSectionDto.getKey(), ruleDescriptionSectionDto); + return this; + } + + private boolean isSectionKeyUsed(String sectionKey) { + return ruleDescriptionSectionDtos.containsKey(sectionKey); } - public RuleDefinitionDto setDescription(@Nullable String description) { - this.description = description; + public RuleDefinitionDto addOrReplaceRuleDescriptionSectionDto(RuleDescriptionSectionDto ruleDescriptionSectionDto) { + this.ruleDescriptionSectionDtos.put(ruleDescriptionSectionDto.getKey(), ruleDescriptionSectionDto); return this; } @@ -434,7 +454,7 @@ public class RuleDefinitionDto { "uuid=" + uuid + ", repositoryKey='" + repositoryKey + '\'' + ", ruleKey='" + ruleKey + '\'' + - ", description='" + description + '\'' + + ", ruleDescriptionSections='" + ruleDescriptionSectionDtos + '\'' + ", descriptionFormat=" + descriptionFormat + ", status=" + status + ", name='" + name + '\'' + diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/rule/RuleDescriptionSectionDto.java b/server/sonar-db-dao/src/main/java/org/sonar/db/rule/RuleDescriptionSectionDto.java new file mode 100644 index 00000000000..bff3a3d30d1 --- /dev/null +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/rule/RuleDescriptionSectionDto.java @@ -0,0 +1,96 @@ +/* + * SonarQube + * Copyright (C) 2009-2022 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.db.rule; + +import java.util.StringJoiner; + +import static org.sonar.api.utils.Preconditions.checkArgument; + +public class RuleDescriptionSectionDto { + static final String DEFAULT_KEY = "default"; + private final String key; + private final String description; + + private RuleDescriptionSectionDto(String key, String description) { + this.key = key; + this.description = description; + } + + public String getKey() { + return key; + } + + public String getDescription() { + return description; + } + + public static RuleDescriptionSectionDto createDefaultRuleDescriptionSection(String description) { + return RuleDescriptionSectionDto.builder() + .setDefault() + .description(description) + .build(); + } + + public static RuleDescriptionSectionDtoBuilder builder() { + return new RuleDescriptionSectionDtoBuilder(); + } + + public boolean isDefault() { + return DEFAULT_KEY.equals(key); + } + + @Override + public String toString() { + return new StringJoiner(", ", RuleDescriptionSectionDto.class.getSimpleName() + "[", "]") + .add("key='" + key + "'") + .add("description='" + description + "'") + .toString(); + } + + public static final class RuleDescriptionSectionDtoBuilder { + private String key = null; + private String description; + + private RuleDescriptionSectionDtoBuilder() { + } + + public RuleDescriptionSectionDtoBuilder setDefault() { + checkArgument(this.key == null, "Only one of setDefault and key methods can be called"); + this.key = DEFAULT_KEY; + return this; + } + + public RuleDescriptionSectionDtoBuilder key(String key) { + checkArgument(this.key == null, "Only one of setDefault and key methods can be called"); + this.key = key; + return this; + } + + + public RuleDescriptionSectionDtoBuilder description(String description) { + this.description = description; + return this; + } + + public RuleDescriptionSectionDto build() { + return new RuleDescriptionSectionDto(key, description); + } + } +} diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/rule/RuleDto.java b/server/sonar-db-dao/src/main/java/org/sonar/db/rule/RuleDto.java index 28fe1ca75ee..3e518801fa7 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/rule/RuleDto.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/rule/RuleDto.java @@ -19,6 +19,7 @@ */ package org.sonar.db.rule; +import java.util.Collection; import java.util.Set; import javax.annotation.CheckForNull; import javax.annotation.Nullable; @@ -100,12 +101,27 @@ public class RuleDto { return this; } - public String getDescription() { - return definition.getDescription(); + @CheckForNull + public RuleDescriptionSectionDto getRuleDescriptionSection(String ruleDescriptionSectionKey) { + return definition.getRuleDescriptionSectionDto(ruleDescriptionSectionKey); + } + + @CheckForNull + public RuleDescriptionSectionDto getDefaultRuleDescriptionSection() { + return definition.getDefaultRuleDescriptionSectionDto(); + } + + public Collection getRuleDescriptionSectionDtos() { + return definition.getRuleDescriptionSectionDtos(); + } + + public RuleDto addRuleDescriptionSectionDto(RuleDescriptionSectionDto ruleDescriptionSectionDto) { + definition.addRuleDescriptionSectionDto(ruleDescriptionSectionDto); + return this; } - public RuleDto setDescription(String description) { - definition.setDescription(description); + public RuleDto addOrReplaceRuleDescriptionSectionDto(RuleDescriptionSectionDto ruleDescriptionSectionDto) { + definition.addOrReplaceRuleDescriptionSectionDto(ruleDescriptionSectionDto); return this; } diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/qualityprofile/QualityProfileExportDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/qualityprofile/QualityProfileExportDaoTest.java index 629418aaa75..fb9efff61ed 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/qualityprofile/QualityProfileExportDaoTest.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/qualityprofile/QualityProfileExportDaoTest.java @@ -36,6 +36,7 @@ import org.sonar.api.rules.RuleType; import org.sonar.db.DbSession; import org.sonar.db.DbTester; import org.sonar.db.rule.RuleDefinitionDto; +import org.sonar.db.rule.RuleDescriptionSectionDto; import org.sonar.db.rule.RuleMetadataDto; import org.sonar.db.rule.RuleParamDto; @@ -94,7 +95,8 @@ public class QualityProfileExportDaoTest { assertThat(exportCustomRuleDto).isNotNull(); assertThat(exportCustomRuleDto.isCustomRule()).isTrue(); assertThat(exportCustomRuleDto.getParams()).isEmpty(); - assertThat(exportCustomRuleDto.getDescription()).isEqualTo(customRule.getDescription()); + //FIXME SONAR-16314 + assertThat(exportCustomRuleDto.getDescription()).isEqualTo(customRule.getRuleDescriptionSectionDtos().stream().map(RuleDescriptionSectionDto::getDescription).collect(Collectors.joining())); assertThat(exportCustomRuleDto.getExtendedDescription()).isEqualTo(customRuleMetadata.getNoteData()); assertThat(exportCustomRuleDto.getName()).isEqualTo(customRule.getName()); assertThat(exportCustomRuleDto.getRuleKey()).isEqualTo(customRule.getKey()); @@ -110,7 +112,8 @@ public class QualityProfileExportDaoTest { assertThat(exportRuleDto).isNotNull(); assertThat(exportRuleDto.isCustomRule()).isFalse(); assertThat(exportRuleDto.getParams()).isEmpty(); - assertThat(exportRuleDto.getDescription()).isEqualTo(rule.getDescription()); + //FIXME SONAR-16314 + assertThat(exportRuleDto.getDescription()).isEqualTo(rule.getRuleDescriptionSectionDtos().stream().map(RuleDescriptionSectionDto::getDescription).collect(Collectors.joining())); assertThat(exportRuleDto.getExtendedDescription()).isEqualTo(ruleMetadata.getNoteData()); assertThat(exportRuleDto.getName()).isEqualTo(rule.getName()); assertThat(exportRuleDto.getRuleKey()).isEqualTo(rule.getKey()); diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/rule/RuleDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/rule/RuleDaoTest.java index 41893e0c3e8..438b517a244 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/rule/RuleDaoTest.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/rule/RuleDaoTest.java @@ -28,6 +28,7 @@ import java.util.List; import java.util.Optional; import java.util.Set; import java.util.function.Consumer; +import java.util.stream.Collectors; import org.apache.ibatis.exceptions.PersistenceException; import org.apache.ibatis.session.ResultHandler; import org.junit.Rule; @@ -48,15 +49,18 @@ import static com.google.common.collect.Sets.newHashSet; import static java.util.Arrays.asList; import static java.util.Collections.emptyList; import static java.util.Collections.singletonList; +import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic; import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.assertj.core.api.Assertions.tuple; import static org.sonar.api.rule.RuleStatus.REMOVED; +import static org.sonar.db.rule.RuleDescriptionSectionDto.createDefaultRuleDescriptionSection; import static org.sonar.db.rule.RuleTesting.newRuleMetadata; public class RuleDaoTest { private static final String UNKNOWN_RULE_UUID = "unknown-uuid"; + private static final RuleDescriptionSectionDto RULE_DESCRIPTION_SECTION_1 = createDefaultRuleDescriptionSection("new description"); @Rule public DbTester db = DbTester.create(System2.INSTANCE); @@ -244,8 +248,6 @@ public class RuleDaoTest { assertThat(actual.getRepositoryKey()).isEqualTo(expected.getRepositoryKey()); assertThat(actual.getRuleKey()).isEqualTo(expected.getRuleKey()); assertThat(actual.getKey()).isEqualTo(expected.getKey()); - assertThat(actual.getDescription()).isEqualTo(expected.getDescription()); - assertThat(actual.getDescriptionFormat()).isEqualTo(expected.getDescriptionFormat()); assertThat(actual.getStatus()).isEqualTo(expected.getStatus()); assertThat(actual.getName()).isEqualTo(expected.getName()); assertThat(actual.getConfigKey()).isEqualTo(expected.getConfigKey()); @@ -262,6 +264,9 @@ public class RuleDaoTest { assertThat(actual.getSystemTags()).isEqualTo(expected.getSystemTags()); assertThat(actual.getSecurityStandards()).isEqualTo(expected.getSecurityStandards()); assertThat(actual.getType()).isEqualTo(expected.getType()); + assertThat(actual.getDescriptionFormat()).isEqualTo(expected.getDescriptionFormat()); + assertThat(actual.getRuleDescriptionSectionDtos()).usingRecursiveFieldByFieldElementComparator() + .isEqualTo(expected.getRuleDescriptionSectionDtos()); } private static void verifyMetadata(RuleMetadataDto metadata, RuleMetadataDto expected) { @@ -427,8 +432,8 @@ public class RuleDaoTest { .setRuleKey("NewRuleKey") .setRepositoryKey("plugin") .setName("new name") - .setDescription("new description") .setDescriptionFormat(RuleDto.Format.MARKDOWN) + .addRuleDescriptionSectionDto(RULE_DESCRIPTION_SECTION_1) .setStatus(RuleStatus.DEPRECATED) .setConfigKey("NewConfigKey") .setSeverity(Severity.INFO) @@ -453,8 +458,6 @@ public class RuleDaoTest { RuleDefinitionDto ruleDto = underTest.selectOrFailDefinitionByKey(db.getSession(), RuleKey.of("plugin", "NewRuleKey")); assertThat(ruleDto.getUuid()).isNotNull(); assertThat(ruleDto.getName()).isEqualTo("new name"); - assertThat(ruleDto.getDescription()).isEqualTo("new description"); - assertThat(ruleDto.getDescriptionFormat()).isEqualTo(RuleDto.Format.MARKDOWN); assertThat(ruleDto.getStatus()).isEqualTo(RuleStatus.DEPRECATED); assertThat(ruleDto.getRuleKey()).isEqualTo("NewRuleKey"); assertThat(ruleDto.getRepositoryKey()).isEqualTo("plugin"); @@ -475,6 +478,9 @@ public class RuleDaoTest { assertThat(ruleDto.getType()).isEqualTo(RuleType.BUG.getDbConstant()); assertThat(ruleDto.getCreatedAt()).isEqualTo(1_500_000_000_000L); assertThat(ruleDto.getUpdatedAt()).isEqualTo(2_000_000_000_000L); + assertThat(ruleDto.getDescriptionFormat()).isEqualTo(RuleDto.Format.MARKDOWN); + assertThat(ruleDto.getRuleDescriptionSectionDtos()).usingRecursiveFieldByFieldElementComparator() + .containsOnly(RULE_DESCRIPTION_SECTION_1); } @Test @@ -485,8 +491,8 @@ public class RuleDaoTest { .setRuleKey("NewRuleKey") .setRepositoryKey("plugin") .setName("new name") - .setDescription("new description") .setDescriptionFormat(RuleDto.Format.MARKDOWN) + .addRuleDescriptionSectionDto(createDefaultRuleDescriptionSection(randomAlphabetic(5))) .setStatus(RuleStatus.DEPRECATED) .setConfigKey("NewConfigKey") .setSeverity(Severity.INFO) @@ -510,8 +516,6 @@ public class RuleDaoTest { RuleDefinitionDto ruleDto = underTest.selectOrFailDefinitionByKey(db.getSession(), RuleKey.of("plugin", "NewRuleKey")); assertThat(ruleDto.getName()).isEqualTo("new name"); - assertThat(ruleDto.getDescription()).isEqualTo("new description"); - assertThat(ruleDto.getDescriptionFormat()).isEqualTo(RuleDto.Format.MARKDOWN); assertThat(ruleDto.getStatus()).isEqualTo(RuleStatus.DEPRECATED); assertThat(ruleDto.getRuleKey()).isEqualTo("NewRuleKey"); assertThat(ruleDto.getRepositoryKey()).isEqualTo("plugin"); @@ -532,6 +536,9 @@ public class RuleDaoTest { assertThat(ruleDto.getType()).isEqualTo(RuleType.BUG.getDbConstant()); assertThat(ruleDto.getCreatedAt()).isEqualTo(rule.getCreatedAt()); assertThat(ruleDto.getUpdatedAt()).isEqualTo(2_000_000_000_000L); + assertThat(ruleDto.getDescriptionFormat()).isEqualTo(RuleDto.Format.MARKDOWN); + assertThat(ruleDto.getRuleDescriptionSectionDtos()).usingRecursiveFieldByFieldElementComparator() + .containsOnly(RULE_DESCRIPTION_SECTION_1); } @Test @@ -798,7 +805,8 @@ public class RuleDaoTest { assertThat(firstRule.getRepository()).isEqualTo(r1.getRepositoryKey()); assertThat(firstRule.getPluginRuleKey()).isEqualTo(r1.getRuleKey()); assertThat(firstRule.getName()).isEqualTo(r1.getName()); - assertThat(firstRule.getDescription()).isEqualTo(r1.getDescription()); + //FIXME SONAR-16309 + assertThat(firstRule.getDescription()).isEqualTo(r1.getRuleDescriptionSectionDtos().stream().map(RuleDescriptionSectionDto::getDescription).collect(Collectors.joining())); assertThat(firstRule.getDescriptionFormat()).isEqualTo(r1.getDescriptionFormat()); assertThat(firstRule.getSeverity()).isEqualTo(r1.getSeverity()); assertThat(firstRule.getStatus()).isEqualTo(r1.getStatus()); @@ -877,7 +885,8 @@ public class RuleDaoTest { assertThat(firstRule.getRepository()).isEqualTo(r1.getRepositoryKey()); assertThat(firstRule.getPluginRuleKey()).isEqualTo(r1.getRuleKey()); assertThat(firstRule.getName()).isEqualTo(r1.getName()); - assertThat(firstRule.getDescription()).isEqualTo(r1.getDescription()); + //FIXME SONAR-16309 + assertThat(firstRule.getDescription()).isEqualTo(r1.getRuleDescriptionSectionDtos().stream().map(RuleDescriptionSectionDto::getDescription).collect(Collectors.joining())); assertThat(firstRule.getDescriptionFormat()).isEqualTo(r1.getDescriptionFormat()); assertThat(firstRule.getSeverity()).isEqualTo(r1.getSeverity()); assertThat(firstRule.getSeverityAsString()).isEqualTo(SeverityUtil.getSeverityFromOrdinal(r1.getSeverity())); diff --git a/server/sonar-db-dao/src/testFixtures/java/org/sonar/db/rule/RuleTesting.java b/server/sonar-db-dao/src/testFixtures/java/org/sonar/db/rule/RuleTesting.java index b86f5ddb33d..b544afa6379 100644 --- a/server/sonar-db-dao/src/testFixtures/java/org/sonar/db/rule/RuleTesting.java +++ b/server/sonar-db-dao/src/testFixtures/java/org/sonar/db/rule/RuleTesting.java @@ -32,7 +32,6 @@ import org.sonar.api.server.rule.RuleParamType; import org.sonar.core.util.UuidFactory; import org.sonar.core.util.UuidFactoryFast; import org.sonar.core.util.Uuids; -import org.sonar.db.rule.RuleDto.Format; import org.sonar.db.rule.RuleDto.Scope; import org.sonar.db.user.UserDto; @@ -43,6 +42,7 @@ import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic; import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric; import static org.apache.commons.lang.math.RandomUtils.nextInt; import static org.sonar.api.rule.RuleKey.EXTERNAL_RULE_REPO_PREFIX; +import static org.sonar.db.rule.RuleDescriptionSectionDto.createDefaultRuleDescriptionSection; /** * Utility class for tests involving rules @@ -65,13 +65,22 @@ public class RuleTesting { } public static RuleDefinitionDto newRule(RuleKey key) { + RuleDefinitionDto ruleDefinitionDto = newRuleWithoutSection(key); + ruleDefinitionDto.addRuleDescriptionSectionDto(createDefaultRuleDescriptionSection("description_" + randomAlphabetic(5))); + return ruleDefinitionDto; + } + + public static RuleDefinitionDto newRuleWithoutSection() { + return newRuleWithoutSection(randomRuleKey()); + } + + public static RuleDefinitionDto newRuleWithoutSection(RuleKey ruleKey) { return new RuleDefinitionDto() - .setRepositoryKey(key.repository()) - .setRuleKey(key.rule()) + .setRepositoryKey(ruleKey.repository()) + .setRuleKey(ruleKey.rule()) .setUuid("rule_uuid_" + randomAlphanumeric(5)) .setName("name_" + randomAlphanumeric(5)) - .setDescription("description_" + randomAlphanumeric(5)) - .setDescriptionFormat(Format.HTML) + .setDescriptionFormat(RuleDto.Format.HTML) .setType(RuleType.values()[nextInt(RuleType.values().length)]) .setStatus(RuleStatus.READY) .setConfigKey("configKey_" + randomAlphanumeric(5)) @@ -171,8 +180,8 @@ public class RuleTesting { .setRuleKey(ruleKey.rule()) .setRepositoryKey(ruleKey.repository()) .setName("Rule " + ruleKey.rule()) - .setDescription("Description " + ruleKey.rule()) - .setDescriptionFormat(Format.HTML) + .setDescriptionFormat(RuleDto.Format.HTML) + .addRuleDescriptionSectionDto(createDefaultRuleDescriptionSection("Description" + ruleKey.rule())) .setStatus(RuleStatus.READY) .setConfigKey("InternalKey" + ruleKey.rule()) .setSeverity(Severity.INFO) diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/rule/DefaultRuleFinder.java b/server/sonar-server-common/src/main/java/org/sonar/server/rule/DefaultRuleFinder.java index c31892506c8..e821838fd17 100644 --- a/server/sonar-server-common/src/main/java/org/sonar/server/rule/DefaultRuleFinder.java +++ b/server/sonar-server-common/src/main/java/org/sonar/server/rule/DefaultRuleFinder.java @@ -40,7 +40,8 @@ import org.sonar.db.rule.RuleDao; import org.sonar.db.rule.RuleDefinitionDto; import org.sonar.db.rule.RuleDto; import org.sonar.db.rule.RuleParamDto; -import org.sonar.markdown.Markdown; + +import static org.sonar.server.rule.RuleDescriptionFormatter.getDescriptionAsHtml; /** * Will be removed in the future. @@ -135,8 +136,6 @@ public class DefaultRuleFinder implements ServerRuleFinder { private static org.sonar.api.rules.Rule toRule(RuleDto rule, List params) { String severity = rule.getSeverityString(); - String description = rule.getDescription(); - RuleDto.Format descriptionFormat = rule.getDescriptionFormat(); org.sonar.api.rules.Rule apiRule = new org.sonar.api.rules.Rule(); apiRule @@ -151,14 +150,9 @@ public class DefaultRuleFinder implements ServerRuleFinder { .setSeverity(severity != null ? RulePriority.valueOf(severity) : null) .setStatus(rule.getStatus().name()) .setSystemTags(rule.getSystemTags().toArray(new String[rule.getSystemTags().size()])) - .setTags(rule.getTags().toArray(new String[rule.getTags().size()])); - if (description != null && descriptionFormat != null) { - if (RuleDto.Format.HTML.equals(descriptionFormat)) { - apiRule.setDescription(description); - } else { - apiRule.setDescription(Markdown.convertToHtml(description)); - } - } + .setTags(rule.getTags().toArray(new String[rule.getTags().size()])) + .setDescription(getDescriptionAsHtml(rule.getDefinition())); + List apiParams = new ArrayList<>(); for (RuleParamDto param : params) { diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/rule/HotspotRuleDescription.java b/server/sonar-server-common/src/main/java/org/sonar/server/rule/HotspotRuleDescription.java index a9e685e0cd4..b3b27628cb0 100644 --- a/server/sonar-server-common/src/main/java/org/sonar/server/rule/HotspotRuleDescription.java +++ b/server/sonar-server-common/src/main/java/org/sonar/server/rule/HotspotRuleDescription.java @@ -48,7 +48,7 @@ public class HotspotRuleDescription { } public static HotspotRuleDescription from(RuleDefinitionDto dto) { - String description = dto.isCustomRule() ? RuleDescriptionFormatter.getDescriptionAsHtml(dto) : dto.getDescription(); + String description = RuleDescriptionFormatter.getDescriptionAsHtml(dto); return from(description); } 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 0eeaaa83d9a..2cae26e2695 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 @@ -19,30 +19,49 @@ */ package org.sonar.server.rule; +import java.util.Collection; +import java.util.Objects; +import java.util.Optional; import org.sonar.db.rule.RuleDefinitionDto; +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; public class RuleDescriptionFormatter { private RuleDescriptionFormatter() { /* static helpers */ } - public static String getDescriptionAsHtml(RuleDefinitionDto ruleDto) { - String description = ruleDto.getDescription(); - RuleDto.Format descriptionFormat = ruleDto.getDescriptionFormat(); - if (description != null && descriptionFormat != null) { - switch (descriptionFormat) { - case MARKDOWN: - return Markdown.convertToHtml(description); - case HTML: - return description; - default: - throw new IllegalStateException(format("Rule description format '%s' is unknown for key '%s'", descriptionFormat, ruleDto.getKey().toString())); - } + public static String getDescriptionAsHtml(RuleDefinitionDto ruleDefinitionDto) { + if (ruleDefinitionDto.getDescriptionFormat() == null) { + return null; + } + Collection ruleDescriptionSectionDtos = ruleDefinitionDto.getRuleDescriptionSectionDtos(); + Optional ruleDescriptionSectionDto = findDefaultDescription(ruleDescriptionSectionDtos); + return ruleDescriptionSectionDto + .map(ruleDescriptionSection -> toHtml(ruleDefinitionDto, ruleDescriptionSection)) + .orElse(null); + } + + private static Optional findDefaultDescription(Collection ruleDescriptionSectionDtos) { + return ruleDescriptionSectionDtos.stream() + .filter(RuleDescriptionSectionDto::isDefault) + .collect(toOptional()); + } + + private static String toHtml(RuleDefinitionDto ruleDefinitionDto, RuleDescriptionSectionDto ruleDescriptionSectionDto) { + RuleDto.Format descriptionFormat = Objects.requireNonNull(ruleDefinitionDto.getDescriptionFormat(), + "Rule " + ruleDefinitionDto.getDescriptionFormat() + " contains section(s) but has no format set"); + switch (descriptionFormat) { + case MARKDOWN: + return Markdown.convertToHtml(ruleDescriptionSectionDto.getDescription()); + case HTML: + return ruleDescriptionSectionDto.getDescription(); + default: + throw new IllegalStateException(format("Rule description section format '%s' is unknown for rule key '%s'", descriptionFormat, ruleDefinitionDto.getKey())); } - return null; } } diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/rule/HotspotRuleDescriptionTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/rule/HotspotRuleDescriptionTest.java index 18982042bd3..12c34c19c62 100644 --- a/server/sonar-server-common/src/test/java/org/sonar/server/rule/HotspotRuleDescriptionTest.java +++ b/server/sonar-server-common/src/test/java/org/sonar/server/rule/HotspotRuleDescriptionTest.java @@ -26,17 +26,18 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.sonar.db.rule.RuleDefinitionDto; import org.sonar.db.rule.RuleDto; -import org.sonar.db.rule.RuleTesting; import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic; import static org.assertj.core.api.Assertions.assertThat; +import static org.sonar.db.rule.RuleDescriptionSectionDto.createDefaultRuleDescriptionSection; +import static org.sonar.db.rule.RuleTesting.newRuleWithoutSection; @RunWith(DataProviderRunner.class) public class HotspotRuleDescriptionTest { @Test public void parse_returns_all_empty_fields_when_no_description() { - RuleDefinitionDto dto = RuleTesting.newRule().setDescription(null); + RuleDefinitionDto dto = newRuleWithoutSection(); HotspotRuleDescription result = HotspotRuleDescription.from(dto); @@ -47,7 +48,7 @@ public class HotspotRuleDescriptionTest { @Test public void parse_returns_all_empty_fields_when_empty_description() { - RuleDefinitionDto dto = RuleTesting.newRule().setDescription(""); + RuleDefinitionDto dto = newRuleWithoutSection().addRuleDescriptionSectionDto(createDefaultRuleDescriptionSection("")); HotspotRuleDescription result = HotspotRuleDescription.from(dto); @@ -59,7 +60,7 @@ public class HotspotRuleDescriptionTest { @Test @UseDataProvider("descriptionsWithoutTitles") public void parse_to_risk_description_fields_when_desc_contains_no_section(String description) { - RuleDefinitionDto dto = RuleTesting.newRule().setDescription(description); + RuleDefinitionDto dto = newRuleWithoutSection().addRuleDescriptionSectionDto(createDefaultRuleDescriptionSection(description)); HotspotRuleDescription result = HotspotRuleDescription.from(dto); @@ -80,7 +81,8 @@ public class HotspotRuleDescriptionTest { @Test public void parse_return_null_risk_when_desc_starts_with_ask_yourself_title() { - RuleDefinitionDto dto = RuleTesting.newRule().setDescription(ASKATRISK + RECOMMENTEDCODINGPRACTICE); + RuleDefinitionDto dto = newRuleWithoutSection().addRuleDescriptionSectionDto( + createDefaultRuleDescriptionSection((ASKATRISK + RECOMMENTEDCODINGPRACTICE))); HotspotRuleDescription result = HotspotRuleDescription.from(dto); @@ -91,7 +93,8 @@ public class HotspotRuleDescriptionTest { @Test public void parse_return_null_vulnerable_when_no_ask_yourself_whether_title() { - RuleDefinitionDto dto = RuleTesting.newRule().setDescription(DESCRIPTION + RECOMMENTEDCODINGPRACTICE); + RuleDefinitionDto dto = newRuleWithoutSection() + .addRuleDescriptionSectionDto(createDefaultRuleDescriptionSection((DESCRIPTION + RECOMMENTEDCODINGPRACTICE))); HotspotRuleDescription result = HotspotRuleDescription.from(dto); @@ -102,7 +105,8 @@ public class HotspotRuleDescriptionTest { @Test public void parse_return_null_fixIt_when_desc_has_no_Recommended_Secure_Coding_Practices_title() { - RuleDefinitionDto dto = RuleTesting.newRule().setDescription(DESCRIPTION + ASKATRISK); + RuleDefinitionDto dto = newRuleWithoutSection() + .addRuleDescriptionSectionDto(createDefaultRuleDescriptionSection((DESCRIPTION + ASKATRISK))); HotspotRuleDescription result = HotspotRuleDescription.from(dto); @@ -113,7 +117,8 @@ public class HotspotRuleDescriptionTest { @Test public void parse_with_noncompliant_section_not_removed() { - RuleDefinitionDto dto = RuleTesting.newRule().setDescription(DESCRIPTION + NONCOMPLIANTCODE + COMPLIANTCODE); + RuleDefinitionDto dto = newRuleWithoutSection().addRuleDescriptionSectionDto( + createDefaultRuleDescriptionSection((DESCRIPTION + NONCOMPLIANTCODE + COMPLIANTCODE))); HotspotRuleDescription result = HotspotRuleDescription.from(dto); @@ -124,7 +129,8 @@ public class HotspotRuleDescriptionTest { @Test public void parse_moved_noncompliant_code() { - RuleDefinitionDto dto = RuleTesting.newRule().setDescription(DESCRIPTION + RECOMMENTEDCODINGPRACTICE + NONCOMPLIANTCODE + SEE); + RuleDefinitionDto dto = newRuleWithoutSection().addRuleDescriptionSectionDto( + createDefaultRuleDescriptionSection((DESCRIPTION + RECOMMENTEDCODINGPRACTICE + NONCOMPLIANTCODE + SEE))); HotspotRuleDescription result = HotspotRuleDescription.from(dto); @@ -137,7 +143,8 @@ public class HotspotRuleDescriptionTest { @Test public void parse_moved_sensitivecode_code() { - RuleDefinitionDto dto = RuleTesting.newRule().setDescription(DESCRIPTION + ASKATRISK + RECOMMENTEDCODINGPRACTICE + SENSITIVECODE + SEE); + RuleDefinitionDto dto = newRuleWithoutSection().addRuleDescriptionSectionDto( + createDefaultRuleDescriptionSection((DESCRIPTION + ASKATRISK + RECOMMENTEDCODINGPRACTICE + SENSITIVECODE + SEE))); HotspotRuleDescription result = HotspotRuleDescription.from(dto); @@ -153,18 +160,18 @@ public class HotspotRuleDescriptionTest { String askContent = "This is the ask section content"; String recommendedContent = "This is the recommended section content"; - RuleDefinitionDto dto = RuleTesting.newRule() + RuleDefinitionDto dto = newRuleWithoutSection() .setTemplateUuid("123") .setDescriptionFormat(RuleDto.Format.MARKDOWN) - .setDescription( + .addRuleDescriptionSectionDto(createDefaultRuleDescriptionSection( ruleDescription + "\n" - + "== Exceptions" + "\n" - + exceptionsContent + "\n" - + "== Ask Yourself Whether" + "\n" - + askContent + "\n" - + "== Recommended Secure Coding Practices" + "\n" - + recommendedContent + "\n" - ); + + "== Exceptions" + "\n" + + exceptionsContent + "\n" + + "== Ask Yourself Whether" + "\n" + + askContent + "\n" + + "== Recommended Secure Coding Practices" + "\n" + + recommendedContent + "\n" + )); HotspotRuleDescription result = HotspotRuleDescription.from(dto); 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 d0f5d6df339..b50f77deb3e 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 @@ -21,37 +21,42 @@ package org.sonar.server.rule; import org.junit.Test; import org.sonar.db.rule.RuleDefinitionDto; +import org.sonar.db.rule.RuleDescriptionSectionDto; import org.sonar.db.rule.RuleDto; import static org.assertj.core.api.Assertions.assertThat; +import static org.sonar.db.rule.RuleDescriptionSectionDto.createDefaultRuleDescriptionSection; public class RuleDescriptionFormatterTest { + private static final RuleDescriptionSectionDto HTML_SECTION = createDefaultRuleDescriptionSection("*md* ``description``"); + private static final RuleDescriptionSectionDto MARKDOWN_SECTION = createDefaultRuleDescriptionSection("*md* ``description``"); + @Test public void getMarkdownDescriptionAsHtml() { - RuleDefinitionDto rule = new RuleDefinitionDto().setDescription("*md* ``description``").setDescriptionFormat(RuleDto.Format.MARKDOWN); + RuleDefinitionDto rule = new RuleDefinitionDto().setDescriptionFormat(RuleDto.Format.MARKDOWN).addRuleDescriptionSectionDto(MARKDOWN_SECTION); String html = RuleDescriptionFormatter.getDescriptionAsHtml(rule); assertThat(html).isEqualTo("md description"); } @Test public void getHtmlDescriptionAsIs() { - String description = "*md* ``description``"; - RuleDefinitionDto rule = new RuleDefinitionDto().setDescription(description).setDescriptionFormat(RuleDto.Format.HTML); + RuleDefinitionDto rule = new RuleDefinitionDto().setDescriptionFormat(RuleDto.Format.HTML).addRuleDescriptionSectionDto(HTML_SECTION); String html = RuleDescriptionFormatter.getDescriptionAsHtml(rule); - assertThat(html).isEqualTo(description); + assertThat(html).isEqualTo(HTML_SECTION.getDescription()); } @Test - public void handleNullDescription() { - RuleDefinitionDto rule = new RuleDefinitionDto().setDescription(null).setDescriptionFormat(RuleDto.Format.HTML); + public void handleEmptyDescription() { + RuleDefinitionDto rule = new RuleDefinitionDto().setDescriptionFormat(RuleDto.Format.HTML); String result = RuleDescriptionFormatter.getDescriptionAsHtml(rule); assertThat(result).isNull(); } @Test public void handleNullDescriptionFormat() { - RuleDefinitionDto rule = new RuleDefinitionDto().setDescription("whatever").setDescriptionFormat(null); + RuleDescriptionSectionDto sectionWithNullFormat = createDefaultRuleDescriptionSection("whatever"); + RuleDefinitionDto rule = new RuleDefinitionDto().addRuleDescriptionSectionDto(sectionWithNullFormat); String result = RuleDescriptionFormatter.getDescriptionAsHtml(rule); assertThat(result).isNull(); } diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/rule/index/RuleIndexTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/rule/index/RuleIndexTest.java index 9ca8078f54f..d4ae6d45d5b 100644 --- a/server/sonar-server-common/src/test/java/org/sonar/server/rule/index/RuleIndexTest.java +++ b/server/sonar-server-common/src/test/java/org/sonar/server/rule/index/RuleIndexTest.java @@ -22,14 +22,11 @@ package org.sonar.server.rule.index; import com.google.common.collect.ImmutableList; import com.google.common.collect.Sets; import java.util.ArrayList; -import java.util.Arrays; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.function.Consumer; -import java.util.stream.Collectors; -import org.assertj.core.api.Assertions; import org.junit.Rule; import org.junit.Test; import org.sonar.api.impl.utils.AlwaysIncreasingSystem2; @@ -66,6 +63,7 @@ import static org.sonar.api.rules.RuleType.BUG; import static org.sonar.api.rules.RuleType.CODE_SMELL; import static org.sonar.api.rules.RuleType.SECURITY_HOTSPOT; import static org.sonar.api.rules.RuleType.VULNERABILITY; +import static org.sonar.db.rule.RuleDescriptionSectionDto.createDefaultRuleDescriptionSection; import static org.sonar.db.rule.RuleTesting.setCreatedAt; import static org.sonar.db.rule.RuleTesting.setIsExternal; import static org.sonar.db.rule.RuleTesting.setIsTemplate; @@ -216,19 +214,19 @@ public class RuleIndexTest { // otherwise the generated random values may raise false-positives RuleDefinitionDto rule1 = createJavaRule(rule -> rule.setRuleKey("123") .setName("rule 123") - .setDescription("My great rule CWE-123 which makes your code 1000 times better!")); + .addRuleDescriptionSectionDto(createDefaultRuleDescriptionSection("My great rule CWE-123 which makes your code 1000 times better!"))); RuleDefinitionDto rule2 = createJavaRule(rule -> rule.setRuleKey("124") .setName("rule 124") - .setDescription("Another great and shiny rule CWE-124")); + .addRuleDescriptionSectionDto(createDefaultRuleDescriptionSection("Another great and shiny rule CWE-124"))); RuleDefinitionDto rule3 = createJavaRule(rule -> rule.setRuleKey("1000") .setName("rule 1000") - .setDescription("Another great rule CWE-1000")); + .addRuleDescriptionSectionDto(createDefaultRuleDescriptionSection("Another great rule CWE-1000"))); RuleDefinitionDto rule4 = createJavaRule(rule -> rule.setRuleKey("404") .setName("rule 404") - .setDescription("

HTML-Geeks

special formatting!

insidetables
")); + .addRuleDescriptionSectionDto(createDefaultRuleDescriptionSection("

HTML-Geeks

special formatting!

insidetables
"))); RuleDefinitionDto rule5 = createJavaRule(rule -> rule.setRuleKey("405") .setName("rule 405") - .setDescription("internationalization missunderstandings alsdkjfnadklsjfnadkdfnsksdjfn")); + .addRuleDescriptionSectionDto(createDefaultRuleDescriptionSection("internationalization missunderstandings alsdkjfnadklsjfnadkdfnsksdjfn"))); index(); // partial match at word boundary diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/rule/index/RuleIndexerTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/rule/index/RuleIndexerTest.java index 6fc13907884..e3362dd71ee 100644 --- a/server/sonar-server-common/src/test/java/org/sonar/server/rule/index/RuleIndexerTest.java +++ b/server/sonar-server-common/src/test/java/org/sonar/server/rule/index/RuleIndexerTest.java @@ -28,7 +28,6 @@ import java.util.Random; import java.util.Set; import java.util.stream.IntStream; import java.util.stream.Stream; -import javax.annotation.Nullable; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -41,6 +40,7 @@ import org.sonar.db.DbClient; import org.sonar.db.DbSession; import org.sonar.db.DbTester; import org.sonar.db.rule.RuleDefinitionDto; +import org.sonar.db.rule.RuleDescriptionSectionDto; import org.sonar.db.rule.RuleDto; import org.sonar.db.rule.RuleDto.Scope; import org.sonar.db.rule.RuleTesting; @@ -55,6 +55,7 @@ import static java.util.stream.Collectors.joining; import static java.util.stream.Collectors.toSet; import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic; import static org.assertj.core.api.Assertions.assertThat; +import static org.sonar.db.rule.RuleDescriptionSectionDto.createDefaultRuleDescriptionSection; import static org.sonar.server.rule.index.RuleIndexDefinition.TYPE_RULE; import static org.sonar.server.security.SecurityStandards.CWES_BY_SQ_CATEGORY; import static org.sonar.server.security.SecurityStandards.SQ_CATEGORY_KEYS_ORDERING; @@ -62,11 +63,14 @@ import static org.sonar.server.security.SecurityStandards.SQ_CATEGORY_KEYS_ORDER @RunWith(DataProviderRunner.class) public class RuleIndexerTest { - public static final String VALID_HOTSPOT_RULE_DESCRIPTION = "acme\n" + + private static final String VALID_HOTSPOT_RULE_DESCRIPTION = "acme\n" + "

Ask Yourself Whether

\n" + "bar\n" + "

Recommended Secure Coding Practices

\n" + "foo"; + + private static final RuleDescriptionSectionDto RULE_DESCRIPTION_SECTION_DTO = createDefaultRuleDescriptionSection(VALID_HOTSPOT_RULE_DESCRIPTION); + @Rule public EsTester es = EsTester.create(); @Rule @@ -83,8 +87,8 @@ public class RuleIndexerTest { .setRepositoryKey("xoo") .setConfigKey("S1") .setName("Null Pointer") - .setDescription("S001 desc") .setDescriptionFormat(RuleDto.Format.HTML) + .addRuleDescriptionSectionDto(RULE_DESCRIPTION_SECTION_DTO) .setLanguage("xoo") .setSeverity(Severity.BLOCKER) .setStatus(RuleStatus.READY) @@ -127,7 +131,8 @@ public class RuleIndexerTest { @Test public void index_long_rule_description() { String description = IntStream.range(0, 100000).map(i -> i % 100).mapToObj(Integer::toString).collect(joining(" ")); - RuleDefinitionDto rule = dbTester.rules().insert(r -> r.setDescription(description)); + RuleDescriptionSectionDto ruleDescriptionSectionDto = createDefaultRuleDescriptionSection(description); + RuleDefinitionDto rule = dbTester.rules().insert(r -> r.addRuleDescriptionSectionDto(ruleDescriptionSectionDto)); underTest.commitAndIndex(dbTester.getSession(), rule.getUuid()); assertThat(es.countDocuments(TYPE_RULE)).isOne(); @@ -143,7 +148,7 @@ public class RuleIndexerTest { RuleDefinitionDto rule = dbTester.rules().insert(RuleTesting.newRule() .setType(RuleType.SECURITY_HOTSPOT) .setSecurityStandards(standards) - .setDescription(VALID_HOTSPOT_RULE_DESCRIPTION)); + .addRuleDescriptionSectionDto(RULE_DESCRIPTION_SECTION_DTO)); underTest.commitAndIndex(dbTester.getSession(), rule.getUuid()); assertThat(logTester.getLogs()).hasSize(1); @@ -174,11 +179,9 @@ public class RuleIndexerTest { } @Test - @UseDataProvider("nullEmptyOrNoTitleDescription") - public void log_debug_when_hotspot_rule_description_is_null_or_empty(@Nullable String description) { - RuleDefinitionDto rule = dbTester.rules().insert(RuleTesting.newRule() - .setType(RuleType.SECURITY_HOTSPOT) - .setDescription(description)); + public void log_debug_when_hotspot_rule_no_description () { + RuleDefinitionDto rule = dbTester.rules().insert(RuleTesting.newRuleWithoutSection() + .setType(RuleType.SECURITY_HOTSPOT)); underTest.commitAndIndex(dbTester.getSession(), rule.getUuid()); assertThat(logTester.getLogs()).hasSize(1); @@ -188,19 +191,11 @@ public class RuleIndexerTest { rule.getKey())); } - @DataProvider - public static Object[][] nullEmptyOrNoTitleDescription() { - return new Object[][] { - {null}, - {""}, - }; - } - @Test public void log_debug_when_hotspot_rule_description_has_none_of_the_key_titles() { RuleDefinitionDto rule = dbTester.rules().insert(RuleTesting.newRule() .setType(RuleType.SECURITY_HOTSPOT) - .setDescription(randomAlphabetic(30))); + .addRuleDescriptionSectionDto(createDefaultRuleDescriptionSection(randomAlphabetic(30)))); underTest.commitAndIndex(dbTester.getSession(), rule.getUuid()); assertThat(logTester.getLogs()).hasSize(1); @@ -214,9 +209,9 @@ public class RuleIndexerTest { public void log_debug_when_hotspot_rule_description_is_missing_fixIt_tab_content() { RuleDefinitionDto rule = dbTester.rules().insert(RuleTesting.newRule() .setType(RuleType.SECURITY_HOTSPOT) - .setDescription("bar\n" + + .addRuleDescriptionSectionDto(createDefaultRuleDescriptionSection("bar\n" + "

Ask Yourself Whether

\n" + - "foo")); + "foo"))); underTest.commitAndIndex(dbTester.getSession(), rule.getUuid()); assertThat(logTester.getLogs()).hasSize(1); @@ -230,10 +225,10 @@ public class RuleIndexerTest { public void log_debug_when_hotspot_rule_description_is_missing_risk_tab_content() { RuleDefinitionDto rule = dbTester.rules().insert(RuleTesting.newRule() .setType(RuleType.SECURITY_HOTSPOT) - .setDescription("

Ask Yourself Whether

\n" + + .addRuleDescriptionSectionDto(createDefaultRuleDescriptionSection("

Ask Yourself Whether

\n" + "bar\n" + "

Recommended Secure Coding Practices

\n" + - "foo")); + "foo"))); underTest.commitAndIndex(dbTester.getSession(), rule.getUuid()); assertThat(logTester.getLogs()).hasSize(1); @@ -247,9 +242,9 @@ public class RuleIndexerTest { public void log_debug_when_hotspot_rule_description_is_missing_vulnerable_tab_content() { RuleDefinitionDto rule = dbTester.rules().insert(RuleTesting.newRule() .setType(RuleType.SECURITY_HOTSPOT) - .setDescription("bar\n" + + .addRuleDescriptionSectionDto(createDefaultRuleDescriptionSection("bar\n" + "

Recommended Secure Coding Practices

\n" + - "foo")); + "foo"))); underTest.commitAndIndex(dbTester.getSession(), rule.getUuid()); assertThat(logTester.getLogs()).hasSize(1); diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/rule/CachingRuleFinder.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/rule/CachingRuleFinder.java index 17199eb9ce1..43f6fdb03c6 100644 --- a/server/sonar-webserver-api/src/main/java/org/sonar/server/rule/CachingRuleFinder.java +++ b/server/sonar-webserver-api/src/main/java/org/sonar/server/rule/CachingRuleFinder.java @@ -41,9 +41,7 @@ import org.sonar.core.util.stream.MoreCollectors; import org.sonar.db.DbClient; import org.sonar.db.DbSession; import org.sonar.db.rule.RuleDefinitionDto; -import org.sonar.db.rule.RuleDto; import org.sonar.db.rule.RuleParamDto; -import org.sonar.markdown.Markdown; import static java.util.Collections.emptyList; import static java.util.Collections.unmodifiableMap; @@ -147,8 +145,6 @@ public class CachingRuleFinder implements ServerRuleFinder { private static Rule toRule(RuleDefinitionDto ruleDefinition, List params) { String severity = ruleDefinition.getSeverityString(); - String description = ruleDefinition.getDescription(); - RuleDto.Format descriptionFormat = ruleDefinition.getDescriptionFormat(); Rule apiRule = new Rule(); apiRule @@ -163,14 +159,9 @@ public class CachingRuleFinder implements ServerRuleFinder { .setSeverity(severity != null ? RulePriority.valueOf(severity) : null) .setStatus(ruleDefinition.getStatus().name()) .setSystemTags(ruleDefinition.getSystemTags().toArray(new String[ruleDefinition.getSystemTags().size()])) - .setTags(new String[0]); - if (description != null && descriptionFormat != null) { - if (RuleDto.Format.HTML.equals(descriptionFormat)) { - apiRule.setDescription(description); - } else { - apiRule.setDescription(Markdown.convertToHtml(description)); - } - } + .setTags(new String[0]) + .setDescription(RuleDescriptionFormatter.getDescriptionAsHtml(ruleDefinition)); + List apiParams = new ArrayList<>(); for (RuleParamDto param : params) { diff --git a/server/sonar-webserver-api/src/test/java/org/sonar/server/rule/CachingRuleFinderTest.java b/server/sonar-webserver-api/src/test/java/org/sonar/server/rule/CachingRuleFinderTest.java index 0d37e1221fe..cd2c2235981 100644 --- a/server/sonar-webserver-api/src/test/java/org/sonar/server/rule/CachingRuleFinderTest.java +++ b/server/sonar-webserver-api/src/test/java/org/sonar/server/rule/CachingRuleFinderTest.java @@ -46,6 +46,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; +import static org.sonar.db.rule.RuleDescriptionSectionDto.createDefaultRuleDescriptionSection; public class CachingRuleFinderTest { @org.junit.Rule @@ -422,7 +423,7 @@ public class CachingRuleFinderTest { assertThat(rule.getSeverity().name()).isEqualTo(ruleDefinition.getSeverityString()); assertThat(rule.getSystemTags()).isEqualTo(ruleDefinition.getSystemTags().toArray(new String[0])); assertThat(rule.getTags()).isEmpty(); - assertThat(rule.getDescription()).isEqualTo(ruleDefinition.getDescription()); + assertThat(rule.getDescription()).isEqualTo(createDefaultRuleDescriptionSection(randomAlphabetic(5)).getDescription()); assertThat(rule.getParams()).hasSize(1); org.sonar.api.rules.RuleParam param = rule.getParams().iterator().next(); diff --git a/server/sonar-webserver-core/src/main/java/org/sonar/server/rule/RegisterRules.java b/server/sonar-webserver-core/src/main/java/org/sonar/server/rule/RegisterRules.java index 5309cbb697f..fb4e08ec15a 100644 --- a/server/sonar-webserver-core/src/main/java/org/sonar/server/rule/RegisterRules.java +++ b/server/sonar-webserver-core/src/main/java/org/sonar/server/rule/RegisterRules.java @@ -75,9 +75,11 @@ import static java.lang.String.format; import static java.util.Collections.emptyList; import static java.util.Collections.emptySet; import static java.util.Collections.unmodifiableMap; +import static org.apache.commons.lang.StringUtils.isNotEmpty; import static org.sonar.core.util.stream.MoreCollectors.toList; import static org.sonar.core.util.stream.MoreCollectors.toSet; import static org.sonar.core.util.stream.MoreCollectors.uniqueIndex; +import static org.sonar.db.rule.RuleDescriptionSectionDto.createDefaultRuleDescriptionSection; /** * Register rules at server startup @@ -261,10 +263,10 @@ public class RegisterRules implements Startable { private Stream getAllModified() { return Stream.of( - created.stream(), - updated.stream(), - removed.stream(), - renamed.keySet().stream()) + created.stream(), + updated.stream(), + removed.stream(), + renamed.keySet().stream()) .flatMap(s -> s); } @@ -398,13 +400,14 @@ public class RegisterRules implements Startable { .setIsAdHoc(false) .setCreatedAt(system2.now()) .setUpdatedAt(system2.now()); - if (ruleDef.htmlDescription() != null) { - ruleDto.setDescription(ruleDef.htmlDescription()); + if (isNotEmpty(ruleDef.htmlDescription())) { + ruleDto.addRuleDescriptionSectionDto(createDefaultRuleDescriptionSection(ruleDef.htmlDescription())); ruleDto.setDescriptionFormat(Format.HTML); - } else { - ruleDto.setDescription(ruleDef.markdownDescription()); + } else if (isNotEmpty(ruleDef.markdownDescription())) { + ruleDto.addRuleDescriptionSectionDto(createDefaultRuleDescriptionSection(ruleDef.markdownDescription())); ruleDto.setDescriptionFormat(Format.MARKDOWN); } + DebtRemediationFunction debtRemediationFunction = ruleDef.debtRemediationFunction(); if (debtRemediationFunction != null) { ruleDto.setDefRemediationFunction(debtRemediationFunction.type().name()); @@ -481,20 +484,30 @@ public class RegisterRules implements Startable { return changed; } - private static boolean mergeDescription(RulesDefinition.Rule def, RuleDefinitionDto dto) { + private static boolean mergeDescription(RulesDefinition.Rule rule, RuleDefinitionDto ruleDefinitionDto) { boolean changed = false; - if (def.htmlDescription() != null && !Objects.equals(dto.getDescription(), def.htmlDescription())) { - dto.setDescription(def.htmlDescription()); - dto.setDescriptionFormat(Format.HTML); + + String currentDescription = ruleDefinitionDto.getDefaultRuleDescriptionSectionDto() != null ? ruleDefinitionDto.getDefaultRuleDescriptionSectionDto().getDescription() : null; + if (isHtmlDescriptionUpdated(rule, currentDescription)) { + ruleDefinitionDto.addOrReplaceRuleDescriptionSectionDto(createDefaultRuleDescriptionSection(rule.htmlDescription())); + ruleDefinitionDto.setDescriptionFormat(Format.HTML); changed = true; - } else if (def.markdownDescription() != null && !Objects.equals(dto.getDescription(), def.markdownDescription())) { - dto.setDescription(def.markdownDescription()); - dto.setDescriptionFormat(Format.MARKDOWN); + } else if (isMarkdownDescriptionUpdated(rule, currentDescription)) { + ruleDefinitionDto.addOrReplaceRuleDescriptionSectionDto(createDefaultRuleDescriptionSection(rule.markdownDescription())); + ruleDefinitionDto.setDescriptionFormat(Format.MARKDOWN); changed = true; } return changed; } + private static boolean isMarkdownDescriptionUpdated(RulesDefinition.Rule rule, @Nullable String currentDescription) { + return isNotEmpty(rule.markdownDescription()) && !Objects.equals(rule.markdownDescription(), currentDescription); + } + + private static boolean isHtmlDescriptionUpdated(RulesDefinition.Rule def, @Nullable String currentDescription) { + return isNotEmpty(def.htmlDescription()) && !Objects.equals(def.htmlDescription(), currentDescription); + } + private static boolean mergeDebtDefinitions(RulesDefinition.Rule def, RuleDefinitionDto dto) { // Debt definitions are set to null if the sub-characteristic and the remediation function are null DebtRemediationFunction debtRemediationFunction = def.debtRemediationFunction(); 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 6c7f40dc89f..e9cbb4ebbc7 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 @@ -88,6 +88,7 @@ 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; import static org.sonar.api.server.rule.RulesDefinition.OwaspTop10Version.Y2021; +import static org.sonar.db.rule.RuleDescriptionSectionDto.createDefaultRuleDescriptionSection; @RunWith(DataProviderRunner.class) public class RegisterRulesTest { @@ -124,7 +125,6 @@ public class RegisterRulesTest { private ActiveRuleIndexer activeRuleIndexer; private RuleIndex ruleIndex; - @Before public void before() { ruleIndexer = new RuleIndexer(es.client(), dbClient); @@ -140,7 +140,7 @@ public class RegisterRulesTest { assertThat(dbClient.ruleDao().selectAllDefinitions(db.getSession())).hasSize(3); RuleDto rule1 = dbClient.ruleDao().selectOrFailByKey(db.getSession(), RULE_KEY1); assertThat(rule1.getName()).isEqualTo("One"); - assertThat(rule1.getDescription()).isEqualTo("Description of One"); + assertThat(rule1.getDefaultRuleDescriptionSection().getDescription()).isEqualTo("Description of One"); assertThat(rule1.getSeverityString()).isEqualTo(BLOCKER); assertThat(rule1.getTags()).isEmpty(); assertThat(rule1.getSystemTags()).containsOnly("tag1", "tag2", "tag3"); @@ -159,7 +159,7 @@ public class RegisterRulesTest { RuleDto hotspotRule = dbClient.ruleDao().selectOrFailByKey(db.getSession(), HOTSPOT_RULE_KEY); assertThat(hotspotRule.getName()).isEqualTo("Hotspot"); - assertThat(hotspotRule.getDescription()).isEqualTo("Minimal hotspot"); + assertThat(hotspotRule.getDefaultRuleDescriptionSection().getDescription()).isEqualTo("Minimal hotspot"); assertThat(hotspotRule.getCreatedAt()).isEqualTo(DATE1.getTime()); assertThat(hotspotRule.getUpdatedAt()).isEqualTo(DATE1.getTime()); assertThat(hotspotRule.getType()).isEqualTo(RuleType.SECURITY_HOTSPOT.getDbConstant()); @@ -188,7 +188,7 @@ public class RegisterRulesTest { assertThat(dbClient.ruleDao().selectAllDefinitions(db.getSession())).hasSize(2); RuleDto rule1 = dbClient.ruleDao().selectOrFailByKey(db.getSession(), EXTERNAL_RULE_KEY1); assertThat(rule1.getName()).isEqualTo("One"); - assertThat(rule1.getDescription()).isEqualTo("Description of One"); + assertThat(rule1.getDefaultRuleDescriptionSection().getDescription()).isEqualTo("Description of One"); assertThat(rule1.getSeverityString()).isEqualTo(BLOCKER); assertThat(rule1.getTags()).isEmpty(); assertThat(rule1.getSystemTags()).containsOnly("tag1", "tag2", "tag3"); @@ -207,7 +207,7 @@ public class RegisterRulesTest { RuleDto hotspotRule = dbClient.ruleDao().selectOrFailByKey(db.getSession(), EXTERNAL_HOTSPOT_RULE_KEY); assertThat(hotspotRule.getName()).isEqualTo("Hotspot"); - assertThat(hotspotRule.getDescription()).isEqualTo("Minimal hotspot"); + assertThat(hotspotRule.getDefaultRuleDescriptionSection().getDescription()).isEqualTo("Minimal hotspot"); assertThat(hotspotRule.getCreatedAt()).isEqualTo(DATE1.getTime()); assertThat(hotspotRule.getUpdatedAt()).isEqualTo(DATE1.getTime()); assertThat(hotspotRule.getType()).isEqualTo(RuleType.SECURITY_HOTSPOT.getDbConstant()); @@ -335,7 +335,7 @@ public class RegisterRulesTest { // rule1 has been updated rule1 = dbClient.ruleDao().selectOrFailByKey(db.getSession(), RULE_KEY1); assertThat(rule1.getName()).isEqualTo("One v2"); - assertThat(rule1.getDescription()).isEqualTo("Description of One v2"); + assertThat(rule1.getDefaultRuleDescriptionSection().getDescription()).isEqualTo("Description of One v2"); assertThat(rule1.getSeverityString()).isEqualTo(INFO); assertThat(rule1.getTags()).containsOnly("usertag1", "usertag2"); assertThat(rule1.getSystemTags()).containsOnly("tag1", "tag4"); @@ -449,7 +449,7 @@ public class RegisterRulesTest { // rule1 has been updated RuleDto rule1 = dbClient.ruleDao().selectOrFailByKey(db.getSession(), RuleKey.of("fake", "rule")); assertThat(rule1.getName()).isEqualTo("Name2"); - assertThat(rule1.getDescription()).isEqualTo("Description"); + assertThat(rule1.getDefaultRuleDescriptionSection().getDescription()).isEqualTo("Description"); assertThat(ruleIndex.search(new RuleQuery().setQueryText("Name2"), new SearchOptions()).getTotal()).isOne(); assertThat(ruleIndex.search(new RuleQuery().setQueryText("Name1"), new SearchOptions()).getTotal()).isZero(); @@ -528,7 +528,7 @@ public class RegisterRulesTest { RuleDto rule2 = dbClient.ruleDao().selectOrFailByKey(db.getSession(), RuleKey.of(repository, ruleKey2)); assertThat(rule2.getUuid()).isEqualTo(rule1.getUuid()); assertThat(rule2.getName()).isEqualTo("Name2"); - assertThat(rule2.getDescription()).isEqualTo(rule1.getDescription()); + assertThat(rule2.getDefaultRuleDescriptionSection().getDescription()).isEqualTo(rule1.getDefaultRuleDescriptionSection().getDescription()); SearchIdResult searchRule2 = ruleIndex.search(new RuleQuery().setQueryText("Name2"), new SearchOptions()); assertThat(searchRule2.getUuids()).containsOnly(rule2.getUuid()); @@ -570,7 +570,7 @@ public class RegisterRulesTest { RuleDto rule2 = dbClient.ruleDao().selectOrFailByKey(db.getSession(), RuleKey.of(repository2, ruleKey)); assertThat(rule2.getUuid()).isEqualTo(rule1.getUuid()); assertThat(rule2.getName()).isEqualTo("Name2"); - assertThat(rule2.getDescription()).isEqualTo(rule1.getDescription()); + assertThat(rule2.getDefaultRuleDescriptionSection().getDescription()).isEqualTo(rule1.getDefaultRuleDescriptionSection().getDescription()); SearchIdResult searchRule2 = ruleIndex.search(new RuleQuery().setQueryText("Name2"), new SearchOptions()); assertThat(searchRule2.getUuids()).containsOnly(rule2.getUuid()); @@ -610,7 +610,7 @@ public class RegisterRulesTest { RuleDto rule2 = dbClient.ruleDao().selectOrFailByKey(db.getSession(), RuleKey.of(repo2, ruleKey2)); assertThat(rule2.getUuid()).isEqualTo(rule1.getUuid()); assertThat(rule2.getName()).isEqualTo(rule1.getName()); - assertThat(rule2.getDescription()).isEqualTo(rule1.getDescription()); + assertThat(rule2.getDefaultRuleDescriptionSection().getDescription()).isEqualTo(rule1.getDefaultRuleDescriptionSection().getDescription()); assertThat(ruleIndex.search(new RuleQuery().setQueryText(name), new SearchOptions()).getUuids()) .containsOnly(rule2.getUuid()); @@ -618,7 +618,7 @@ public class RegisterRulesTest { @DataProvider public static Object[][] allRenamingCases() { - return new Object[][]{ + return new Object[][] { {"repo1", "rule1", "repo1", "rule2"}, {"repo1", "rule1", "repo2", "rule1"}, {"repo1", "rule1", "repo2", "rule2"}, @@ -688,7 +688,7 @@ public class RegisterRulesTest { // rule1 has been updated RuleDto rule1 = dbClient.ruleDao().selectOrFailByKey(db.getSession(), RuleKey.of("fake", "rule")); assertThat(rule1.getName()).isEqualTo("Name"); - assertThat(rule1.getDescription()).isEqualTo("Desc2"); + assertThat(rule1.getDefaultRuleDescriptionSection().getDescription()).isEqualTo("Desc2"); assertThat(ruleIndex.search(new RuleQuery().setQueryText("Desc2"), new SearchOptions()).getTotal()).isOne(); assertThat(ruleIndex.search(new RuleQuery().setQueryText("Desc1"), new SearchOptions()).getTotal()).isZero(); @@ -702,7 +702,7 @@ public class RegisterRulesTest { NewRepository repo = context.createExternalRepository("fake", rule.getLanguage()); repo.createRule(rule.getRuleKey()) .setName(rule.getName()) - .setHtmlDescription(rule.getDescription()); + .setHtmlDescription(rule.getDefaultRuleDescriptionSectionDto().getDescription()); repo.done(); }); @@ -836,7 +836,7 @@ public class RegisterRulesTest { .setRepositoryKey("findbugs") .setName("Rule One") .setScope(Scope.ALL) - .setDescription("Rule one description") + .addRuleDescriptionSectionDto(createDefaultRuleDescriptionSection("Rule one description")) .setDescriptionFormat(RuleDto.Format.HTML) .setSystemTags(newHashSet("tag1", "tag2"))); db.getSession().commit(); diff --git a/server/sonar-webserver-pushapi/src/test/java/org/sonar/server/qualityprofile/builtin/QualityProfileChangeEventServiceImplTest.java b/server/sonar-webserver-pushapi/src/test/java/org/sonar/server/qualityprofile/builtin/QualityProfileChangeEventServiceImplTest.java index 1e12fda35ca..77b5db39783 100644 --- a/server/sonar-webserver-pushapi/src/test/java/org/sonar/server/qualityprofile/builtin/QualityProfileChangeEventServiceImplTest.java +++ b/server/sonar-webserver-pushapi/src/test/java/org/sonar/server/qualityprofile/builtin/QualityProfileChangeEventServiceImplTest.java @@ -47,7 +47,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.tuple; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; -import static org.sonar.db.rule.RuleDto.Format.MARKDOWN; +import static org.sonar.db.rule.RuleDescriptionSectionDto.createDefaultRuleDescriptionSection; import static org.sonar.db.rule.RuleTesting.newCustomRule; import static org.sonar.db.rule.RuleTesting.newTemplateRule; import static org.sonar.server.qualityprofile.ActiveRuleChange.Type.ACTIVATED; @@ -73,8 +73,8 @@ public class QualityProfileChangeEventServiceImplTest { .setLanguage("xoo") .setRepositoryKey("repo") .setRuleKey("ruleKey") - .setDescription("
line1\nline2
") - .setDescriptionFormat(MARKDOWN); + .setDescriptionFormat(RuleDto.Format.MARKDOWN) + .addRuleDescriptionSectionDto(createDefaultRuleDescriptionSection("
line1\nline2
")); db.rules().insert(rule1); ActiveRuleDto activeRuleDto = ActiveRuleDto.createFor(qualityProfileDto, rule1); diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/rule/RuleCreator.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/rule/RuleCreator.java index ed07ddfa98c..e67c9d22a4d 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/rule/RuleCreator.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/rule/RuleCreator.java @@ -39,6 +39,7 @@ import org.sonar.core.util.UuidFactory; import org.sonar.db.DbClient; import org.sonar.db.DbSession; import org.sonar.db.rule.RuleDefinitionDto; +import org.sonar.db.rule.RuleDescriptionSectionDto; import org.sonar.db.rule.RuleDto; import org.sonar.db.rule.RuleDto.Format; import org.sonar.db.rule.RuleMetadataDto; @@ -50,6 +51,7 @@ import org.sonar.server.util.TypeValidations; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.collect.Lists.newArrayList; import static java.lang.String.format; +import static org.sonar.db.rule.RuleDescriptionSectionDto.createDefaultRuleDescriptionSection; import static org.sonar.server.exceptions.BadRequestException.checkRequest; @ServerSide @@ -189,8 +191,6 @@ public class RuleCreator { .setTemplateUuid(templateRuleDto.getUuid()) .setConfigKey(templateRuleDto.getConfigKey()) .setName(newRule.name()) - .setDescription(newRule.markdownDescription()) - .setDescriptionFormat(Format.MARKDOWN) .setSeverity(newRule.severity()) .setStatus(newRule.status()) .setType(newRule.type() == null ? templateRuleDto.getType() : newRule.type().getDbConstant()) @@ -206,6 +206,13 @@ public class RuleCreator { .setIsAdHoc(false) .setCreatedAt(system2.now()) .setUpdatedAt(system2.now()); + + if (newRule.markdownDescription() != null) { + RuleDescriptionSectionDto ruleDescriptionSectionDto = createDefaultRuleDescriptionSection(newRule.markdownDescription()); + ruleDefinition.setDescriptionFormat(Format.MARKDOWN); + ruleDefinition.addRuleDescriptionSectionDto(ruleDescriptionSectionDto); + } + dbClient.ruleDao().insert(dbSession, ruleDefinition); Set tags = templateRuleDto.getTags(); diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/rule/RuleUpdater.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/rule/RuleUpdater.java index 09dc0e58de0..78e4aebf313 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/rule/RuleUpdater.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/rule/RuleUpdater.java @@ -43,6 +43,7 @@ import org.sonar.db.qualityprofile.ActiveRuleDto; import org.sonar.db.qualityprofile.ActiveRuleParamDto; import org.sonar.db.qualityprofile.OrgActiveRuleDto; import org.sonar.db.rule.RuleDefinitionDto; +import org.sonar.db.rule.RuleDescriptionSectionDto; import org.sonar.db.rule.RuleDto; import org.sonar.db.rule.RuleParamDto; import org.sonar.server.rule.index.RuleIndexer; @@ -53,6 +54,7 @@ import static com.google.common.base.Strings.isNullOrEmpty; import static com.google.common.collect.FluentIterable.from; import static com.google.common.collect.Lists.newArrayList; import static org.apache.commons.lang.StringUtils.isBlank; +import static org.sonar.db.rule.RuleDescriptionSectionDto.createDefaultRuleDescriptionSection; @ServerSide public class RuleUpdater { @@ -136,8 +138,9 @@ public class RuleUpdater { if (isNullOrEmpty(description)) { throw new IllegalArgumentException("The description is missing"); } - rule.setDescription(description); + RuleDescriptionSectionDto descriptionSectionDto = createDefaultRuleDescriptionSection(description); rule.setDescriptionFormat(RuleDto.Format.MARKDOWN); + rule.addOrReplaceRuleDescriptionSectionDto(descriptionSectionDto); } private static void updateSeverity(RuleUpdate update, RuleDto rule) { diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/rule/ws/RuleMapper.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/rule/ws/RuleMapper.java index 867e323cabd..4b4afd81a9a 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/rule/ws/RuleMapper.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/rule/ws/RuleMapper.java @@ -34,6 +34,7 @@ import org.sonar.api.server.debt.DebtRemediationFunction; import org.sonar.api.server.debt.internal.DefaultDebtRemediationFunction; import org.sonar.db.rule.DeprecatedRuleKeyDto; import org.sonar.db.rule.RuleDefinitionDto; +import org.sonar.db.rule.RuleDescriptionSectionDto; import org.sonar.db.rule.RuleDto; import org.sonar.db.rule.RuleDto.Scope; import org.sonar.db.rule.RuleMetadataDto; @@ -47,6 +48,7 @@ import org.sonarqube.ws.Common; import org.sonarqube.ws.Common.RuleScope; import org.sonarqube.ws.Rules; +import static java.util.stream.Collectors.joining; import static org.sonar.api.utils.DateUtils.formatDateTime; import static org.sonar.core.util.stream.MoreCollectors.toList; import static org.sonar.server.rule.ws.RulesWsParameters.FIELD_CREATED_AT; @@ -329,12 +331,19 @@ public class RuleMapper { } } - String description = ruleDto.getDescription(); - if (shouldReturnField(fieldsToReturn, FIELD_MARKDOWN_DESCRIPTION) && description != null) { + if (shouldReturnField(fieldsToReturn, FIELD_MARKDOWN_DESCRIPTION) + && !ruleDto.getRuleDescriptionSectionDtos().isEmpty()) { + String description = concatenateSectionTemporaryForSonar16302(ruleDto); ruleResponse.setMdDesc(description); } } + private static String concatenateSectionTemporaryForSonar16302(RuleDefinitionDto ruleDto) { + return ruleDto.getRuleDescriptionSectionDtos().stream() + .map(RuleDescriptionSectionDto::getDescription) + .collect(joining()); + } + private void setNotesFields(Rules.Rule.Builder ruleResponse, RuleMetadataDto ruleDto, Map usersByUuid, Set fieldsToReturn) { String noteData = ruleDto.getNoteData(); if (shouldReturnField(fieldsToReturn, "htmlNote") && noteData != null) { diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/hotspot/ws/ShowActionTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/hotspot/ws/ShowActionTest.java index f571e58bbd9..fd9c031e807 100644 --- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/hotspot/ws/ShowActionTest.java +++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/hotspot/ws/ShowActionTest.java @@ -30,6 +30,7 @@ import java.util.List; import java.util.Random; import java.util.Set; import java.util.function.Consumer; +import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.IntStream; import java.util.stream.Stream; @@ -89,6 +90,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.sonar.api.rules.RuleType.SECURITY_HOTSPOT; import static org.sonar.db.component.ComponentTesting.newFileDto; +import static org.sonar.db.rule.RuleDescriptionSectionDto.createDefaultRuleDescriptionSection; import static org.sonar.db.rule.RuleDto.Format.MARKDOWN; @RunWith(DataProviderRunner.class) @@ -431,9 +433,9 @@ public class ShowActionTest { String description = "== Title\n
line1\nline2
"; - RuleDefinitionDto rule = newRule(SECURITY_HOTSPOT, + RuleDefinitionDto rule = newRuleWithoutSection(SECURITY_HOTSPOT, r -> r.setTemplateUuid("123") - .setDescription(description) + .addRuleDescriptionSectionDto(createDefaultRuleDescriptionSection(description)) .setDescriptionFormat(MARKDOWN)); IssueDto hotspot = dbTester.issues().insertHotspot(rule, project, file); @@ -452,7 +454,7 @@ public class ShowActionTest { userSessionRule.logIn().addProjectPermission(UserRole.USER, project); ComponentDto file = dbTester.components().insertComponent(newFileDto(project)); - RuleDefinitionDto rule = newRule(SECURITY_HOTSPOT, r -> r.setTemplateUuid("123").setDescription(null)); + RuleDefinitionDto rule = newRule(SECURITY_HOTSPOT, r -> r.setTemplateUuid("123")); IssueDto hotspot = dbTester.issues().insertHotspot(rule, project, file); mockChangelogAndCommentsFormattingContext(); @@ -1024,11 +1026,18 @@ public class ShowActionTest { } private RuleDefinitionDto newRule(RuleType ruleType, Consumer populate) { - RuleDefinitionDto ruleDefinition = RuleTesting.newRule() - .setType(ruleType); - populate.accept(ruleDefinition); - dbTester.rules().insert(ruleDefinition); - return ruleDefinition; + return newRule(ruleType, RuleTesting::newRule, populate); + } + + private RuleDefinitionDto newRuleWithoutSection(RuleType ruleType, Consumer populate) { + return newRule(ruleType, RuleTesting::newRuleWithoutSection, populate); + } + + private RuleDefinitionDto newRule(RuleType ruleType, Supplier ruleDefinitionDtoSupplier, Consumer populate) { + RuleDefinitionDto ruleDefinitionDto = ruleDefinitionDtoSupplier.get().setType(ruleType); + populate.accept(ruleDefinitionDto); + dbTester.rules().insert(ruleDefinitionDto); + return ruleDefinitionDto; } private static class IssueDtoSetArgumentMatcher implements ArgumentMatcher> { diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/issue/ws/SearchActionTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/issue/ws/SearchActionTest.java index c1c4ba82695..cfc87dc6e5f 100644 --- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/issue/ws/SearchActionTest.java +++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/issue/ws/SearchActionTest.java @@ -100,6 +100,7 @@ import static org.sonar.api.web.UserRole.ISSUE_ADMIN; import static org.sonar.db.component.ComponentDto.PULL_REQUEST_SEPARATOR; import static org.sonar.db.component.ComponentTesting.newFileDto; import static org.sonar.db.issue.IssueTesting.newDto; +import static org.sonar.db.rule.RuleDescriptionSectionDto.createDefaultRuleDescriptionSection; import static org.sonar.server.tester.UserSessionRule.standalone; import static org.sonarqube.ws.Common.RuleType.BUG; import static org.sonarqube.ws.Common.RuleType.SECURITY_HOTSPOT_VALUE; @@ -1441,7 +1442,7 @@ public class SearchActionTest { private RuleDto newIssueRule() { RuleDto rule = RuleTesting.newXooX1() .setName("Rule name") - .setDescription("Rule desc") + .addOrReplaceRuleDescriptionSectionDto(createDefaultRuleDescriptionSection("Rule desc")) .setStatus(RuleStatus.READY); db.rules().insert(rule.getDefinition()); return rule; @@ -1450,7 +1451,7 @@ public class SearchActionTest { private RuleDto newHotspotRule() { RuleDto rule = RuleTesting.newXooX2() .setName("Rule name") - .setDescription("Rule desc") + .addOrReplaceRuleDescriptionSectionDto(createDefaultRuleDescriptionSection("Rule desc")) .setStatus(RuleStatus.READY) .setType(SECURITY_HOTSPOT_VALUE); db.rules().insert(rule.getDefinition()); diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/QProfileBackuperImplTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/QProfileBackuperImplTest.java index 800c5623d59..a4faa701fa1 100644 --- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/QProfileBackuperImplTest.java +++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/QProfileBackuperImplTest.java @@ -54,6 +54,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyList; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import static org.sonar.db.rule.RuleDescriptionSectionDto.createDefaultRuleDescriptionSection; public class QProfileBackuperImplTest { @@ -141,7 +142,7 @@ public class QProfileBackuperImplTest { RuleDefinitionDto templateRule = db.rules().insert(ruleDefinitionDto -> ruleDefinitionDto .setIsTemplate(true)); RuleDefinitionDto rule = db.rules().insert(ruleDefinitionDto -> ruleDefinitionDto - .setDescription("custom rule description") + .addOrReplaceRuleDescriptionSectionDto(createDefaultRuleDescriptionSection("custom rule description")) .setName("custom rule name") .setStatus(RuleStatus.READY) .setTemplateUuid(templateRule.getUuid())); @@ -163,7 +164,7 @@ public class QProfileBackuperImplTest { "" + activeRule.getSeverityString() + "" + "" + rule.getName() + "" + "" + templateRule.getKey().rule() + "" + - "" + rule.getDescription() + "" + + "" + rule.getDefaultRuleDescriptionSectionDto().getDescription() + "" + "" + "" + param.getName() + "" + "20" + diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/rule/RuleCreatorTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/rule/RuleCreatorTest.java index dd98bd59223..63627fd5556 100644 --- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/rule/RuleCreatorTest.java +++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/rule/RuleCreatorTest.java @@ -55,6 +55,7 @@ import org.sonar.server.rule.index.RuleQuery; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.Assert.fail; +import static org.sonar.db.rule.RuleDescriptionSectionDto.createDefaultRuleDescriptionSection; import static org.sonar.db.rule.RuleTesting.newRule; import static org.sonar.server.util.TypeValidationsTesting.newFullTypeValidations; @@ -95,7 +96,7 @@ public class RuleCreatorTest { assertThat(rule.getPluginKey()).isEqualTo("sonarjava"); assertThat(rule.getTemplateUuid()).isEqualTo(templateRule.getUuid()); assertThat(rule.getName()).isEqualTo("My custom"); - assertThat(rule.getDescription()).isEqualTo("Some description"); + assertThat(rule.getDefaultRuleDescriptionSection().getDescription()).isEqualTo("Some description"); assertThat(rule.getSeverityString()).isEqualTo("MAJOR"); assertThat(rule.getStatus()).isEqualTo(RuleStatus.READY); assertThat(rule.getLanguage()).isEqualTo("java"); @@ -288,7 +289,7 @@ public class RuleCreatorTest { .setRuleKey(key) .setStatus(RuleStatus.REMOVED) .setName("Old name") - .setDescription("Old description") + .addOrReplaceRuleDescriptionSectionDto(createDefaultRuleDescriptionSection("Old description")) .setDescriptionFormat(Format.MARKDOWN) .setSeverity(Severity.INFO); dbTester.rules().insert(rule.getDefinition()); @@ -310,7 +311,7 @@ public class RuleCreatorTest { // These values should be the same than before assertThat(result.getName()).isEqualTo("Old name"); - assertThat(result.getDescription()).isEqualTo("Old description"); + assertThat(result.getDefaultRuleDescriptionSectionDto().getDescription()).isEqualTo("Old description"); assertThat(result.getSeverityString()).isEqualTo(Severity.INFO); List params = dbTester.getDbClient().ruleDao().selectRuleParamsByRuleKey(dbSession, customRuleKey); @@ -328,7 +329,7 @@ public class RuleCreatorTest { .setRuleKey(key) .setStatus(RuleStatus.REMOVED) .setName("Old name") - .setDescription("Old description") + .addOrReplaceRuleDescriptionSectionDto(createDefaultRuleDescriptionSection("Old description")) .setSeverity(Severity.INFO); dbTester.rules().insert(rule.getDefinition()); dbTester.rules().insertRuleParam(rule.getDefinition(), param -> param.setDefaultValue("a.*")); diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/rule/RuleUpdaterTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/rule/RuleUpdaterTest.java index 5670c5d1489..0c2fbae7dc7 100644 --- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/rule/RuleUpdaterTest.java +++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/rule/RuleUpdaterTest.java @@ -57,6 +57,7 @@ import org.sonar.server.tester.UserSessionRule; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.sonar.api.rule.Severity.CRITICAL; +import static org.sonar.db.rule.RuleDescriptionSectionDto.createDefaultRuleDescriptionSection; import static org.sonar.db.rule.RuleTesting.newRule; import static org.sonar.server.rule.RuleUpdate.createForCustomRule; import static org.sonar.server.rule.RuleUpdate.createForPluginRule; @@ -341,7 +342,7 @@ public class RuleUpdaterTest { // Create custom rule RuleDefinitionDto customRule = RuleTesting.newCustomRule(templateRule) .setName("Old name") - .setDescription("Old description") + .addOrReplaceRuleDescriptionSectionDto(createDefaultRuleDescriptionSection("Old description")) .setSeverity(Severity.MINOR) .setStatus(RuleStatus.BETA) .getDefinition(); @@ -364,7 +365,7 @@ public class RuleUpdaterTest { RuleDto customRuleReloaded = db.getDbClient().ruleDao().selectOrFailByKey(dbSession, customRule.getKey()); assertThat(customRuleReloaded).isNotNull(); assertThat(customRuleReloaded.getName()).isEqualTo("New name"); - assertThat(customRuleReloaded.getDescription()).isEqualTo("New description"); + assertThat(customRuleReloaded.getDefaultRuleDescriptionSection().getDescription()).isEqualTo("New description"); assertThat(customRuleReloaded.getSeverityString()).isEqualTo("MAJOR"); assertThat(customRuleReloaded.getStatus()).isEqualTo(RuleStatus.READY); @@ -389,7 +390,7 @@ public class RuleUpdaterTest { // Create custom rule RuleDefinitionDto customRule = RuleTesting.newCustomRule(templateRule) .setName("Old name") - .setDescription("Old description") + .addRuleDescriptionSectionDto(createDefaultRuleDescriptionSection("Old description")) .setSeverity(Severity.MINOR) .setStatus(RuleStatus.BETA) .getDefinition(); diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/rule/ws/CreateActionTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/rule/ws/CreateActionTest.java index f81c42ce1d1..dca7895a189 100644 --- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/rule/ws/CreateActionTest.java +++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/rule/ws/CreateActionTest.java @@ -51,6 +51,7 @@ import static org.mockito.Mockito.mock; import static org.sonar.api.rules.RuleType.BUG; import static org.sonar.api.rules.RuleType.CODE_SMELL; import static org.sonar.db.permission.GlobalPermission.ADMINISTER_QUALITY_PROFILES; +import static org.sonar.db.rule.RuleDescriptionSectionDto.createDefaultRuleDescriptionSection; import static org.sonar.db.rule.RuleTesting.newCustomRule; import static org.sonar.db.rule.RuleTesting.newTemplateRule; import static org.sonar.server.util.TypeValidationsTesting.newFullTypeValidations; @@ -141,7 +142,7 @@ public class CreateActionTest { .setRuleKey("MY_CUSTOM") .setStatus(RuleStatus.REMOVED) .setName("My custom rule") - .setDescription("Description") + .addOrReplaceRuleDescriptionSectionDto(createDefaultRuleDescriptionSection("Description")) .setDescriptionFormat(RuleDto.Format.MARKDOWN) .setSeverity(Severity.MAJOR); db.rules().insert(customRule); diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/rule/ws/SearchActionTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/rule/ws/SearchActionTest.java index 81e08380c3d..3b6db659ba2 100644 --- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/rule/ws/SearchActionTest.java +++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/rule/ws/SearchActionTest.java @@ -79,6 +79,7 @@ import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.sonar.api.rule.Severity.BLOCKER; +import static org.sonar.db.rule.RuleDescriptionSectionDto.createDefaultRuleDescriptionSection; import static org.sonar.db.rule.RuleTesting.setSystemTags; import static org.sonar.db.rule.RuleTesting.setTags; import static org.sonar.server.rule.ws.RulesWsParameters.PARAM_ACTIVATION; @@ -232,7 +233,8 @@ public class SearchActionTest { @Test public void filter_by_rule_description() { - RuleDefinitionDto rule1 = db.rules().insert(r1 -> r1.setDescription("This is the best rule now&forever")); + RuleDefinitionDto rule1 = db.rules() + .insert(r1 -> r1.addOrReplaceRuleDescriptionSectionDto(createDefaultRuleDescriptionSection("This is the best rule now&forever"))); RuleDefinitionDto rule2 = db.rules().insert(r1 -> r1.setName("Some other stuff")); indexRules(); @@ -243,8 +245,10 @@ public class SearchActionTest { @Test public void filter_by_rule_name_or_descriptions_requires_all_words_to_match_anywhere() { - RuleDefinitionDto rule1 = db.rules().insert(r1 -> r1.setName("Best rule ever").setDescription("This is a good rule")); - RuleDefinitionDto rule2 = db.rules().insert(r1 -> r1.setName("Some other stuff").setDescription("Another thing")); + RuleDefinitionDto rule1 = db.rules().insert(r1 -> r1.setName("Best rule ever") + .addOrReplaceRuleDescriptionSectionDto(createDefaultRuleDescriptionSection("This is a good rule"))); + RuleDefinitionDto rule2 = db.rules().insert(r1 -> r1.setName("Another thing") + .addOrReplaceRuleDescriptionSectionDto(createDefaultRuleDescriptionSection("Another thing"))); indexRules(); verify(r -> r.setParam("q", "Best good"), rule1); diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/rule/ws/ShowActionTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/rule/ws/ShowActionTest.java index 6a0400dd5d3..41ba8fab653 100644 --- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/rule/ws/ShowActionTest.java +++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/rule/ws/ShowActionTest.java @@ -50,6 +50,7 @@ import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; +import static org.sonar.db.rule.RuleDescriptionSectionDto.createDefaultRuleDescriptionSection; import static org.sonar.db.rule.RuleDto.Format.MARKDOWN; import static org.sonar.db.rule.RuleTesting.newCustomRule; import static org.sonar.db.rule.RuleTesting.newTemplateRule; @@ -284,7 +285,7 @@ public class ShowActionTest { db.rules().insert(templateRule.getDefinition()); // Custom rule RuleDefinitionDto customRule = newCustomRule(templateRule.getDefinition()) - .setDescription("
line1\nline2
") + .addOrReplaceRuleDescriptionSectionDto(createDefaultRuleDescriptionSection("
line1\nline2
")) .setDescriptionFormat(MARKDOWN); db.rules().insert(customRule); doReturn("<div>line1
line2</div>").when(macroInterpreter).interpret("
line1\nline2
"); @@ -342,7 +343,7 @@ public class ShowActionTest { .setIsExternal(true) .setIsAdHoc(true) .setName("predefined name") - .setDescription("
predefined desc
") + .addOrReplaceRuleDescriptionSectionDto(createDefaultRuleDescriptionSection("
predefined desc
")) .setSeverity(Severity.BLOCKER) .setType(RuleType.VULNERABILITY)); RuleMetadataDto metadata = db.rules().insertOrUpdateMetadata(externalRule, m -> m @@ -370,7 +371,6 @@ public class ShowActionTest { .setIsExternal(true) .setIsAdHoc(true) .setName(null) - .setDescription(null) .setDescriptionFormat(null) .setSeverity((String) null) .setType(0)); diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/rule/ws/UpdateActionTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/rule/ws/UpdateActionTest.java index 60f00c186e0..8be2f583b11 100644 --- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/rule/ws/UpdateActionTest.java +++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/rule/ws/UpdateActionTest.java @@ -53,6 +53,7 @@ import static org.mockito.Mockito.mock; import static org.sonar.api.server.debt.DebtRemediationFunction.Type.LINEAR; import static org.sonar.api.server.debt.DebtRemediationFunction.Type.LINEAR_OFFSET; import static org.sonar.db.permission.GlobalPermission.ADMINISTER_QUALITY_PROFILES; +import static org.sonar.db.rule.RuleDescriptionSectionDto.createDefaultRuleDescriptionSection; import static org.sonar.db.rule.RuleTesting.setSystemTags; import static org.sonar.db.rule.RuleTesting.setTags; import static org.sonar.server.rule.ws.UpdateAction.PARAM_KEY; @@ -107,7 +108,7 @@ public class UpdateActionTest { RuleDefinitionDto customRule = db.rules().insert( r -> r.setRuleKey(RuleKey.of("java", "MY_CUSTOM")), r -> r.setName("Old custom"), - r -> r.setDescription("Old description"), + r -> r.addOrReplaceRuleDescriptionSectionDto(createDefaultRuleDescriptionSection("Old description")), r -> r.setSeverity(Severity.MINOR), r -> r.setStatus(RuleStatus.BETA), r -> r.setTemplateUuid(templateRule.getUuid()), @@ -243,7 +244,7 @@ public class UpdateActionTest { RuleDefinitionDto customRule = db.rules().insert( r -> r.setRuleKey(RuleKey.of("java", "MY_CUSTOM")), r -> r.setName("Old custom"), - r -> r.setDescription("Old description"), + r -> r.addOrReplaceRuleDescriptionSectionDto(createDefaultRuleDescriptionSection("Old description")), r -> r.setTemplateUuid(templateRule.getUuid()), r -> r.setCreatedAt(PAST), r -> r.setUpdatedAt(PAST)); -- 2.39.5