*/
package org.sonar.db.rule;
+import java.util.Objects;
import java.util.StringJoiner;
import static org.sonar.api.utils.Preconditions.checkArgument;
.toString();
}
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ RuleDescriptionSectionDto that = (RuleDescriptionSectionDto) o;
+ return Objects.equals(uuid, that.uuid) && Objects.equals(key, that.key) && Objects.equals(description, that.description);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(uuid, key, description);
+ }
+
public static final class RuleDescriptionSectionDtoBuilder {
private String uuid;
private String key = null;
*/
package org.sonar.db.rule;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Optional;
import java.util.Set;
import javax.annotation.CheckForNull;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.rule.RuleStatus;
import org.sonar.api.rules.RuleType;
+import static org.sonar.db.rule.RuleDescriptionSectionDto.DEFAULT_KEY;
+
public class RuleForIndexingDto {
private String uuid;
private String repository;
private String pluginRuleKey;
private String name;
- private String description;
private RuleDto.Format descriptionFormat;
private Integer severity;
private RuleStatus status;
private long createdAt;
private long updatedAt;
+ private Set<RuleDescriptionSectionDto> ruleDescriptionSectionsDtos = new HashSet<>();
+
public RuleForIndexingDto() {
// nothing to do here
}
return repository;
}
+ public void setRepository(String repository) {
+ this.repository = repository;
+ }
+
public String getPluginRuleKey() {
return pluginRuleKey;
}
- public String getName() {
- return name;
+ public void setPluginRuleKey(String pluginRuleKey) {
+ this.pluginRuleKey = pluginRuleKey;
}
- public String getDescription() {
- return description;
+ public String getName() {
+ return name;
}
public RuleDto.Format getDescriptionFormat() {
return descriptionFormat;
}
+ public void setDescriptionFormat(RuleDto.Format descriptionFormat) {
+ this.descriptionFormat = descriptionFormat;
+ }
+
public Integer getSeverity() {
return severity;
}
public RuleKey getRuleKey() {
return RuleKey.of(repository, pluginRuleKey);
}
+
+ public Set<RuleDescriptionSectionDto> getRuleDescriptionSectionsDtos() {
+ return ruleDescriptionSectionsDtos;
+ }
+
+ public void setRuleDescriptionSectionsDtos(Set<RuleDescriptionSectionDto> ruleDescriptionSectionsDtos) {
+ this.ruleDescriptionSectionsDtos = ruleDescriptionSectionsDtos;
+ }
+
+ private Optional<RuleDescriptionSectionDto> findExistingSectionWithSameKey(String ruleDescriptionSectionKey) {
+ return ruleDescriptionSectionsDtos.stream().filter(section -> section.getKey().equals(ruleDescriptionSectionKey)).findAny();
+ }
+
+ @CheckForNull
+ public RuleDescriptionSectionDto getDefaultRuleDescriptionSectionDto() {
+ return findExistingSectionWithSameKey(DEFAULT_KEY).orElse(null);
+ }
}
<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="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"/>
</resultMap>
+ <resultMap id="ruleForIndexingDtoResultMap" type="org.sonar.db.rule.RuleForIndexingDto">
+ <result property="uuid" column="uuid"/>
+ <result property="repository" column="repository"/>
+ <result property="pluginRuleKey" column="pluginRuleKey"/>
+ <result property="name" column="name"/>
+ <result property="descriptionFormat" column="descriptionFormat"/>
+ <result property="severity" column="severity"/>
+ <result property="status" column="status"/>
+ <result property="isTemplate" column="isTemplate"/>
+ <result property="isExternal" column="isExternal"/>
+ <result property="systemTags" column="systemTags"/>
+ <result property="securityStandards" column="securityStandards"/>
+ <result property="templateRuleKey" column="templateRuleKey"/>
+ <result property="templateRepository" column="templateRepository"/>
+ <result property="internalKey" column="internalKey"/>
+ <result property="language" column="language"/>
+ <result property="type" column="type"/>
+ <result property="createdAt" column="createdAt"/>
+ <result property="updatedAt" column="updatedAt"/>
+ <result property="tags" column="tags"/>
+ <collection property="ruleDescriptionSectionsDtos" 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"/>
</foreach>
</select>
- <select id="selectIndexingRulesByUuids" parameterType="map" resultType="org.sonar.db.rule.RuleForIndexingDto">
+ <select id="selectIndexingRulesByUuids" parameterType="map" resultMap="ruleForIndexingDtoResultMap">
<include refid="sqlSelectIndexingRules"/>
where
<foreach collection="ruleUuids" index="index" item="ruleUuid" open="" separator=" or " close="">
order by r.created_at asc
</select>
- <select id="scrollIndexingRules" resultType="org.sonar.db.rule.RuleForIndexingDto" fetchSize="${_scrollFetchSize}"
- resultSetType="FORWARD_ONLY">
+ <select id="scrollIndexingRules" resultMap="ruleForIndexingDtoResultMap" fetchSize="${_scrollFetchSize}"
+ resultSetType="FORWARD_ONLY" resultOrdered="true">
<include refid="sqlSelectIndexingRules"/>
- order by r.created_at asc
+ order by r.created_at,r.uuid asc
</select>
<sql id="sqlSelectIndexingRules">
r.rule_type as "type",
r.created_at as "createdAt",
r.updated_at as "updatedAt",
- rm.tags as "tags"
+ rm.tags as "tags",
+ rds.uuid as "rds_uuid",
+ rds.kee as "rds_kee",
+ rds.description as "rds_description"
from rules r
left outer join rules t on t.uuid = r.template_uuid
left outer join rules_metadata rm on r.uuid = rm.rule_uuid
+ left outer join rule_desc_sections rds on
+ rds.rule_uuid = r.uuid
</sql>
<select id="selectByQuery" parameterType="map" resultMap="ruleResultMap">
package org.sonar.db.rule;
import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.MoreCollectors;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@Test
public void scrollIndexingRules() {
Accumulator<RuleForIndexingDto> accumulator = new Accumulator<>();
- RuleDefinitionDto r1 = db.rules().insert();
+ RuleDescriptionSectionDto ruleDescriptionSectionDto = RuleDescriptionSectionDto.builder()
+ .key("DESC")
+ .uuid("uuid")
+ .description("my description")
+ .build();
+ RuleDefinitionDto r1 = db.rules().insert(r -> {
+ r.addRuleDescriptionSectionDto(ruleDescriptionSectionDto);
+ });
RuleDefinitionDto r2 = db.rules().insert(r -> r.setIsExternal(true));
underTest.scrollIndexingRules(db.getSession(), accumulator);
assertThat(firstRule.getRepository()).isEqualTo(r1.getRepositoryKey());
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.getRuleDescriptionSectionsDtos().stream()
+ .filter(s -> s.getKey().equals(ruleDescriptionSectionDto.getKey()))
+ .collect(MoreCollectors.onlyElement()))
+ .isEqualTo(ruleDescriptionSectionDto);
assertThat(firstRule.getDescriptionFormat()).isEqualTo(r1.getDescriptionFormat());
assertThat(firstRule.getSeverity()).isEqualTo(r1.getSeverity());
assertThat(firstRule.getStatus()).isEqualTo(r1.getStatus());
assertThat(firstRule.getRepository()).isEqualTo(r1.getRepositoryKey());
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.getRuleDescriptionSectionsDtos()).isEqualTo(r1.getRuleDescriptionSectionDtos());
assertThat(firstRule.getDescriptionFormat()).isEqualTo(r1.getDescriptionFormat());
assertThat(firstRule.getSeverity()).isEqualTo(r1.getSeverity());
assertThat(firstRule.getSeverityAsString()).isEqualTo(SeverityUtil.getSeverityFromOrdinal(r1.getSeverity()));
}
public static HotspotRuleDescription from(RuleForIndexingDto dto) {
- return from(dto.getDescription());
+ return from(RuleDescriptionFormatter.getDescriptionAsHtml(dto));
}
private static HotspotRuleDescription from(@Nullable String description) {
import org.sonar.db.rule.RuleDefinitionDto;
import org.sonar.db.rule.RuleDescriptionSectionDto;
import org.sonar.db.rule.RuleDto;
+import org.sonar.db.rule.RuleForIndexingDto;
import org.sonar.markdown.Markdown;
import static com.google.common.collect.MoreCollectors.toOptional;
return null;
}
Collection<RuleDescriptionSectionDto> ruleDescriptionSectionDtos = ruleDefinitionDto.getRuleDescriptionSectionDtos();
+ return retrieveDescription(ruleDescriptionSectionDtos, ruleDefinitionDto.getRuleKey(), ruleDefinitionDto.getDescriptionFormat());
+ }
+
+ public static String getDescriptionAsHtml(RuleForIndexingDto ruleForIndexingDto) {
+ if (ruleForIndexingDto.getDescriptionFormat() == null) {
+ return null;
+ }
+ Collection<RuleDescriptionSectionDto> ruleDescriptionSectionDtos = ruleForIndexingDto.getRuleDescriptionSectionsDtos();
+ return retrieveDescription(ruleDescriptionSectionDtos, ruleForIndexingDto.getRuleKey().toString(), ruleForIndexingDto.getDescriptionFormat());
+ }
+
+ private static String retrieveDescription(Collection<RuleDescriptionSectionDto> ruleDescriptionSectionDtos,
+ String ruleKey, RuleDto.Format descriptionFormat) {
Optional<RuleDescriptionSectionDto> ruleDescriptionSectionDto = findDefaultDescription(ruleDescriptionSectionDtos);
return ruleDescriptionSectionDto
- .map(ruleDescriptionSection -> toHtml(ruleDefinitionDto, ruleDescriptionSection))
+ .map(ruleDescriptionSection -> toHtml(ruleKey, descriptionFormat, ruleDescriptionSection))
.orElse(null);
}
+
private static Optional<RuleDescriptionSectionDto> findDefaultDescription(Collection<RuleDescriptionSectionDto> 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) {
+ private static String toHtml(String ruleKey, RuleDto.Format descriptionFormat, RuleDescriptionSectionDto ruleDescriptionSectionDto) {
+ RuleDto.Format nonNullDescriptionFormat = Objects.requireNonNull(descriptionFormat,
+ "Rule " + descriptionFormat + " contains section(s) but has no format set");
+ switch (nonNullDescriptionFormat) {
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()));
+ throw new IllegalStateException(format("Rule description section format '%s' is unknown for rule key '%s'", descriptionFormat, ruleKey));
}
}
import org.sonar.db.rule.RuleForIndexingDto;
import org.sonar.markdown.Markdown;
import org.sonar.server.es.BaseDoc;
+import org.sonar.server.rule.RuleDescriptionFormatter;
import org.sonar.server.security.SecurityStandards;
import org.sonar.server.security.SecurityStandards.SQCategory;
ruleDoc.setTemplateKey(null);
}
- if (dto.getDescription() != null && dto.getDescriptionFormat() != null) {
- if (RuleDto.Format.HTML == dto.getDescriptionFormat()) {
- ruleDoc.setHtmlDescription(dto.getDescription());
- } else {
- ruleDoc.setHtmlDescription(Markdown.convertToHtml(dto.getDescription()));
- }
- }
+ String descriptionAsHtml = RuleDescriptionFormatter.getDescriptionAsHtml(dto);
+ ruleDoc.setHtmlDescription(descriptionAsHtml);
return ruleDoc;
}
}
*/
package org.sonar.server.rule;
+import com.google.common.collect.Sets;
+import java.util.Collections;
+import java.util.Set;
+import org.jetbrains.annotations.NotNull;
import org.junit.Test;
import org.sonar.db.rule.RuleDefinitionDto;
import org.sonar.db.rule.RuleDescriptionSectionDto;
import org.sonar.db.rule.RuleDto;
+import org.sonar.db.rule.RuleForIndexingDto;
import static org.assertj.core.api.Assertions.assertThat;
import static org.sonar.db.rule.RuleDescriptionSectionDto.createDefaultRuleDescriptionSection;
String result = RuleDescriptionFormatter.getDescriptionAsHtml(rule);
assertThat(result).isNull();
}
+
+ @Test
+ public void getHtmlDescriptionForRuleForIndexingDtoAsIs() {
+ Set<RuleDescriptionSectionDto> sectionsDtos = Sets.newHashSet(
+ createDefaultRuleDescriptionSection("uuid", HTML_SECTION.getDescription()));
+ RuleForIndexingDto rule = createRuleForIndexingDto(sectionsDtos, RuleDto.Format.HTML);
+ String html = RuleDescriptionFormatter.getDescriptionAsHtml(rule);
+ assertThat(html).isEqualTo(HTML_SECTION.getDescription());
+ }
+
+ @Test
+ public void handleEmptyDescriptionForRuleForIndexingDto() {
+ RuleForIndexingDto rule = createRuleForIndexingDto(Collections.emptySet(), RuleDto.Format.HTML);
+ String result = RuleDescriptionFormatter.getDescriptionAsHtml(rule);
+ assertThat(result).isNull();
+ }
+
+ @Test
+ public void handleNullDescriptionFormatForRuleForIndexingDto() {
+ Set<RuleDescriptionSectionDto> sectionsDtos = Sets.newHashSet(
+ createDefaultRuleDescriptionSection("uuid", HTML_SECTION.getDescription()));
+ RuleForIndexingDto rule = createRuleForIndexingDto(sectionsDtos, null);
+ String result = RuleDescriptionFormatter.getDescriptionAsHtml(rule);
+ assertThat(result).isNull();
+ }
+
+ @NotNull
+ private static RuleForIndexingDto createRuleForIndexingDto(Set<RuleDescriptionSectionDto> sectionsDtos, RuleDto.Format format) {
+ RuleForIndexingDto rule = new RuleForIndexingDto();
+ rule.setRuleDescriptionSectionsDtos(sectionsDtos);
+ rule.setDescriptionFormat(format);
+ rule.setRepository("repository");
+ rule.setPluginRuleKey("pluginKey");
+ return rule;
+ }
}