]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-16302 persist and fetch rules with new DB structure
authorAurelien Poscia <aurelien.poscia@sonarsource.com>
Sat, 23 Apr 2022 12:21:23 +0000 (14:21 +0200)
committersonartech <sonartech@sonarsource.com>
Fri, 6 May 2022 20:02:43 +0000 (20:02 +0000)
31 files changed:
server/sonar-db-core/src/main/java/org/sonar/db/version/SqTables.java
server/sonar-db-dao/src/main/java/org/sonar/db/MyBatis.java
server/sonar-db-dao/src/main/java/org/sonar/db/rule/RuleDao.java
server/sonar-db-dao/src/main/java/org/sonar/db/rule/RuleDefinitionDto.java
server/sonar-db-dao/src/main/java/org/sonar/db/rule/RuleDescriptionSectionDto.java
server/sonar-db-dao/src/main/java/org/sonar/db/rule/RuleDto.java
server/sonar-db-dao/src/main/java/org/sonar/db/rule/RuleMapper.java
server/sonar-db-dao/src/main/resources/org/sonar/db/rule/RuleMapper.xml
server/sonar-db-dao/src/test/java/org/sonar/db/rule/RuleDaoTest.java
server/sonar-db-dao/src/testFixtures/java/org/sonar/db/rule/RuleTesting.java
server/sonar-server-common/src/main/java/org/sonar/server/rule/DefaultRuleFinder.java
server/sonar-server-common/src/test/java/org/sonar/server/rule/HotspotRuleDescriptionTest.java
server/sonar-server-common/src/test/java/org/sonar/server/rule/RuleDescriptionFormatterTest.java
server/sonar-server-common/src/test/java/org/sonar/server/rule/index/RuleIndexTest.java
server/sonar-server-common/src/test/java/org/sonar/server/rule/index/RuleIndexerTest.java
server/sonar-webserver-api/src/test/java/org/sonar/server/rule/CachingRuleFinderTest.java
server/sonar-webserver-core/src/main/java/org/sonar/server/rule/RegisterRules.java
server/sonar-webserver-core/src/test/java/org/sonar/server/rule/RegisterRulesTest.java
server/sonar-webserver-pushapi/src/test/java/org/sonar/server/qualityprofile/builtin/QualityProfileChangeEventServiceImplTest.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/rule/RuleCreator.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/rule/RuleUpdater.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/rule/ws/ListAction.java
server/sonar-webserver-webapi/src/test/java/org/sonar/server/hotspot/ws/ShowActionTest.java
server/sonar-webserver-webapi/src/test/java/org/sonar/server/issue/ws/SearchActionTest.java
server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/QProfileBackuperImplTest.java
server/sonar-webserver-webapi/src/test/java/org/sonar/server/rule/RuleCreatorTest.java
server/sonar-webserver-webapi/src/test/java/org/sonar/server/rule/RuleUpdaterTest.java
server/sonar-webserver-webapi/src/test/java/org/sonar/server/rule/ws/CreateActionTest.java
server/sonar-webserver-webapi/src/test/java/org/sonar/server/rule/ws/SearchActionTest.java
server/sonar-webserver-webapi/src/test/java/org/sonar/server/rule/ws/ShowActionTest.java
server/sonar-webserver-webapi/src/test/java/org/sonar/server/rule/ws/UpdateActionTest.java

index 0efe8159a86db54c50b06954cfa983e2188f5ecc..b46f6a4a1d98a10b468b1d830d2d6f9eca69614f 100644 (file)
@@ -90,6 +90,7 @@ public final class SqTables {
     "quality_gate_conditions",
     "saml_message_ids",
     "rules",
+    "rule_desc_sections",
     "rules_metadata",
     "rules_parameters",
     "rules_profiles",
index 99875cdcbd7e673d5e17e9934c776c3cbbe6127f..462ea745804ef72eb32d2b59056236af6be09a8f 100644 (file)
@@ -131,7 +131,6 @@ import org.sonar.db.qualityprofile.QProfileEditGroupsMapper;
 import org.sonar.db.qualityprofile.QProfileEditUsersMapper;
 import org.sonar.db.qualityprofile.QualityProfileExportMapper;
 import org.sonar.db.qualityprofile.QualityProfileMapper;
-import org.sonar.db.rule.RuleDto;
 import org.sonar.db.rule.RuleMapper;
 import org.sonar.db.rule.RuleParamDto;
 import org.sonar.db.rule.RuleRepositoryMapper;
@@ -221,7 +220,6 @@ public class MyBatis {
     confBuilder.loadAlias("QualityGate", QualityGateDto.class);
     confBuilder.loadAlias("Resource", ResourceDto.class);
     confBuilder.loadAlias("RuleParam", RuleParamDto.class);
-    confBuilder.loadAlias("Rule", RuleDto.class);
     confBuilder.loadAlias("SchemaMigration", SchemaMigrationDto.class);
     confBuilder.loadAlias("ScrapProperty", ScrapPropertyDto.class);
     confBuilder.loadAlias("ScrapAnalysisProperty", ScrapAnalysisPropertyDto.class);
index 20b7c6edd46e4d19b034bc4bcbc33c4f98fde4f5..95e81b68c4a0e181cdfa0df8edb1a3171a026c09 100644 (file)
@@ -24,7 +24,6 @@ import java.util.List;
 import java.util.Optional;
 import java.util.Set;
 import java.util.function.Consumer;
-import org.apache.ibatis.session.ResultHandler;
 import org.sonar.api.rule.RuleKey;
 import org.sonar.api.rules.RuleQuery;
 import org.sonar.core.util.UuidFactory;
@@ -34,7 +33,6 @@ import org.sonar.db.RowNotFoundException;
 
 import static com.google.common.base.Preconditions.checkNotNull;
 import static java.util.Collections.emptyList;
-import static java.util.Optional.ofNullable;
 import static org.sonar.db.DatabaseUtils.executeLargeInputs;
 import static org.sonar.db.DatabaseUtils.executeLargeInputsWithoutOutput;
 import static org.sonar.db.DatabaseUtils.executeLargeUpdates;
@@ -48,16 +46,15 @@ public class RuleDao implements Dao {
   }
 
   public Optional<RuleDto> selectByKey(DbSession session, RuleKey key) {
-    RuleDto res = mapper(session).selectByKey(key);
-    return ofNullable(res);
+    return Optional.ofNullable(mapper(session).selectByKey(key));
   }
 
   public Optional<RuleDefinitionDto> selectDefinitionByKey(DbSession session, RuleKey key) {
-    return ofNullable(mapper(session).selectDefinitionByKey(key));
+    return Optional.ofNullable(mapper(session).selectDefinitionByKey(key));
   }
 
   public Optional<RuleMetadataDto> selectMetadataByKey(DbSession session, RuleKey key) {
-    return ofNullable(mapper(session).selectMetadataByKey(key));
+    return Optional.ofNullable(mapper(session).selectMetadataByKey(key));
   }
 
   public List<RuleMetadataDto> selectMetadataByKeys(DbSession session, Collection<RuleKey> keys) {
@@ -68,28 +65,21 @@ public class RuleDao implements Dao {
   }
 
   public RuleDto selectOrFailByKey(DbSession session, RuleKey key) {
-    RuleDto rule = mapper(session).selectByKey(key);
-    if (rule == null) {
-      throw new RowNotFoundException(String.format("Rule with key '%s' does not exist", key));
-    }
-    return rule;
+    return Optional.ofNullable(mapper(session).selectByKey(key))
+      .orElseThrow(() -> new RowNotFoundException(String.format("Rule with key '%s' does not exist", key)));
   }
 
   public RuleDefinitionDto selectOrFailDefinitionByKey(DbSession session, RuleKey key) {
-    RuleDefinitionDto rule = mapper(session).selectDefinitionByKey(key);
-    if (rule == null) {
-      throw new RowNotFoundException(String.format("Rule with key '%s' does not exist", key));
-    }
-    return rule;
+    return Optional.ofNullable(mapper(session).selectDefinitionByKey(key))
+      .orElseThrow(() -> new RowNotFoundException(String.format("Rule with key '%s' does not exist", key)));
   }
 
   public Optional<RuleDto> selectByUuid(String uuid, DbSession session) {
-    RuleDto res = mapper(session).selectByUuid(uuid);
-    return ofNullable(res);
+    return Optional.ofNullable(mapper(session).selectByUuid(uuid));
   }
 
   public Optional<RuleDefinitionDto> selectDefinitionByUuid(String uuid, DbSession session) {
-    return ofNullable(mapper(session).selectDefinitionByUuid(uuid));
+    return Optional.ofNullable(mapper(session).selectDefinitionByUuid(uuid));
   }
 
   public List<RuleDto> selectByUuids(DbSession session, List<String> uuids) {
@@ -120,8 +110,8 @@ public class RuleDao implements Dao {
     return executeLargeInputs(keys, mapper(session)::selectDefinitionByKeys);
   }
 
-  public void selectEnabled(DbSession session, ResultHandler<RuleDefinitionDto> resultHandler) {
-    mapper(session).selectEnabled(resultHandler);
+  public List<RuleDefinitionDto> selectEnabled(DbSession session) {
+    return mapper(session).selectEnabled();
   }
 
   public List<RuleDto> selectAll(DbSession session) {
@@ -140,9 +130,11 @@ public class RuleDao implements Dao {
     return mapper(session).selectByQuery(ruleQuery);
   }
 
-  public void insert(DbSession session, RuleDefinitionDto dto) {
-    checkNotNull(dto.getUuid(), "RuleDefinitionDto has no 'uuid'.");
-    mapper(session).insertDefinition(dto);
+  public void insert(DbSession session, RuleDefinitionDto ruleDefinitionDto) {
+    checkNotNull(ruleDefinitionDto.getUuid(), "RuleDefinitionDto has no 'uuid'.");
+    RuleMapper mapper = mapper(session);
+    mapper.insertDefinition(ruleDefinitionDto);
+    insertRuleDescriptionSectionDtos(ruleDefinitionDto, mapper);
   }
 
   public void insert(DbSession session, RuleMetadataDto dto) {
@@ -150,8 +142,20 @@ public class RuleDao implements Dao {
     mapper(session).insertMetadata(dto);
   }
 
-  public void update(DbSession session, RuleDefinitionDto dto) {
-    mapper(session).updateDefinition(dto);
+  public void update(DbSession session, RuleDefinitionDto ruleDefinitionDto) {
+    RuleMapper mapper = mapper(session);
+    mapper.updateDefinition(ruleDefinitionDto);
+    updateRuleDescriptionDtos(ruleDefinitionDto, mapper);
+  }
+
+  private void updateRuleDescriptionDtos(RuleDefinitionDto ruleDefinitionDto, RuleMapper mapper) {
+    mapper.deleteRuleDescriptionSection(ruleDefinitionDto.getUuid());
+    insertRuleDescriptionSectionDtos(ruleDefinitionDto, mapper);
+  }
+
+  private void insertRuleDescriptionSectionDtos(RuleDefinitionDto ruleDefinitionDto, RuleMapper mapper) {
+    ruleDefinitionDto.getRuleDescriptionSectionDtos()
+      .forEach(section -> mapper.insertRuleDescriptionSection(ruleDefinitionDto.getUuid(), section));
   }
 
   public void insertOrUpdate(DbSession session, RuleMetadataDto dto) {
index 1cd974855ff125a934663c28de0d3d2caf814507..2347e80f8f56cb25f4c08ae46f8aadb39363cbf9 100644 (file)
@@ -21,10 +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.HashSet;
 import java.util.Objects;
+import java.util.Optional;
 import java.util.Set;
 import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
@@ -36,7 +35,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;
+import static org.sonar.db.rule.RuleDescriptionSectionDto.DEFAULT_KEY;
 
 public class RuleDefinitionDto {
 
@@ -46,7 +45,7 @@ public class RuleDefinitionDto {
   private String repositoryKey;
   private String ruleKey;
 
-  private Map<String, RuleDescriptionSectionDto> ruleDescriptionSectionDtos = new HashMap<>();
+  private Set<RuleDescriptionSectionDto> ruleDescriptionSectionDtos = new HashSet<>();
 
   /**
    * Description format can be null on external rule, otherwise it should never be null
@@ -167,35 +166,38 @@ public class RuleDefinitionDto {
     return this;
   }
 
-  public Collection<RuleDescriptionSectionDto> getRuleDescriptionSectionDtos() {
-    return unmodifiableCollection(ruleDescriptionSectionDtos.values());
-  }
-
   @CheckForNull
   public RuleDescriptionSectionDto getRuleDescriptionSectionDto(String ruleDescriptionSectionKey) {
-    return ruleDescriptionSectionDtos.get(ruleDescriptionSectionKey);
+    return findExistingSectionWithSameKey(ruleDescriptionSectionKey).orElse(null);
   }
 
   @CheckForNull
   public RuleDescriptionSectionDto getDefaultRuleDescriptionSectionDto() {
-    return ruleDescriptionSectionDtos.get(RuleDescriptionSectionDto.DEFAULT_KEY);
+    return findExistingSectionWithSameKey(DEFAULT_KEY).orElse(null);
   }
 
   public RuleDefinitionDto addRuleDescriptionSectionDto(RuleDescriptionSectionDto ruleDescriptionSectionDto) {
-    checkArgument(!isSectionKeyUsed(ruleDescriptionSectionDto.getKey()), "A section with key %s already exists", ruleDescriptionSectionDto.getKey());
-    this.ruleDescriptionSectionDtos.put(ruleDescriptionSectionDto.getKey(), ruleDescriptionSectionDto);
+    checkArgument(sectionWithSameKeyShouldNotExist(ruleDescriptionSectionDto),
+      "A section with key %s already exists", ruleDescriptionSectionDto.getKey());
+    ruleDescriptionSectionDtos.add(ruleDescriptionSectionDto);
     return this;
   }
 
-  private boolean isSectionKeyUsed(String sectionKey) {
-    return ruleDescriptionSectionDtos.containsKey(sectionKey);
+  private boolean sectionWithSameKeyShouldNotExist(RuleDescriptionSectionDto ruleDescriptionSectionDto) {
+    return findExistingSectionWithSameKey(ruleDescriptionSectionDto.getKey()).isEmpty();
   }
 
   public RuleDefinitionDto addOrReplaceRuleDescriptionSectionDto(RuleDescriptionSectionDto ruleDescriptionSectionDto) {
-    this.ruleDescriptionSectionDtos.put(ruleDescriptionSectionDto.getKey(), ruleDescriptionSectionDto);
+    Optional<RuleDescriptionSectionDto> existingSectionWithSameKey = findExistingSectionWithSameKey(ruleDescriptionSectionDto.getKey());
+    existingSectionWithSameKey.ifPresent(ruleDescriptionSectionDtos::remove);
+    ruleDescriptionSectionDtos.add(ruleDescriptionSectionDto);
     return this;
   }
 
+  private Optional<RuleDescriptionSectionDto> findExistingSectionWithSameKey(String ruleDescriptionSectionKey) {
+    return ruleDescriptionSectionDtos.stream().filter(section -> section.getKey().equals(ruleDescriptionSectionKey)).findAny();
+  }
+
   @CheckForNull
   public RuleDto.Format getDescriptionFormat() {
     return descriptionFormat;
@@ -429,6 +431,14 @@ public class RuleDefinitionDto {
     return this;
   }
 
+  public Set<RuleDescriptionSectionDto> getRuleDescriptionSectionDtos() {
+    return ruleDescriptionSectionDtos;
+  }
+
+  void setRuleDescriptionSectionDtos(Set<RuleDescriptionSectionDto> ruleDescriptionSectionDtos) {
+    this.ruleDescriptionSectionDtos = ruleDescriptionSectionDtos;
+  }
+
   @Override
   public boolean equals(Object obj) {
     if (!(obj instanceof RuleDefinitionDto)) {
@@ -475,4 +485,5 @@ public class RuleDefinitionDto {
       ", scope=" + scope +
       '}';
   }
+
 }
index bff3a3d30d1b8a4be77fa88b84379dca2862eec6..76dc0684d9e61ee8cf8d3ed351778cb141adcb6d 100644 (file)
@@ -25,14 +25,21 @@ import static org.sonar.api.utils.Preconditions.checkArgument;
 
 public class RuleDescriptionSectionDto {
   static final String DEFAULT_KEY = "default";
+
+  private final String uuid;
   private final String key;
   private final String description;
 
-  private RuleDescriptionSectionDto(String key, String description) {
+  private RuleDescriptionSectionDto(String uuid, String key, String description) {
+    this.uuid = uuid;
     this.key = key;
     this.description = description;
   }
 
+  public String getUuid() {
+    return uuid;
+  }
+
   public String getKey() {
     return key;
   }
@@ -41,9 +48,10 @@ public class RuleDescriptionSectionDto {
     return description;
   }
 
-  public static RuleDescriptionSectionDto createDefaultRuleDescriptionSection(String description) {
+  public static RuleDescriptionSectionDto createDefaultRuleDescriptionSection(String uuid, String description) {
     return RuleDescriptionSectionDto.builder()
       .setDefault()
+      .uuid(uuid)
       .description(description)
       .build();
   }
@@ -59,18 +67,25 @@ public class RuleDescriptionSectionDto {
   @Override
   public String toString() {
     return new StringJoiner(", ", RuleDescriptionSectionDto.class.getSimpleName() + "[", "]")
+      .add("uuid='" + uuid + "'")
       .add("key='" + key + "'")
       .add("description='" + description + "'")
       .toString();
   }
 
   public static final class RuleDescriptionSectionDtoBuilder {
+    private String uuid;
     private String key = null;
     private String description;
 
     private RuleDescriptionSectionDtoBuilder() {
     }
 
+    public RuleDescriptionSectionDtoBuilder uuid(String uuid) {
+      this.uuid = uuid;
+      return this;
+    }
+
     public RuleDescriptionSectionDtoBuilder setDefault() {
       checkArgument(this.key == null, "Only one of setDefault and key methods can be called");
       this.key = DEFAULT_KEY;
@@ -83,14 +98,13 @@ public class RuleDescriptionSectionDto {
       return this;
     }
 
-
     public RuleDescriptionSectionDtoBuilder description(String description) {
       this.description = description;
       return this;
     }
 
     public RuleDescriptionSectionDto build() {
-      return new RuleDescriptionSectionDto(key, description);
+      return new RuleDescriptionSectionDto(uuid, key, description);
     }
   }
 }
index 3e518801fa71c1318af6d56af161b7ea55f20e98..12f4d4e4625be5ce954807264ba7deab2c49d811 100644 (file)
@@ -125,6 +125,10 @@ public class RuleDto {
     return this;
   }
 
+  void setRuleDescriptionSectionDtos(Set<RuleDescriptionSectionDto> ruleDescriptionSectionDtos) {
+    definition.setRuleDescriptionSectionDtos(ruleDescriptionSectionDtos);
+  }
+
   public Format getDescriptionFormat() {
     return definition.getDescriptionFormat();
   }
@@ -203,7 +207,7 @@ public class RuleDto {
     return definition.isAdHoc();
   }
 
-  public RuleDto setIsAdhoc(boolean isAdHoc) {
+  public RuleDto setIsAdHoc(boolean isAdHoc) {
     definition.setIsAdHoc(isAdHoc);
     return this;
   }
@@ -510,6 +514,8 @@ public class RuleDto {
     return this;
   }
 
+
+
   @Override
   public boolean equals(Object obj) {
     if (!(obj instanceof RuleDto)) {
index 34bb0d063b701a67f6c3bacd69ffa041cf302f0c..74f671107469e7f5d806046e2b59167148a5751a 100644 (file)
@@ -33,7 +33,7 @@ public interface RuleMapper {
 
   List<RuleDefinitionDto> selectAllDefinitions();
 
-  void selectEnabled(ResultHandler<RuleDefinitionDto> resultHandler);
+  List<RuleDefinitionDto> selectEnabled();
 
   RuleDto selectByUuid(@Param("uuid") String uuid);
 
@@ -67,8 +67,12 @@ public interface RuleMapper {
 
   void insertDefinition(RuleDefinitionDto ruleDefinitionDto);
 
+  void insertRuleDescriptionSection(@Param("ruleUuid") String ruleUuid, @Param("dto") RuleDescriptionSectionDto ruleDescriptionSectionDto);
+
   void updateDefinition(RuleDefinitionDto ruleDefinitionDto);
 
+  void deleteRuleDescriptionSection(String ruleUuid);
+
   int countMetadata(RuleMetadataDto ruleMetadataDto);
 
   void insertMetadata(RuleMetadataDto ruleMetadataDto);
index 06b8316d728735ab594cca4f55604cd0c415b15c..0188bf511d8c5d333ae57174f183f7170dd69585 100644 (file)
@@ -3,11 +3,22 @@
 
 <mapper namespace="org.sonar.db.rule.RuleMapper">
 
+  <sql id="selectRuleDescriptionSectionColumns">
+    rds.uuid as "rds_uuid",
+    rds.kee as "rds_kee",
+    rds.description as "rds_description",
+  </sql>
+
+  <sql id="leftOuterJoinRulesDescriptionSections">
+    left outer join rule_desc_sections rds on
+      rds.rule_uuid = r.uuid
+  </sql>
+
   <sql id="selectDefinitionColumns">
-    r.uuid,
+    <include refid="selectRuleDescriptionSectionColumns"/>
+    r.uuid as "r_uuid",
     r.plugin_rule_key as "ruleKey",
     r.plugin_name as "repositoryKey",
-    r.description,
     r.description_format as "descriptionFormat",
     r.status,
     r.name,
       rm.rule_uuid = r.uuid
   </sql>
 
-  <select id="selectAll" resultType="Rule">
+  <select id="selectAll" resultMap="ruleResultMap">
     select
       <include refid="selectJoinedTablesColumns"/>
     from
       rules r
     <include refid="outerJoinRulesMetadata"/>
+    <include refid="leftOuterJoinRulesDescriptionSections"/>
   </select>
 
-  <select id="selectAllDefinitions" resultType="org.sonar.db.rule.RuleDefinitionDto">
+  <resultMap id="ruleResultMap" type="org.sonar.db.rule.RuleDto">
+    <id property="uuid" column="r_uuid"/>
+
+    <result property="createdAtFromDefinition" column="createdAtFromDefinition" />
+    <result property="updatedAtFromDefinition" column="updatedAtFromDefinition" />
+    <result property="noteData" column="noteData" />
+    <result property="noteUserUuid" column="noteUserUuid" />
+    <result property="noteCreatedAt" column="noteCreatedAt" />
+    <result property="noteUpdatedAt" column="noteUpdatedAt" />
+    <result property="remediationFunction" column="remediationFunction" />
+    <result property="remediationGapMultiplier" column="remediationGapMultiplier" />
+    <result property="remediationBaseEffort" column="remediationBaseEffort" />
+    <result property="tagsField" column="tagsField" />
+    <result property="adHocName" column="adHocName" />
+    <result property="adHocDescription" column="adHocDescription" />
+    <result property="adHocSeverity" column="adHocSeverity" />
+    <result property="adHocType" column="adHocType" />
+    <result property="createdAtFromMetadata" column="createdAtFromMetadata" />
+    <result property="updatedAtFromMetadata" column="updatedAtFromMetadata"  />
+
+    <result property="ruleKey" column="ruleKey"/>
+    <result property="repositoryKey" column="repositoryKey"/>
+    <result property="descriptionFormat" column="descriptionFormat"/>
+    <result property="status" column="status"/>
+    <result property="name" column="name"/>
+    <result property="configKey" column="configKey"/>
+    <result property="severity" column="severity"/>
+    <result property="isTemplate" column="isTemplate"/>
+    <result property="isExternal" column="isExternal"/>
+    <result property="isAdHoc" column="isAdHoc"/>
+    <result property="language" column="language"/>
+    <result property="templateUuid" column="templateUuid"/>
+    <result property="defRemediationFunction" column="defRemediationFunction"/>
+    <result property="defRemediationGapMultiplier" column="defRemediationGapMultiplier"/>
+    <result property="defRemediationBaseEffort" column="defRemediationBaseEffort"/>
+    <result property="gapDescription" column="gapDescription"/>
+    <result property="systemTagsField" column="systemTagsField"/>
+    <result property="securityStandardsField" column="securityStandardsField"/>
+    <result property="type" column="type"/>
+    <result property="pluginKey" column="pluginKey"/>
+    <result property="scope" column="scope"/>
+    <result property="createdAt" column="created_at"/>
+    <result property="updatedAt" column="updated_at"/>
+
+    <collection property="ruleDescriptionSectionDtos" ofType="org.sonar.db.rule.RuleDescriptionSectionDto">
+      <id property="uuid" column="rds_uuid"/>
+      <result property="key" column="rds_kee"/>
+      <result property="description" column="rds_description"/>
+    </collection>
+  </resultMap>
+
+  <resultMap id="ruleDefinitionResultMap" type="org.sonar.db.rule.RuleDefinitionDto">
+    <id property="uuid" column="r_uuid"/>
+    <result property="ruleKey" column="ruleKey"/>
+    <result property="repositoryKey" column="repositoryKey"/>
+    <result property="descriptionFormat" column="descriptionFormat"/>
+    <result property="status" column="status"/>
+    <result property="name" column="name"/>
+    <result property="configKey" column="configKey"/>
+    <result property="severity" column="severity"/>
+    <result property="isTemplate" column="isTemplate"/>
+    <result property="isExternal" column="isExternal"/>
+    <result property="isAdHoc" column="isAdHoc"/>
+    <result property="language" column="language"/>
+    <result property="templateUuid" column="templateUuid"/>
+    <result property="defRemediationFunction" column="defRemediationFunction"/>
+    <result property="defRemediationGapMultiplier" column="defRemediationGapMultiplier"/>
+    <result property="defRemediationBaseEffort" column="defRemediationBaseEffort"/>
+    <result property="gapDescription" column="gapDescription"/>
+    <result property="systemTagsField" column="systemTagsField"/>
+    <result property="securityStandardsField" column="securityStandardsField"/>
+    <result property="type" column="type"/>
+    <result property="pluginKey" column="pluginKey"/>
+    <result property="scope" column="scope"/>
+    <result property="createdAt" column="createdAt"/>
+    <result property="updatedAt" column="updatedAt"/>
+
+    <collection property="ruleDescriptionSectionDtos" ofType="org.sonar.db.rule.RuleDescriptionSectionDto">
+      <id property="uuid" column="rds_uuid"/>
+      <result property="key" column="rds_kee"/>
+      <result property="description" column="rds_description"/>
+    </collection>
+
+  </resultMap>
+
+  <select id="selectAllDefinitions" resultMap="ruleDefinitionResultMap">
     select
       <include refid="selectRuleTableColumns"/>
     from
       rules r
+    <include refid="leftOuterJoinRulesDescriptionSections"/>
   </select>
 
-  <select id="selectEnabled" resultType="org.sonar.db.rule.RuleDefinitionDto">
+  <select id="selectEnabled" resultMap="ruleDefinitionResultMap">
     select
       <include refid="selectRuleTableColumns"/>
     from
       rules r
+    <include refid="leftOuterJoinRulesDescriptionSections"/>
     where
       r.status != 'REMOVED'
   </select>
 
-  <select id="selectByUuid" parameterType="map" resultType="Rule">
+  <select id="selectByUuid" parameterType="map" resultMap="ruleResultMap">
     select
       <include refid="selectJoinedTablesColumns"/>
     from
       rules r
     <include refid="outerJoinRulesMetadata"/>
+    <include refid="leftOuterJoinRulesDescriptionSections"/>
     where
       r.uuid=#{uuid,jdbcType=VARCHAR}
   </select>
 
-  <select id="selectDefinitionByUuid" parameterType="String" resultType="org.sonar.db.rule.RuleDefinitionDto">
+  <select id="selectDefinitionByUuid" parameterType="String" resultMap="ruleDefinitionResultMap">
     select
       <include refid="selectRuleTableColumns"/>
     from
       rules r
+    <include refid="leftOuterJoinRulesDescriptionSections"/>
     where
       r.uuid=#{uuid,jdbcType=VARCHAR}
   </select>
 
-  <select id="selectByUuids" parameterType="map" resultType="Rule">
+  <select id="selectByUuids" parameterType="map" resultMap="ruleResultMap">
     select
       <include refid="selectJoinedTablesColumns"/>
     from
       rules r
     <include refid="outerJoinRulesMetadata"/>
+    <include refid="leftOuterJoinRulesDescriptionSections"/>
     where
       <foreach collection="uuids" index="index" item="uuid" open="" separator=" or " close="">
         r.uuid=#{uuid,jdbcType=VARCHAR}
       </foreach>
   </select>
 
-  <select id="selectDefinitionByUuids" parameterType="map" resultType="org.sonar.db.rule.RuleDefinitionDto">
+  <select id="selectDefinitionByUuids" parameterType="map" resultMap="ruleDefinitionResultMap">
     select
       <include refid="selectRuleTableColumns"/>
     from
       rules r
+    <include refid="leftOuterJoinRulesDescriptionSections"/>
     where
       <foreach collection="uuids" index="index" item="uuid" open="" separator=" or " close="">
         r.uuid=#{uuid,jdbcType=VARCHAR}
       </foreach>
   </select>
 
-  <select id="selectByKey" parameterType="map" resultType="Rule">
+  <select id="selectByKey" parameterType="map" resultMap="ruleResultMap">
     select
       <include refid="selectJoinedTablesColumns"/>
     from
       rules r
     <include refid="outerJoinRulesMetadata"/>
+    <include refid="leftOuterJoinRulesDescriptionSections"/>
     where
       r.plugin_name=#{ruleKey.repository,jdbcType=VARCHAR}
       and r.plugin_rule_key=#{ruleKey.rule,jdbcType=VARCHAR}
   </select>
 
-  <select id="selectDefinitionByKey" parameterType="map" resultType="org.sonar.db.rule.RuleDefinitionDto">
+  <select id="selectDefinitionByKey" parameterType="map" resultMap="ruleDefinitionResultMap">
     select
       <include refid="selectRuleTableColumns"/>
     from
       rules r
+    <include refid="leftOuterJoinRulesDescriptionSections"/>
     where
       r.plugin_name=#{repository,jdbcType=VARCHAR}
       and r.plugin_rule_key=#{rule,jdbcType=VARCHAR}
       </foreach>
   </select>
 
-  <select id="selectByKeys" parameterType="map" resultType="Rule">
+  <select id="selectByKeys" parameterType="map" resultMap="ruleResultMap">
     select
       <include refid="selectJoinedTablesColumns"/>
     from
       rules r
     <include refid="outerJoinRulesMetadata"/>
+    <include refid="leftOuterJoinRulesDescriptionSections"/>
     where
       <foreach collection="ruleKeys" index="index" item="ruleKey" open="" separator=" or " close="">
         (r.plugin_name=#{ruleKey.repository,jdbcType=VARCHAR} and r.plugin_rule_key=#{ruleKey.rule,jdbcType=VARCHAR})
       </foreach>
   </select>
 
-  <select id="selectDefinitionByKeys" parameterType="map" resultType="org.sonar.db.rule.RuleDefinitionDto">
+  <select id="selectDefinitionByKeys" parameterType="map" resultMap="ruleDefinitionResultMap">
     select
       <include refid="selectRuleTableColumns"/>
     from
       rules r
+    <include refid="leftOuterJoinRulesDescriptionSections"/>
     where
       <foreach collection="ruleKeys" index="index" item="ruleKey" open="" separator=" or " close="">
         (r.plugin_name=#{ruleKey.repository,jdbcType=VARCHAR} and r.plugin_rule_key=#{ruleKey.rule,jdbcType=VARCHAR})
       r.plugin_name as "repository",
       r.plugin_rule_key as "pluginRuleKey",
       r.name as "name",
-      r.description as "description",
       r.description_format as "descriptionFormat",
       r.priority as "severity",
       r.status as "status",
     left outer join rules_metadata rm on r.uuid = rm.rule_uuid
   </sql>
 
-  <select id="selectByQuery" parameterType="map" resultType="Rule">
+  <select id="selectByQuery" parameterType="map" resultMap="ruleResultMap">
     select
       <include refid="selectJoinedTablesColumns"/>
     from
       rules r
     <include refid="outerJoinRulesMetadata"/>
+    <include refid="leftOuterJoinRulesDescriptionSections"/>
     where
       r.status != 'REMOVED'
       <if test="query.repositoryKey!=null">
       r.updated_at desc
   </select>
 
-  <select id="selectByTypeAndLanguages" parameterType="map" resultType="Rule">
+  <select id="selectByTypeAndLanguages" parameterType="map" resultMap="ruleResultMap">
     select
     <include refid="selectJoinedTablesColumns"/>
     from
     rules r
     <include refid="outerJoinRulesMetadata"/>
+    <include refid="leftOuterJoinRulesDescriptionSections"/>
     where
       r.status != 'REMOVED' and r.is_external=${_false} and r.is_template=${_false}
       and r.rule_type in
     <foreach collection="languages" item="language" separator="," open="(" close=")">#{language, jdbcType=VARCHAR}</foreach>
   </select>
 
+  <insert id="insertRuleDescriptionSection" parameterType="Map" useGeneratedKeys="false">
+    insert into rule_desc_sections (
+      uuid,
+      rule_uuid,
+      kee,
+      description
+    )
+    values (
+      #{dto.uuid,jdbcType=VARCHAR},
+      #{ruleUuid,jdbcType=VARCHAR},
+      #{dto.key,jdbcType=VARCHAR},
+      #{dto.description,jdbcType=VARCHAR}
+    )
+  </insert>
+
   <insert id="insertDefinition" parameterType="org.sonar.db.rule.RuleDefinitionDto" useGeneratedKeys="false">
     insert into rules (
       uuid,
       plugin_key,
       plugin_rule_key,
       plugin_name,
-      description,
       description_format,
       status,
       name,
       #{pluginKey,jdbcType=VARCHAR},
       #{ruleKey,jdbcType=VARCHAR},
       #{repositoryKey,jdbcType=VARCHAR},
-      #{description,jdbcType=VARCHAR},
       #{descriptionFormat,jdbcType=VARCHAR},
       #{status,jdbcType=VARCHAR},
       #{name,jdbcType=VARCHAR},
       plugin_key=#{pluginKey,jdbcType=VARCHAR},
       plugin_rule_key=#{ruleKey,jdbcType=VARCHAR},
       plugin_name=#{repositoryKey,jdbcType=VARCHAR},
-      description=#{description,jdbcType=VARCHAR},
       description_format=#{descriptionFormat,jdbcType=VARCHAR},
       status=#{status,jdbcType=VARCHAR},
       name=#{name,jdbcType=VARCHAR},
       uuid=#{uuid,jdbcType=VARCHAR}
   </update>
 
+  <delete id="deleteRuleDescriptionSection" parameterType="String">
+    delete from
+      rule_desc_sections
+    where
+      rule_uuid=#{uuid,jdbcType=VARCHAR}
+  </delete>
+
   <select id="countMetadata" parameterType="org.sonar.db.rule.RuleMetadataDto" resultType="int">
     select
       count(1)
index 438b517a244eb18956b8413dea7c56d1459c5988..c68c59ad0bfc77e20c45a5ade53c09cdfbf8eb7b 100644 (file)
@@ -28,9 +28,8 @@ 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.commons.lang.RandomStringUtils;
 import org.apache.ibatis.exceptions.PersistenceException;
-import org.apache.ibatis.session.ResultHandler;
 import org.junit.Rule;
 import org.junit.Test;
 import org.sonar.api.rule.RuleKey;
@@ -41,6 +40,7 @@ import org.sonar.api.rules.RuleType;
 import org.sonar.api.server.debt.DebtRemediationFunction;
 import org.sonar.api.utils.DateUtils;
 import org.sonar.api.utils.System2;
+import org.sonar.core.util.UuidFactoryFast;
 import org.sonar.db.DbTester;
 import org.sonar.db.RowNotFoundException;
 import org.sonar.db.rule.RuleDto.Scope;
@@ -49,18 +49,15 @@ 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);
@@ -299,9 +296,7 @@ public class RuleDaoTest {
     RuleDefinitionDto rule = db.rules().insert();
     db.rules().insert(r -> r.setStatus(REMOVED));
 
-    final List<RuleDefinitionDto> rules = new ArrayList<>();
-    ResultHandler<RuleDefinitionDto> resultHandler = resultContext -> rules.add(resultContext.getResultObject());
-    underTest.selectEnabled(db.getSession(), resultHandler);
+    List<RuleDefinitionDto> rules = underTest.selectEnabled(db.getSession());
 
     assertThat(rules.size()).isOne();
     RuleDefinitionDto ruleDto = rules.get(0);
@@ -427,13 +422,14 @@ public class RuleDaoTest {
 
   @Test
   public void insert() {
+    RuleDescriptionSectionDto sectionDto = createDefaultRuleDescriptionSection();
     RuleDefinitionDto newRule = new RuleDefinitionDto()
       .setUuid("rule-uuid")
       .setRuleKey("NewRuleKey")
       .setRepositoryKey("plugin")
       .setName("new name")
       .setDescriptionFormat(RuleDto.Format.MARKDOWN)
-      .addRuleDescriptionSectionDto(RULE_DESCRIPTION_SECTION_1)
+      .addRuleDescriptionSectionDto(sectionDto)
       .setStatus(RuleStatus.DEPRECATED)
       .setConfigKey("NewConfigKey")
       .setSeverity(Severity.INFO)
@@ -480,19 +476,20 @@ public class RuleDaoTest {
     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);
+      .containsOnly(sectionDto);
   }
 
   @Test
   public void update_RuleDefinitionDto() {
     RuleDefinitionDto rule = db.rules().insert();
+    RuleDescriptionSectionDto sectionDto = createDefaultRuleDescriptionSection();
     RuleDefinitionDto ruleToUpdate = new RuleDefinitionDto()
       .setUuid(rule.getUuid())
       .setRuleKey("NewRuleKey")
       .setRepositoryKey("plugin")
       .setName("new name")
       .setDescriptionFormat(RuleDto.Format.MARKDOWN)
-      .addRuleDescriptionSectionDto(createDefaultRuleDescriptionSection(randomAlphabetic(5)))
+      .addRuleDescriptionSectionDto(sectionDto)
       .setStatus(RuleStatus.DEPRECATED)
       .setConfigKey("NewConfigKey")
       .setSeverity(Severity.INFO)
@@ -537,8 +534,53 @@ public class RuleDaoTest {
     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);
+      .containsOnly(sectionDto);
+  }
+
+  @Test
+  public void update_rule_sections_add_new_section() {
+    RuleDefinitionDto rule = db.rules().insert();
+    RuleDescriptionSectionDto existingSection = rule.getRuleDescriptionSectionDtos().iterator().next();
+    RuleDescriptionSectionDto newSection = RuleDescriptionSectionDto.builder()
+      .uuid(randomAlphanumeric(20))
+      .key("new_key")
+      .description(randomAlphanumeric(1000))
+      .build();
+
+    rule.addRuleDescriptionSectionDto(newSection);
+
+    underTest.update(db.getSession(), rule);
+    db.getSession().commit();
+
+    RuleDefinitionDto ruleDto = underTest.selectOrFailDefinitionByKey(db.getSession(), RuleKey.of(rule.getRepositoryKey(), rule.getRuleKey()));
+
+    assertThat(ruleDto.getRuleDescriptionSectionDtos())
+      .usingRecursiveFieldByFieldElementComparator()
+      .containsExactlyInAnyOrder(newSection, existingSection);
+  }
+
+  @Test
+  public void update_rule_sections_replaces_section() {
+    RuleDefinitionDto rule = db.rules().insert();
+    RuleDescriptionSectionDto existingSection = rule.getRuleDescriptionSectionDtos().iterator().next();
+    RuleDescriptionSectionDto replacingSection = RuleDescriptionSectionDto.builder()
+      .uuid(randomAlphanumeric(20))
+      .key(existingSection.getKey())
+      .description(randomAlphanumeric(1000))
+      .build();
+
+    rule.addOrReplaceRuleDescriptionSectionDto(replacingSection);
+
+    underTest.update(db.getSession(), rule);
+    db.getSession().commit();
+
+    RuleDefinitionDto ruleDto = underTest.selectOrFailDefinitionByKey(db.getSession(), RuleKey.of(rule.getRepositoryKey(), rule.getRuleKey()));
+
+    assertThat(ruleDto.getRuleDescriptionSectionDtos())
+      .usingRecursiveFieldByFieldElementComparator()
+      .containsOnly(replacingSection);
   }
 
   @Test
@@ -806,7 +848,7 @@ public class RuleDaoTest {
     assertThat(firstRule.getPluginRuleKey()).isEqualTo(r1.getRuleKey());
     assertThat(firstRule.getName()).isEqualTo(r1.getName());
     //FIXME SONAR-16309
-    assertThat(firstRule.getDescription()).isEqualTo(r1.getRuleDescriptionSectionDtos().stream().map(RuleDescriptionSectionDto::getDescription).collect(Collectors.joining()));
+    //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());
@@ -886,7 +928,7 @@ public class RuleDaoTest {
     assertThat(firstRule.getPluginRuleKey()).isEqualTo(r1.getRuleKey());
     assertThat(firstRule.getName()).isEqualTo(r1.getName());
     //FIXME SONAR-16309
-    assertThat(firstRule.getDescription()).isEqualTo(r1.getRuleDescriptionSectionDtos().stream().map(RuleDescriptionSectionDto::getDescription).collect(Collectors.joining()));
+    //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()));
@@ -1047,6 +1089,10 @@ public class RuleDaoTest {
       .isInstanceOf(PersistenceException.class);
   }
 
+  private static RuleDescriptionSectionDto createDefaultRuleDescriptionSection() {
+    return RuleDescriptionSectionDto.createDefaultRuleDescriptionSection(UuidFactoryFast.getInstance().create(), RandomStringUtils.randomAlphanumeric(1000));
+  }
+
   private static class Accumulator<T> implements Consumer<T> {
     private final List<T> list = new ArrayList<>();
 
index b544afa6379e140953debbf2aca5cf311399b70b..28f8fc3d219741e536bbb650865f131d006cb22e 100644 (file)
@@ -66,7 +66,7 @@ public class RuleTesting {
 
   public static RuleDefinitionDto newRule(RuleKey key) {
     RuleDefinitionDto ruleDefinitionDto = newRuleWithoutSection(key);
-    ruleDefinitionDto.addRuleDescriptionSectionDto(createDefaultRuleDescriptionSection("description_" + randomAlphabetic(5)));
+    ruleDefinitionDto.addRuleDescriptionSectionDto(createDefaultRuleDescriptionSection(uuidFactory.create(), "description_" + randomAlphabetic(5)));
     return ruleDefinitionDto;
   }
 
@@ -181,7 +181,7 @@ public class RuleTesting {
       .setRepositoryKey(ruleKey.repository())
       .setName("Rule " + ruleKey.rule())
       .setDescriptionFormat(RuleDto.Format.HTML)
-      .addRuleDescriptionSectionDto(createDefaultRuleDescriptionSection("Description" + ruleKey.rule()))
+      .addRuleDescriptionSectionDto(createDefaultRuleDescriptionSection(uuidFactory.create(), "Description" + ruleKey.rule()))
       .setStatus(RuleStatus.READY)
       .setConfigKey("InternalKey" + ruleKey.rule())
       .setSeverity(Severity.INFO)
index e821838fd17e2af6fbcf93d7478d2cb5146cd149..d423396f3290d61f34c6d08b80c8319efb6383c8 100644 (file)
@@ -75,9 +75,7 @@ public class DefaultRuleFinder implements ServerRuleFinder {
   @Override
   public Collection<RuleDefinitionDto> findAll() {
     try (DbSession dbSession = dbClient.openSession(false)) {
-      List<RuleDefinitionDto> list = new ArrayList<>();
-      ruleDao.selectEnabled(dbSession, r -> list.add(r.getResultObject()));
-      return list;
+      return ruleDao.selectEnabled(dbSession);
     }
   }
 
index 12c34c19c62404f277d0d9adec45ff165c533ecf..494b8af7a1664c8ebc01b87c91048943d80b4ef5 100644 (file)
@@ -48,7 +48,7 @@ public class HotspotRuleDescriptionTest {
 
   @Test
   public void parse_returns_all_empty_fields_when_empty_description() {
-    RuleDefinitionDto dto = newRuleWithoutSection().addRuleDescriptionSectionDto(createDefaultRuleDescriptionSection(""));
+    RuleDefinitionDto dto = newRuleWithoutSection().addRuleDescriptionSectionDto(createDefaultRuleDescriptionSection("uuid", ""));
 
     HotspotRuleDescription result = HotspotRuleDescription.from(dto);
 
@@ -60,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 = newRuleWithoutSection().addRuleDescriptionSectionDto(createDefaultRuleDescriptionSection(description));
+    RuleDefinitionDto dto = newRuleWithoutSection().addRuleDescriptionSectionDto(createDefaultRuleDescriptionSection("uuid", description));
 
     HotspotRuleDescription result = HotspotRuleDescription.from(dto);
 
@@ -82,7 +82,7 @@ public class HotspotRuleDescriptionTest {
   @Test
   public void parse_return_null_risk_when_desc_starts_with_ask_yourself_title() {
     RuleDefinitionDto dto = newRuleWithoutSection().addRuleDescriptionSectionDto(
-      createDefaultRuleDescriptionSection((ASKATRISK + RECOMMENTEDCODINGPRACTICE)));
+      createDefaultRuleDescriptionSection("uuid", (ASKATRISK + RECOMMENTEDCODINGPRACTICE)));
 
     HotspotRuleDescription result = HotspotRuleDescription.from(dto);
 
@@ -94,7 +94,7 @@ public class HotspotRuleDescriptionTest {
   @Test
   public void parse_return_null_vulnerable_when_no_ask_yourself_whether_title() {
     RuleDefinitionDto dto = newRuleWithoutSection()
-      .addRuleDescriptionSectionDto(createDefaultRuleDescriptionSection((DESCRIPTION + RECOMMENTEDCODINGPRACTICE)));
+      .addRuleDescriptionSectionDto(createDefaultRuleDescriptionSection("uuid", (DESCRIPTION + RECOMMENTEDCODINGPRACTICE)));
 
     HotspotRuleDescription result = HotspotRuleDescription.from(dto);
 
@@ -106,7 +106,7 @@ public class HotspotRuleDescriptionTest {
   @Test
   public void parse_return_null_fixIt_when_desc_has_no_Recommended_Secure_Coding_Practices_title() {
     RuleDefinitionDto dto = newRuleWithoutSection()
-      .addRuleDescriptionSectionDto(createDefaultRuleDescriptionSection((DESCRIPTION + ASKATRISK)));
+      .addRuleDescriptionSectionDto(createDefaultRuleDescriptionSection("uuid", (DESCRIPTION + ASKATRISK)));
 
     HotspotRuleDescription result = HotspotRuleDescription.from(dto);
 
@@ -118,7 +118,7 @@ public class HotspotRuleDescriptionTest {
   @Test
   public void parse_with_noncompliant_section_not_removed() {
     RuleDefinitionDto dto = newRuleWithoutSection().addRuleDescriptionSectionDto(
-      createDefaultRuleDescriptionSection((DESCRIPTION + NONCOMPLIANTCODE + COMPLIANTCODE)));
+      createDefaultRuleDescriptionSection("uuid", (DESCRIPTION + NONCOMPLIANTCODE + COMPLIANTCODE)));
 
     HotspotRuleDescription result = HotspotRuleDescription.from(dto);
 
@@ -130,7 +130,7 @@ public class HotspotRuleDescriptionTest {
   @Test
   public void parse_moved_noncompliant_code() {
     RuleDefinitionDto dto = newRuleWithoutSection().addRuleDescriptionSectionDto(
-      createDefaultRuleDescriptionSection((DESCRIPTION + RECOMMENTEDCODINGPRACTICE + NONCOMPLIANTCODE + SEE)));
+      createDefaultRuleDescriptionSection("uuid", (DESCRIPTION + RECOMMENTEDCODINGPRACTICE + NONCOMPLIANTCODE + SEE)));
 
     HotspotRuleDescription result = HotspotRuleDescription.from(dto);
 
@@ -144,7 +144,7 @@ public class HotspotRuleDescriptionTest {
   @Test
   public void parse_moved_sensitivecode_code() {
     RuleDefinitionDto dto = newRuleWithoutSection().addRuleDescriptionSectionDto(
-      createDefaultRuleDescriptionSection((DESCRIPTION + ASKATRISK + RECOMMENTEDCODINGPRACTICE + SENSITIVECODE + SEE)));
+      createDefaultRuleDescriptionSection("uuid", (DESCRIPTION + ASKATRISK + RECOMMENTEDCODINGPRACTICE + SENSITIVECODE + SEE)));
 
     HotspotRuleDescription result = HotspotRuleDescription.from(dto);
 
@@ -164,7 +164,7 @@ public class HotspotRuleDescriptionTest {
       .setTemplateUuid("123")
       .setDescriptionFormat(RuleDto.Format.MARKDOWN)
       .addRuleDescriptionSectionDto(createDefaultRuleDescriptionSection(
-        ruleDescription + "\n"
+        "uuid", ruleDescription + "\n"
           + "== Exceptions" + "\n"
           + exceptionsContent + "\n"
           + "== Ask Yourself Whether" + "\n"
index b50f77deb3e82311754146c93df4e1cfd6273950..ca8ca7d6da292697ae4a4a58d4a65343f7c11424 100644 (file)
@@ -29,8 +29,8 @@ import static org.sonar.db.rule.RuleDescriptionSectionDto.createDefaultRuleDescr
 
 public class RuleDescriptionFormatterTest {
 
-  private static final RuleDescriptionSectionDto HTML_SECTION = createDefaultRuleDescriptionSection("<span class=\"example\">*md* ``description``</span>");
-  private static final RuleDescriptionSectionDto MARKDOWN_SECTION = createDefaultRuleDescriptionSection("*md* ``description``");
+  private static final RuleDescriptionSectionDto HTML_SECTION = createDefaultRuleDescriptionSection("uuid", "<span class=\"example\">*md* ``description``</span>");
+  private static final RuleDescriptionSectionDto MARKDOWN_SECTION = createDefaultRuleDescriptionSection("uuid", "*md* ``description``");
 
   @Test
   public void getMarkdownDescriptionAsHtml() {
@@ -55,7 +55,7 @@ public class RuleDescriptionFormatterTest {
 
   @Test
   public void handleNullDescriptionFormat() {
-    RuleDescriptionSectionDto sectionWithNullFormat = createDefaultRuleDescriptionSection("whatever");
+    RuleDescriptionSectionDto sectionWithNullFormat = createDefaultRuleDescriptionSection("uuid", "whatever");
     RuleDefinitionDto rule = new RuleDefinitionDto().addRuleDescriptionSectionDto(sectionWithNullFormat);
     String result = RuleDescriptionFormatter.getDescriptionAsHtml(rule);
     assertThat(result).isNull();
index d4ae6d45d5baec8741d23faf97b6bfc11dcc4654..d617fd46c293cfe22739e1d12235efd90620785d 100644 (file)
@@ -33,6 +33,8 @@ import org.sonar.api.impl.utils.AlwaysIncreasingSystem2;
 import org.sonar.api.rule.RuleKey;
 import org.sonar.api.rule.RuleStatus;
 import org.sonar.api.utils.System2;
+import org.sonar.core.util.UuidFactory;
+import org.sonar.core.util.UuidFactoryFast;
 import org.sonar.db.DbTester;
 import org.sonar.db.qualityprofile.QProfileDto;
 import org.sonar.db.rule.RuleDefinitionDto;
@@ -103,7 +105,8 @@ public class RuleIndexTest {
   private ActiveRuleIndexer activeRuleIndexer = new ActiveRuleIndexer(db.getDbClient(), es.client());
 
   private RuleIndex underTest = new RuleIndex(es.client(), system2);
-
+  private UuidFactory uuidFactory = UuidFactoryFast.getInstance();
+  
   @Test
   public void search_all_rules() {
     createRule();
@@ -214,19 +217,20 @@ public class RuleIndexTest {
     // otherwise the generated random values may raise false-positives
     RuleDefinitionDto rule1 = createJavaRule(rule -> rule.setRuleKey("123")
       .setName("rule 123")
-      .addRuleDescriptionSectionDto(createDefaultRuleDescriptionSection("My great rule CWE-123 which makes your code 1000 times better!")));
+      .addOrReplaceRuleDescriptionSectionDto(createDefaultRuleDescriptionSection(uuidFactory.create(), "My great rule CWE-123 which makes your code 1000 times better!")));
     RuleDefinitionDto rule2 = createJavaRule(rule -> rule.setRuleKey("124")
       .setName("rule 124")
-      .addRuleDescriptionSectionDto(createDefaultRuleDescriptionSection("Another great and shiny rule CWE-124")));
+      .addOrReplaceRuleDescriptionSectionDto(createDefaultRuleDescriptionSection(uuidFactory.create(), "Another great and shiny rule CWE-124")));
     RuleDefinitionDto rule3 = createJavaRule(rule -> rule.setRuleKey("1000")
       .setName("rule 1000")
-      .addRuleDescriptionSectionDto(createDefaultRuleDescriptionSection("Another great rule CWE-1000")));
+      .addOrReplaceRuleDescriptionSectionDto(createDefaultRuleDescriptionSection(uuidFactory.create(), "Another great rule CWE-1000")));
     RuleDefinitionDto rule4 = createJavaRule(rule -> rule.setRuleKey("404")
       .setName("rule 404")
-      .addRuleDescriptionSectionDto(createDefaultRuleDescriptionSection("<h1>HTML-Geeks</h1><p style=\"color:blue\">special formatting!</p><table><tr><td>inside</td><td>tables</td></tr></table>")));
+      .addOrReplaceRuleDescriptionSectionDto(createDefaultRuleDescriptionSection(uuidFactory.create(),
+        "<h1>HTML-Geeks</h1><p style=\"color:blue\">special formatting!</p><table><tr><td>inside</td><td>tables</td></tr></table>")));
     RuleDefinitionDto rule5 = createJavaRule(rule -> rule.setRuleKey("405")
       .setName("rule 405")
-      .addRuleDescriptionSectionDto(createDefaultRuleDescriptionSection("internationalization missunderstandings alsdkjfnadklsjfnadkdfnsksdjfn")));
+      .addOrReplaceRuleDescriptionSectionDto(createDefaultRuleDescriptionSection(uuidFactory.create(), "internationalization missunderstandings alsdkjfnadklsjfnadkdfnsksdjfn")));
     index();
 
     // partial match at word boundary
index e3362dd71ee9a56eb0c23f9079048c8e1ee5575a..1c8a1ee73e18ae97b989369a76894c8f49539ce1 100644 (file)
@@ -36,6 +36,7 @@ import org.sonar.api.rule.Severity;
 import org.sonar.api.rules.RuleType;
 import org.sonar.api.utils.log.LogTester;
 import org.sonar.api.utils.log.LoggerLevel;
+import org.sonar.core.util.UuidFactoryFast;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
 import org.sonar.db.DbTester;
@@ -43,7 +44,6 @@ 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;
 import org.sonar.server.es.EsTester;
 import org.sonar.server.security.SecurityStandards;
 import org.sonar.server.security.SecurityStandards.SQCategory;
@@ -56,6 +56,7 @@ 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.db.rule.RuleTesting.newRuleWithoutSection;
 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;
@@ -69,7 +70,8 @@ public class RuleIndexerTest {
     "<h2>Recommended Secure Coding Practices</h2>\n" +
     "foo";
 
-  private static final RuleDescriptionSectionDto RULE_DESCRIPTION_SECTION_DTO = createDefaultRuleDescriptionSection(VALID_HOTSPOT_RULE_DESCRIPTION);
+  private static final UuidFactoryFast uuidFactory = UuidFactoryFast.getInstance();
+  private static final RuleDescriptionSectionDto RULE_DESCRIPTION_SECTION_DTO = createDefaultRuleDescriptionSection(uuidFactory.create(), VALID_HOTSPOT_RULE_DESCRIPTION);
 
   @Rule
   public EsTester es = EsTester.create();
@@ -131,8 +133,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(" "));
-    RuleDescriptionSectionDto ruleDescriptionSectionDto = createDefaultRuleDescriptionSection(description);
-    RuleDefinitionDto rule = dbTester.rules().insert(r -> r.addRuleDescriptionSectionDto(ruleDescriptionSectionDto));
+    RuleDescriptionSectionDto ruleDescriptionSectionDto = createDefaultRuleDescriptionSection(uuidFactory.create(), description);
+    RuleDefinitionDto rule = dbTester.rules().insert(r -> r.addOrReplaceRuleDescriptionSectionDto(ruleDescriptionSectionDto));
     underTest.commitAndIndex(dbTester.getSession(), rule.getUuid());
 
     assertThat(es.countDocuments(TYPE_RULE)).isOne();
@@ -145,7 +147,7 @@ public class RuleIndexerTest {
       .flatMap(t -> CWES_BY_SQ_CATEGORY.get(t).stream().map(e -> "cwe:" + e))
       .collect(toSet());
     SecurityStandards securityStandards = SecurityStandards.fromSecurityStandards(standards);
-    RuleDefinitionDto rule = dbTester.rules().insert(RuleTesting.newRule()
+    RuleDefinitionDto rule = dbTester.rules().insert(newRuleWithoutSection()
       .setType(RuleType.SECURITY_HOTSPOT)
       .setSecurityStandards(standards)
       .addRuleDescriptionSectionDto(RULE_DESCRIPTION_SECTION_DTO));
@@ -180,7 +182,7 @@ public class RuleIndexerTest {
 
   @Test
   public void log_debug_when_hotspot_rule_no_description () {
-    RuleDefinitionDto rule = dbTester.rules().insert(RuleTesting.newRuleWithoutSection()
+    RuleDefinitionDto rule = dbTester.rules().insert(newRuleWithoutSection()
       .setType(RuleType.SECURITY_HOTSPOT));
     underTest.commitAndIndex(dbTester.getSession(), rule.getUuid());
 
@@ -193,9 +195,9 @@ public class RuleIndexerTest {
 
   @Test
   public void log_debug_when_hotspot_rule_description_has_none_of_the_key_titles() {
-    RuleDefinitionDto rule = dbTester.rules().insert(RuleTesting.newRule()
+    RuleDefinitionDto rule = dbTester.rules().insert(newRuleWithoutSection()
       .setType(RuleType.SECURITY_HOTSPOT)
-      .addRuleDescriptionSectionDto(createDefaultRuleDescriptionSection(randomAlphabetic(30))));
+      .addRuleDescriptionSectionDto(createDefaultRuleDescriptionSection(uuidFactory.create(), randomAlphabetic(30))));
     underTest.commitAndIndex(dbTester.getSession(), rule.getUuid());
 
     assertThat(logTester.getLogs()).hasSize(1);
@@ -207,9 +209,9 @@ public class RuleIndexerTest {
 
   @Test
   public void log_debug_when_hotspot_rule_description_is_missing_fixIt_tab_content() {
-    RuleDefinitionDto rule = dbTester.rules().insert(RuleTesting.newRule()
+    RuleDefinitionDto rule = dbTester.rules().insert(newRuleWithoutSection()
       .setType(RuleType.SECURITY_HOTSPOT)
-      .addRuleDescriptionSectionDto(createDefaultRuleDescriptionSection("bar\n" +
+      .addRuleDescriptionSectionDto(createDefaultRuleDescriptionSection(uuidFactory.create(), "bar\n" +
         "<h2>Ask Yourself Whether</h2>\n" +
         "foo")));
     underTest.commitAndIndex(dbTester.getSession(), rule.getUuid());
@@ -223,9 +225,9 @@ public class RuleIndexerTest {
 
   @Test
   public void log_debug_when_hotspot_rule_description_is_missing_risk_tab_content() {
-    RuleDefinitionDto rule = dbTester.rules().insert(RuleTesting.newRule()
+    RuleDefinitionDto rule = dbTester.rules().insert(newRuleWithoutSection()
       .setType(RuleType.SECURITY_HOTSPOT)
-      .addRuleDescriptionSectionDto(createDefaultRuleDescriptionSection("<h2>Ask Yourself Whether</h2>\n" +
+      .addRuleDescriptionSectionDto(createDefaultRuleDescriptionSection(uuidFactory.create(), "<h2>Ask Yourself Whether</h2>\n" +
         "bar\n" +
         "<h2>Recommended Secure Coding Practices</h2>\n" +
         "foo")));
@@ -240,9 +242,9 @@ public class RuleIndexerTest {
 
   @Test
   public void log_debug_when_hotspot_rule_description_is_missing_vulnerable_tab_content() {
-    RuleDefinitionDto rule = dbTester.rules().insert(RuleTesting.newRule()
+    RuleDefinitionDto rule = dbTester.rules().insert(newRuleWithoutSection()
       .setType(RuleType.SECURITY_HOTSPOT)
-      .addRuleDescriptionSectionDto(createDefaultRuleDescriptionSection("bar\n" +
+      .addRuleDescriptionSectionDto(createDefaultRuleDescriptionSection(uuidFactory.create(), "bar\n" +
         "<h2>Recommended Secure Coding Practices</h2>\n" +
         "foo")));
     underTest.commitAndIndex(dbTester.getSession(), rule.getUuid());
index cd2c223598156cb52f01d14d4a2731b81c688599..57db0bb1390013b66dbcd25ea931b17b8c1cab74 100644 (file)
@@ -46,7 +46,6 @@ 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
@@ -423,7 +422,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(createDefaultRuleDescriptionSection(randomAlphabetic(5)).getDescription());
+    assertThat(rule.getDescription()).isEqualTo(ruleDefinition.getDefaultRuleDescriptionSectionDto().getDescription());
 
     assertThat(rule.getParams()).hasSize(1);
     org.sonar.api.rules.RuleParam param = rule.getParams().iterator().next();
index fb4e08ec15a1a0a65a2b132576dcaa0cda3cffc1..5540f8f376cd4dbf8a499906f8b408915d8e9516 100644 (file)
@@ -401,10 +401,10 @@ public class RegisterRules implements Startable {
       .setCreatedAt(system2.now())
       .setUpdatedAt(system2.now());
     if (isNotEmpty(ruleDef.htmlDescription())) {
-      ruleDto.addRuleDescriptionSectionDto(createDefaultRuleDescriptionSection(ruleDef.htmlDescription()));
+      ruleDto.addRuleDescriptionSectionDto(createDefaultRuleDescriptionSection(uuidFactory.create(), ruleDef.htmlDescription()));
       ruleDto.setDescriptionFormat(Format.HTML);
     } else if (isNotEmpty(ruleDef.markdownDescription())) {
-      ruleDto.addRuleDescriptionSectionDto(createDefaultRuleDescriptionSection(ruleDef.markdownDescription()));
+      ruleDto.addRuleDescriptionSectionDto(createDefaultRuleDescriptionSection(uuidFactory.create(), ruleDef.markdownDescription()));
       ruleDto.setDescriptionFormat(Format.MARKDOWN);
     }
 
@@ -433,7 +433,7 @@ public class RegisterRules implements Startable {
     }
   }
 
-  private static boolean mergeRule(RulesDefinition.Rule def, RuleDefinitionDto dto) {
+  private boolean mergeRule(RulesDefinition.Rule def, RuleDefinitionDto dto) {
     boolean changed = false;
     if (!Objects.equals(dto.getName(), def.name())) {
       dto.setName(def.name());
@@ -484,16 +484,16 @@ public class RegisterRules implements Startable {
     return changed;
   }
 
-  private static boolean mergeDescription(RulesDefinition.Rule rule, RuleDefinitionDto ruleDefinitionDto) {
+  private boolean mergeDescription(RulesDefinition.Rule rule, RuleDefinitionDto ruleDefinitionDto) {
     boolean changed = false;
 
     String currentDescription = ruleDefinitionDto.getDefaultRuleDescriptionSectionDto() != null ? ruleDefinitionDto.getDefaultRuleDescriptionSectionDto().getDescription() : null;
     if (isHtmlDescriptionUpdated(rule, currentDescription)) {
-      ruleDefinitionDto.addOrReplaceRuleDescriptionSectionDto(createDefaultRuleDescriptionSection(rule.htmlDescription()));
+      ruleDefinitionDto.addOrReplaceRuleDescriptionSectionDto(createDefaultRuleDescriptionSection(uuidFactory.create(), rule.htmlDescription()));
       ruleDefinitionDto.setDescriptionFormat(Format.HTML);
       changed = true;
     } else if (isMarkdownDescriptionUpdated(rule, currentDescription)) {
-      ruleDefinitionDto.addOrReplaceRuleDescriptionSectionDto(createDefaultRuleDescriptionSection(rule.markdownDescription()));
+      ruleDefinitionDto.addOrReplaceRuleDescriptionSectionDto(createDefaultRuleDescriptionSection(uuidFactory.create(), rule.markdownDescription()));
       ruleDefinitionDto.setDescriptionFormat(Format.MARKDOWN);
       changed = true;
     }
index e9cbb4ebbc79a8fdd10aff5003c8cb5e3d45b01c..34d1c8deaad3869a37460d87ae3a28e323bbc7b9 100644 (file)
@@ -836,7 +836,7 @@ public class RegisterRulesTest {
       .setRepositoryKey("findbugs")
       .setName("Rule One")
       .setScope(Scope.ALL)
-      .addRuleDescriptionSectionDto(createDefaultRuleDescriptionSection("Rule one description"))
+      .addRuleDescriptionSectionDto(createDefaultRuleDescriptionSection(uuidFactory.create(), "Rule one description"))
       .setDescriptionFormat(RuleDto.Format.HTML)
       .setSystemTags(newHashSet("tag1", "tag2")));
     db.getSession().commit();
index 77b5db39783d2ecc0d57b6ff64a659daf868987d..f96821ea009c2879df2907706149c74ae2212135 100644 (file)
@@ -74,7 +74,7 @@ public class QualityProfileChangeEventServiceImplTest {
       .setRepositoryKey("repo")
       .setRuleKey("ruleKey")
       .setDescriptionFormat(RuleDto.Format.MARKDOWN)
-      .addRuleDescriptionSectionDto(createDefaultRuleDescriptionSection("<div>line1\nline2</div>"));
+      .addOrReplaceRuleDescriptionSectionDto(createDefaultRuleDescriptionSection("uuid", "<div>line1\nline2</div>"));
     db.rules().insert(rule1);
 
     ActiveRuleDto activeRuleDto = ActiveRuleDto.createFor(qualityProfileDto, rule1);
index e67c9d22a4db126be6c4d8ec27cf0ce2711c565e..b7d321877a789b0643a3a7907be69a3be25b937f 100644 (file)
@@ -208,7 +208,7 @@ public class RuleCreator {
       .setUpdatedAt(system2.now());
 
     if (newRule.markdownDescription() != null) {
-      RuleDescriptionSectionDto ruleDescriptionSectionDto = createDefaultRuleDescriptionSection(newRule.markdownDescription());
+      RuleDescriptionSectionDto ruleDescriptionSectionDto = createDefaultRuleDescriptionSection(uuidFactory.create(), newRule.markdownDescription());
       ruleDefinition.setDescriptionFormat(Format.MARKDOWN);
       ruleDefinition.addRuleDescriptionSectionDto(ruleDescriptionSectionDto);
     }
index 78e4aebf313542c56df1ca357d2339cf9cf737f2..c6fa6e3342d2a25322e2018f7028a616454a9a58 100644 (file)
@@ -37,6 +37,7 @@ import org.sonar.api.rule.Severity;
 import org.sonar.api.server.ServerSide;
 import org.sonar.api.server.debt.DebtRemediationFunction;
 import org.sonar.api.utils.System2;
+import org.sonar.core.util.UuidFactory;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
 import org.sonar.db.qualityprofile.ActiveRuleDto;
@@ -61,11 +62,13 @@ public class RuleUpdater {
 
   private final DbClient dbClient;
   private final RuleIndexer ruleIndexer;
+  private final UuidFactory uuidFactory;
   private final System2 system;
 
-  public RuleUpdater(DbClient dbClient, RuleIndexer ruleIndexer, System2 system) {
+  public RuleUpdater(DbClient dbClient, RuleIndexer ruleIndexer, UuidFactory uuidFactory, System2 system) {
     this.dbClient = dbClient;
     this.ruleIndexer = ruleIndexer;
+    this.uuidFactory = uuidFactory;
     this.system = system;
   }
 
@@ -133,12 +136,12 @@ public class RuleUpdater {
     rule.setName(name);
   }
 
-  private static void updateDescription(RuleUpdate update, RuleDto rule) {
+  private void updateDescription(RuleUpdate update, RuleDto rule) {
     String description = update.getMarkdownDescription();
     if (isNullOrEmpty(description)) {
       throw new IllegalArgumentException("The description is missing");
     }
-    RuleDescriptionSectionDto descriptionSectionDto = createDefaultRuleDescriptionSection(description);
+    RuleDescriptionSectionDto descriptionSectionDto = createDefaultRuleDescriptionSection(uuidFactory.create(), description);
     rule.setDescriptionFormat(RuleDto.Format.MARKDOWN);
     rule.addOrReplaceRuleDescriptionSectionDto(descriptionSectionDto);
   }
index 70823cd4d32320e7dc2d30fa2f4d4b0b6af0442c..a32457519141779157e92b1a95c95d9a1e864abd 100644 (file)
@@ -19,6 +19,7 @@
  */
 package org.sonar.server.rule.ws;
 
+import java.util.Set;
 import org.sonar.api.server.ws.Request;
 import org.sonar.api.server.ws.Response;
 import org.sonar.api.server.ws.WebService;
@@ -29,6 +30,7 @@ import org.sonarqube.ws.MediaTypes;
 import org.sonarqube.ws.Rules.ListResponse;
 
 import static com.google.common.base.Strings.nullToEmpty;
+import static java.util.stream.Collectors.toSet;
 
 public class ListAction implements RulesWsAction {
 
@@ -54,21 +56,24 @@ public class ListAction implements RulesWsAction {
     final ListResponse.Builder listResponseBuilder = ListResponse.newBuilder();
     final ListResponse.Rule.Builder ruleBuilder = ListResponse.Rule.newBuilder();
     try (DbSession dbSession = dbClient.openSession(false)) {
-      dbClient.ruleDao().selectEnabled(dbSession, resultContext -> {
-        RuleDefinitionDto dto = resultContext.getResultObject();
-        ruleBuilder
-          .clear()
-          .setRepository(dto.getRepositoryKey())
-          .setKey(dto.getRuleKey())
-          .setName(nullToEmpty(dto.getName()))
-          .setInternalKey(nullToEmpty(dto.getConfigKey()));
-        listResponseBuilder.addRules(ruleBuilder.build());
-      });
+      Set<ListResponse.Rule> rules = dbClient.ruleDao().selectEnabled(dbSession).stream()
+        .map(dto -> toRule(ruleBuilder, dto))
+        .collect(toSet());
+      listResponseBuilder.addAllRules(rules);
     }
-
     // JSON response is voluntarily not supported. This WS is for internal use.
     wsResponse.stream().setMediaType(MediaTypes.PROTOBUF);
     listResponseBuilder.build().writeTo(wsResponse.stream().output());
   }
 
+  private ListResponse.Rule toRule(ListResponse.Rule.Builder ruleBuilder, RuleDefinitionDto dto) {
+    return ruleBuilder
+      .clear()
+      .setRepository(dto.getRepositoryKey())
+      .setKey(dto.getRuleKey())
+      .setName(nullToEmpty(dto.getName()))
+      .setInternalKey(nullToEmpty(dto.getConfigKey()))
+      .build();
+  }
+
 }
index fd9c031e807fbe4dec5d873b570da8ea3d968330..d791c487edc880839d0fea2a16a772d324e77fc4 100644 (file)
@@ -45,6 +45,8 @@ import org.sonar.api.issue.Issue;
 import org.sonar.api.rules.RuleType;
 import org.sonar.api.utils.System2;
 import org.sonar.api.web.UserRole;
+import org.sonar.core.util.UuidFactory;
+import org.sonar.core.util.UuidFactoryFast;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
 import org.sonar.db.DbTester;
@@ -113,6 +115,7 @@ public class ShowActionTest {
   private final TextRangeResponseFormatter textRangeFormatter = new TextRangeResponseFormatter();
   private final ShowAction underTest = new ShowAction(dbClient, hotspotWsSupport, responseFormatter, textRangeFormatter, userFormatter, issueChangeSupport);
   private final WsActionTester actionTester = new WsActionTester(underTest);
+  private final UuidFactory uuidFactory = UuidFactoryFast.getInstance();
 
   @Test
   public void ws_is_public() {
@@ -435,7 +438,7 @@ public class ShowActionTest {
 
     RuleDefinitionDto rule = newRuleWithoutSection(SECURITY_HOTSPOT,
       r -> r.setTemplateUuid("123")
-        .addRuleDescriptionSectionDto(createDefaultRuleDescriptionSection(description))
+        .addRuleDescriptionSectionDto(createDefaultRuleDescriptionSection(uuidFactory.create(), description))
         .setDescriptionFormat(MARKDOWN));
 
     IssueDto hotspot = dbTester.issues().insertHotspot(rule, project, file);
@@ -454,7 +457,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"));
+    RuleDefinitionDto rule = newRuleWithoutSection(SECURITY_HOTSPOT, r -> r.setTemplateUuid("123"));
 
     IssueDto hotspot = dbTester.issues().insertHotspot(rule, project, file);
     mockChangelogAndCommentsFormattingContext();
index cfc87dc6e5f1855f55258b9a5cc73000b24a7572..2954b7b8c8c97fb9ddd2e919f9ac73a735f07007 100644 (file)
@@ -40,6 +40,7 @@ import org.sonar.api.rules.RuleType;
 import org.sonar.api.server.ws.WebService;
 import org.sonar.api.utils.Durations;
 import org.sonar.api.utils.System2;
+import org.sonar.core.util.UuidFactoryFast;
 import org.sonar.core.util.Uuids;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
@@ -118,6 +119,7 @@ import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_SINCE_LEAK_
 
 public class SearchActionTest {
 
+  private final UuidFactoryFast uuidFactory = UuidFactoryFast.getInstance();
   @Rule
   public UserSessionRule userSession = standalone();
   @Rule
@@ -1442,7 +1444,7 @@ public class SearchActionTest {
   private RuleDto newIssueRule() {
     RuleDto rule = RuleTesting.newXooX1()
       .setName("Rule name")
-      .addOrReplaceRuleDescriptionSectionDto(createDefaultRuleDescriptionSection("Rule desc"))
+      .addOrReplaceRuleDescriptionSectionDto(createDefaultRuleDescriptionSection(uuidFactory.create(), "Rule desc"))
       .setStatus(RuleStatus.READY);
     db.rules().insert(rule.getDefinition());
     return rule;
@@ -1451,7 +1453,7 @@ public class SearchActionTest {
   private RuleDto newHotspotRule() {
     RuleDto rule = RuleTesting.newXooX2()
       .setName("Rule name")
-      .addOrReplaceRuleDescriptionSectionDto(createDefaultRuleDescriptionSection("Rule desc"))
+      .addOrReplaceRuleDescriptionSectionDto(createDefaultRuleDescriptionSection(uuidFactory.create(), "Rule desc"))
       .setStatus(RuleStatus.READY)
       .setType(SECURITY_HOTSPOT_VALUE);
     db.rules().insert(rule.getDefinition());
index a4faa701fa1e680d2b38dcec36cab1faae2a1ce6..08e74098b12aed6a3c5290e2b2c33fdca6ccd8a1 100644 (file)
@@ -35,6 +35,7 @@ import org.sonar.api.rule.RuleKey;
 import org.sonar.api.rule.RuleStatus;
 import org.sonar.api.rules.RuleType;
 import org.sonar.api.utils.System2;
+import org.sonar.core.util.UuidFactoryFast;
 import org.sonar.db.DbSession;
 import org.sonar.db.DbTester;
 import org.sonar.db.qualityprofile.ActiveRuleDto;
@@ -142,7 +143,7 @@ public class QProfileBackuperImplTest {
     RuleDefinitionDto templateRule = db.rules().insert(ruleDefinitionDto -> ruleDefinitionDto
       .setIsTemplate(true));
     RuleDefinitionDto rule = db.rules().insert(ruleDefinitionDto -> ruleDefinitionDto
-      .addOrReplaceRuleDescriptionSectionDto(createDefaultRuleDescriptionSection("custom rule description"))
+      .addOrReplaceRuleDescriptionSectionDto(createDefaultRuleDescriptionSection(UuidFactoryFast.getInstance().create(), "custom rule description"))
       .setName("custom rule name")
       .setStatus(RuleStatus.READY)
       .setTemplateUuid(templateRule.getUuid()));
index 63627fd5556f529d876c2f19e59ace07d657a07b..082fe1de06ac18544993513a0bd7df8f90d9c9ae 100644 (file)
@@ -289,7 +289,7 @@ public class RuleCreatorTest {
       .setRuleKey(key)
       .setStatus(RuleStatus.REMOVED)
       .setName("Old name")
-      .addOrReplaceRuleDescriptionSectionDto(createDefaultRuleDescriptionSection("Old description"))
+      .addOrReplaceRuleDescriptionSectionDto(createDefaultRuleDescriptionSection(uuidFactory.create(), "Old description"))
       .setDescriptionFormat(Format.MARKDOWN)
       .setSeverity(Severity.INFO);
     dbTester.rules().insert(rule.getDefinition());
@@ -329,7 +329,7 @@ public class RuleCreatorTest {
       .setRuleKey(key)
       .setStatus(RuleStatus.REMOVED)
       .setName("Old name")
-      .addOrReplaceRuleDescriptionSectionDto(createDefaultRuleDescriptionSection("Old description"))
+      .addOrReplaceRuleDescriptionSectionDto(createDefaultRuleDescriptionSection(uuidFactory.create(), "Old description"))
       .setSeverity(Severity.INFO);
     dbTester.rules().insert(rule.getDefinition());
     dbTester.rules().insertRuleParam(rule.getDefinition(), param -> param.setDefaultValue("a.*"));
index 0c2fbae7dc7dcf51a767cb92fe2e0d2a34edebc5..8cce0607d88d3028ae37ecdd5c65ea2048ff614c 100644 (file)
@@ -35,6 +35,7 @@ import org.sonar.api.rule.Severity;
 import org.sonar.api.server.debt.DebtRemediationFunction;
 import org.sonar.api.server.debt.internal.DefaultDebtRemediationFunction;
 import org.sonar.api.utils.System2;
+import org.sonar.core.util.UuidFactoryFast;
 import org.sonar.db.DbSession;
 import org.sonar.db.DbTester;
 import org.sonar.db.qualityprofile.ActiveRuleDto;
@@ -58,6 +59,7 @@ 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.newCustomRule;
 import static org.sonar.db.rule.RuleTesting.newRule;
 import static org.sonar.server.rule.RuleUpdate.createForCustomRule;
 import static org.sonar.server.rule.RuleUpdate.createForPluginRule;
@@ -68,7 +70,6 @@ public class RuleUpdaterTest {
 
   private final System2 system2 = new TestSystem2().setNow(Instant.now().toEpochMilli());
 
-
   @Rule
   public UserSessionRule userSessionRule = UserSessionRule.standalone();
 
@@ -82,7 +83,8 @@ public class RuleUpdaterTest {
   private final RuleIndexer ruleIndexer = new RuleIndexer(es.client(), db.getDbClient());
   private final DbSession dbSession = db.getSession();
 
-  private final RuleUpdater underTest = new RuleUpdater(db.getDbClient(), ruleIndexer, system2);
+  private final UuidFactoryFast uuidFactory = UuidFactoryFast.getInstance();
+  private final RuleUpdater underTest = new RuleUpdater(db.getDbClient(), ruleIndexer, uuidFactory, system2);
 
   @Test
   public void do_not_update_rule_with_removed_status() {
@@ -340,9 +342,9 @@ public class RuleUpdaterTest {
     db.rules().insertRuleParam(templateRule.getDefinition(), param -> param.setName("format").setType("STRING").setDescription("Format"));
 
     // Create custom rule
-    RuleDefinitionDto customRule = RuleTesting.newCustomRule(templateRule)
+    RuleDefinitionDto customRule = newCustomRule(templateRule)
       .setName("Old name")
-      .addOrReplaceRuleDescriptionSectionDto(createDefaultRuleDescriptionSection("Old description"))
+      .addOrReplaceRuleDescriptionSectionDto(createDefaultRuleDescriptionSection(uuidFactory.create(), "Old description"))
       .setSeverity(Severity.MINOR)
       .setStatus(RuleStatus.BETA)
       .getDefinition();
@@ -388,12 +390,11 @@ public class RuleUpdaterTest {
     db.rules().insertRuleParam(templateRule.getDefinition(), param -> param.setName("regex").setType("STRING").setDescription("Reg ex").setDefaultValue(null));
 
     // Create custom rule
-    RuleDefinitionDto customRule = RuleTesting.newCustomRule(templateRule)
+    RuleDefinitionDto customRule = newCustomRule(templateRule.getDefinition())
       .setName("Old name")
-      .addRuleDescriptionSectionDto(createDefaultRuleDescriptionSection("Old description"))
+      .addOrReplaceRuleDescriptionSectionDto(createDefaultRuleDescriptionSection(uuidFactory.create(), "Old description"))
       .setSeverity(Severity.MINOR)
-      .setStatus(RuleStatus.BETA)
-      .getDefinition();
+      .setStatus(RuleStatus.BETA);
     db.rules().insert(customRule);
     db.rules().insertRuleParam(customRule, param -> param.setName("regex").setType("STRING").setDescription("Reg ex").setDefaultValue(null));
 
@@ -425,7 +426,7 @@ public class RuleUpdaterTest {
     db.rules().insertRuleParam(templateRuleDefinition, param -> param.setName("message").setType("STRING").setDescription("message"));
 
     // Create custom rule
-    RuleDefinitionDto customRule = RuleTesting.newCustomRule(templateRule)
+    RuleDefinitionDto customRule = newCustomRule(templateRule)
       .setSeverity(Severity.MAJOR)
       .setLanguage("xoo")
       .getDefinition();
@@ -490,7 +491,7 @@ public class RuleUpdaterTest {
     db.rules().insert(templateRule);
 
     // Create custom rule
-    RuleDefinitionDto customRule = RuleTesting.newCustomRule(templateRule);
+    RuleDefinitionDto customRule = newCustomRule(templateRule);
     db.rules().insert(customRule);
 
     dbSession.commit();
@@ -514,7 +515,7 @@ public class RuleUpdaterTest {
     db.rules().insert(templateRule.getDefinition());
 
     // Create custom rule
-    RuleDto customRule = RuleTesting.newCustomRule(templateRule);
+    RuleDto customRule = newCustomRule(templateRule);
     db.rules().insert(customRule.getDefinition());
 
     dbSession.commit();
index dca7895a18941e7d8257a2f450545a1485cbe290..9c2f4bcbd709e148a56d7f040bbf89b69f84ae01 100644 (file)
@@ -142,7 +142,7 @@ public class CreateActionTest {
       .setRuleKey("MY_CUSTOM")
       .setStatus(RuleStatus.REMOVED)
       .setName("My custom rule")
-      .addOrReplaceRuleDescriptionSectionDto(createDefaultRuleDescriptionSection("Description"))
+      .addOrReplaceRuleDescriptionSectionDto(createDefaultRuleDescriptionSection(uuidFactory.create(), "Description"))
       .setDescriptionFormat(RuleDto.Format.MARKDOWN)
       .setSeverity(Severity.MAJOR);
     db.rules().insert(customRule);
index 3b6db659ba20f96fa000ab61e2995462460e154c..3482799dea2268bf5e14dcdc4349920920376ffa 100644 (file)
@@ -34,6 +34,8 @@ import org.sonar.api.rules.RuleType;
 import org.sonar.api.server.debt.DebtRemediationFunction;
 import org.sonar.api.server.ws.WebService;
 import org.sonar.api.utils.System2;
+import org.sonar.core.util.UuidFactory;
+import org.sonar.core.util.UuidFactoryFast;
 import org.sonar.core.util.stream.MoreCollectors;
 import org.sonar.db.DbTester;
 import org.sonar.db.qualityprofile.ActiveRuleParamDto;
@@ -116,6 +118,7 @@ public class SearchActionTest {
   private final QProfileRules qProfileRules = new QProfileRulesImpl(db.getDbClient(), ruleActivator, ruleIndex, activeRuleIndexer,
     qualityProfileChangeEventService);
   private final WsActionTester ws = new WsActionTester(underTest);
+  private final UuidFactory uuidFactory = UuidFactoryFast.getInstance();
 
   @Before
   public void before() {
@@ -234,7 +237,7 @@ public class SearchActionTest {
   @Test
   public void filter_by_rule_description() {
     RuleDefinitionDto rule1 = db.rules()
-      .insert(r1 -> r1.addOrReplaceRuleDescriptionSectionDto(createDefaultRuleDescriptionSection("This is the <bold>best</bold> rule now&amp;for<b>ever</b>")));
+      .insert(r1 -> r1.addOrReplaceRuleDescriptionSectionDto(createDefaultRuleDescriptionSection(uuidFactory.create(), "This is the <bold>best</bold> rule now&amp;for<b>ever</b>")));
     RuleDefinitionDto rule2 = db.rules().insert(r1 -> r1.setName("Some other stuff"));
     indexRules();
 
@@ -246,9 +249,9 @@ 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")
-      .addOrReplaceRuleDescriptionSectionDto(createDefaultRuleDescriptionSection("This is a good rule")));
+      .addOrReplaceRuleDescriptionSectionDto(createDefaultRuleDescriptionSection(uuidFactory.create(), "This is a good rule")));
     RuleDefinitionDto rule2 = db.rules().insert(r1 -> r1.setName("Another thing")
-      .addOrReplaceRuleDescriptionSectionDto(createDefaultRuleDescriptionSection("Another thing")));
+      .addOrReplaceRuleDescriptionSectionDto(createDefaultRuleDescriptionSection(uuidFactory.create(), "Another thing")));
     indexRules();
 
     verify(r -> r.setParam("q", "Best good"), rule1);
@@ -931,8 +934,7 @@ public class SearchActionTest {
   private void verify(Consumer<TestRequest> requestPopulator, RuleDefinitionDto... expectedRules) {
     TestRequest request = ws.newRequest();
     requestPopulator.accept(request);
-    Rules.SearchResponse response = request
-      .executeProtobuf(Rules.SearchResponse.class);
+    Rules.SearchResponse response = request.executeProtobuf(Rules.SearchResponse.class);
 
     assertThat(response.getP()).isOne();
     RuleKey[] expectedRuleKeys = stream(expectedRules).map(RuleDefinitionDto::getKey).collect(MoreCollectors.toList()).toArray(new RuleKey[0]);
index 41ba8fab653aaefc513a0913a693573053385c3e..77e97e592b5cb32b73b4aa88c1c83a3d7ca0a198 100644 (file)
@@ -27,6 +27,8 @@ import org.sonar.api.rule.RuleKey;
 import org.sonar.api.rule.Severity;
 import org.sonar.api.rules.RuleType;
 import org.sonar.api.server.ws.WebService;
+import org.sonar.core.util.UuidFactory;
+import org.sonar.core.util.UuidFactoryFast;
 import org.sonar.db.DbTester;
 import org.sonar.db.qualityprofile.ActiveRuleDto;
 import org.sonar.db.qualityprofile.ActiveRuleParamDto;
@@ -70,6 +72,7 @@ public class ShowActionTest {
   @org.junit.Rule
   public DbTester db = DbTester.create();
 
+  private final UuidFactory uuidFactory = UuidFactoryFast.getInstance();
   private final MacroInterpreter macroInterpreter = mock(MacroInterpreter.class);
   private final Languages languages = new Languages(newLanguage("xoo", "Xoo"));
   private final WsActionTester ws = new WsActionTester(
@@ -285,7 +288,7 @@ public class ShowActionTest {
     db.rules().insert(templateRule.getDefinition());
     // Custom rule
     RuleDefinitionDto customRule = newCustomRule(templateRule.getDefinition())
-      .addOrReplaceRuleDescriptionSectionDto(createDefaultRuleDescriptionSection("<div>line1\nline2</div>"))
+      .addOrReplaceRuleDescriptionSectionDto(createDefaultRuleDescriptionSection(uuidFactory.create(), "<div>line1\nline2</div>"))
       .setDescriptionFormat(MARKDOWN);
     db.rules().insert(customRule);
     doReturn("&lt;div&gt;line1<br/>line2&lt;/div&gt;").when(macroInterpreter).interpret("<div>line1\nline2</div>");
@@ -343,7 +346,7 @@ public class ShowActionTest {
       .setIsExternal(true)
       .setIsAdHoc(true)
       .setName("predefined name")
-      .addOrReplaceRuleDescriptionSectionDto(createDefaultRuleDescriptionSection("<div>predefined desc</div>"))
+      .addOrReplaceRuleDescriptionSectionDto(createDefaultRuleDescriptionSection(uuidFactory.create(), "<div>predefined desc</div>"))
       .setSeverity(Severity.BLOCKER)
       .setType(RuleType.VULNERABILITY));
     RuleMetadataDto metadata = db.rules().insertOrUpdateMetadata(externalRule, m -> m
index 8be2f583b11e838c8c016a581b98c4492c9914c9..671f4e7a5d2351f2e0f95e6237829e57dbcb1bb4 100644 (file)
@@ -26,9 +26,11 @@ import org.sonar.api.rule.RuleKey;
 import org.sonar.api.rule.RuleStatus;
 import org.sonar.api.rule.Severity;
 import org.sonar.api.utils.System2;
+import org.sonar.core.util.UuidFactoryFast;
 import org.sonar.db.DbClient;
 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.user.UserDto;
 import org.sonar.server.es.EsClient;
@@ -68,7 +70,6 @@ public class UpdateActionTest {
 
   private static final long PAST = 10000L;
 
-
   @Rule
   public DbTester db = DbTester.create();
 
@@ -84,7 +85,9 @@ public class UpdateActionTest {
   private Languages languages = new Languages();
   private RuleMapper mapper = new RuleMapper(languages, createMacroInterpreter());
   private RuleIndexer ruleIndexer = new RuleIndexer(esClient, dbClient);
-  private RuleUpdater ruleUpdater = new RuleUpdater(dbClient, ruleIndexer, System2.INSTANCE);
+  private UuidFactoryFast uuidFactory = UuidFactoryFast.getInstance();
+
+  private RuleUpdater ruleUpdater = new RuleUpdater(dbClient, ruleIndexer, uuidFactory, System2.INSTANCE);
   private WsAction underTest = new UpdateAction(dbClient, ruleUpdater, mapper, userSession, new RuleWsSupport(db.getDbClient(), userSession));
   private WsActionTester ws = new WsActionTester(underTest);
 
@@ -108,7 +111,7 @@ public class UpdateActionTest {
     RuleDefinitionDto customRule = db.rules().insert(
       r -> r.setRuleKey(RuleKey.of("java", "MY_CUSTOM")),
       r -> r.setName("Old custom"),
-      r -> r.addOrReplaceRuleDescriptionSectionDto(createDefaultRuleDescriptionSection("Old description")),
+      r -> r.addOrReplaceRuleDescriptionSectionDto(createRuleDescriptionSectionDto()),
       r -> r.setSeverity(Severity.MINOR),
       r -> r.setStatus(RuleStatus.BETA),
       r -> r.setTemplateUuid(templateRule.getUuid()),
@@ -244,7 +247,7 @@ public class UpdateActionTest {
     RuleDefinitionDto customRule = db.rules().insert(
       r -> r.setRuleKey(RuleKey.of("java", "MY_CUSTOM")),
       r -> r.setName("Old custom"),
-      r -> r.addOrReplaceRuleDescriptionSectionDto(createDefaultRuleDescriptionSection("Old description")),
+      r -> r.addOrReplaceRuleDescriptionSectionDto(createRuleDescriptionSectionDto()),
       r -> r.setTemplateUuid(templateRule.getUuid()),
       r -> r.setCreatedAt(PAST),
       r -> r.setUpdatedAt(PAST));
@@ -300,6 +303,10 @@ public class UpdateActionTest {
       .addPermission(ADMINISTER_QUALITY_PROFILES);
   }
 
+  private RuleDescriptionSectionDto createRuleDescriptionSectionDto() {
+    return createDefaultRuleDescriptionSection(uuidFactory.create(), "Old description");
+  }
+
   private static MacroInterpreter createMacroInterpreter() {
     MacroInterpreter macroInterpreter = mock(MacroInterpreter.class);
     doAnswer(returnsFirstArg()).when(macroInterpreter).interpret(anyString());