"internal_component_props",
"internal_properties",
"issues",
+ "issues_impacts",
"issue_changes",
"live_measures",
"metrics",
"report_subscriptions",
"rules",
"rule_desc_sections",
+ "rules_default_impacts",
"rules_parameters",
"rules_profiles",
"rule_repositories",
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
+import org.sonar.api.issue.impact.Severity;
+import org.sonar.api.issue.impact.SoftwareQuality;
import org.sonar.api.rule.RuleKey;
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.Pagination;
@Before
public void setup() {
+ int i = db.countSql(db.getSession(), "select count(1) from rules_default_impacts");
+
db.rules().insert(RULE.setIsExternal(true));
projectDto = db.components().insertPrivateProject(t -> t.setUuid(PROJECT_UUID).setKey(PROJECT_KEY)).getMainBranchComponent();
db.components().insertComponent(newFileDto(projectDto).setUuid(FILE_UUID).setKey(FILE_KEY));
IssueDto issue = underTest.selectOrFailByKey(db.getSession(), ISSUE_KEY1);
assertThat(issue).usingRecursiveComparison()
- .ignoringFields("filePath", "issueCreationDate", "issueUpdateDate", "issueCloseDate", "cleanCodeAttribute")
+ .ignoringFields("filePath", "issueCreationDate", "issueUpdateDate", "issueCloseDate", "cleanCodeAttribute", "impacts", "ruleDefaultImpacts")
.isEqualTo(expected);
assertThat(issue.parseMessageFormattings()).isEqualTo(MESSAGE_FORMATTING);
assertThat(issue.getIssueCreationDate()).isNotNull();
assertThat(issue.getRule()).isEqualTo(RULE.getRuleKey());
assertThat(issue.getCleanCodeAttribute()).isEqualTo(RULE.getCleanCodeAttribute());
assertThat(issue.parseLocations()).isNull();
+ assertThat(issue.getImpacts())
+ .extracting(ImpactDto::getSeverity, ImpactDto::getSoftwareQuality)
+ .containsExactlyInAnyOrder(
+ tuple(Severity.MEDIUM, SoftwareQuality.RELIABILITY),
+ tuple(Severity.LOW, SoftwareQuality.SECURITY));
+
+ assertThat(issue.getEffectiveImpacts())
+ // impacts from rule
+ .containsEntry(SoftwareQuality.MAINTAINABILITY, Severity.HIGH)
+ // impacts from issue
+ .containsEntry(SoftwareQuality.RELIABILITY, Severity.MEDIUM)
+ .containsEntry(SoftwareQuality.SECURITY, Severity.LOW);
}
@Test
prepareTables();
List<IssueDto> issues = underTest.selectByKeys(db.getSession(), asList("I1", "I2", "I3"));
- // results are not ordered, so do not use "containsExactly"
- assertThat(issues).extracting("key").containsOnly("I1", "I2");
+
+ assertThat(issues).extracting(IssueDto::getKey).containsExactlyInAnyOrder("I1", "I2");
+ assertThat(issues).filteredOn(issueDto -> issueDto.getKey().equals("I1"))
+ .extracting(IssueDto::getImpacts)
+ .flatMap(issueImpactDtos -> issueImpactDtos)
+ .extracting(ImpactDto::getSeverity, ImpactDto::getSoftwareQuality)
+ .containsExactlyInAnyOrder(
+ tuple(Severity.MEDIUM, SoftwareQuality.RELIABILITY),
+ tuple(Severity.LOW, SoftwareQuality.SECURITY));
}
@Test
assertThat(issue1.getOptionalRuleDescriptionContextKey()).contains(TEST_CONTEXT_KEY);
}
+ @Test
+ public void insert_shouldInsertBatchIssuesWithImpacts() {
+ ImpactDto impact1 = new ImpactDto()
+ .setUuid(UuidFactoryFast.getInstance().create())
+ .setSoftwareQuality(SoftwareQuality.MAINTAINABILITY)
+ .setSeverity(Severity.HIGH);
+ ImpactDto impact2 = new ImpactDto()
+ .setUuid(UuidFactoryFast.getInstance().create())
+ .setSoftwareQuality(SoftwareQuality.SECURITY)
+ .setSeverity(Severity.LOW);
+ IssueDto issue1 = createIssueWithKey(ISSUE_KEY1)
+ .addImpact(impact1)
+ .addImpact(impact2);
+ IssueDto issue2 = createIssueWithKey(ISSUE_KEY2);
+ underTest.insert(db.getSession(), issue1, issue2);
+
+ List<IssueDto> issueDtos = underTest.selectByKeys(db.getSession(), Set.of(ISSUE_KEY1, ISSUE_KEY2));
+ assertThat(issueDtos)
+ .extracting(IssueDto::getKey)
+ .containsExactlyInAnyOrder(ISSUE_KEY1, ISSUE_KEY2);
+ assertThat(issueDtos).filteredOn(issueDto -> issueDto.getKey().equals(ISSUE_KEY1))
+ .flatExtracting(IssueDto::getImpacts)
+ .containsExactlyInAnyOrder(impact1, impact2)
+ .doesNotContainNull();
+ }
+
@Test
public void update_whenUpdatingRuleDescriptionContextKeyToNull_returnsEmptyContextKey() {
IssueDto issue = createIssueWithKey(ISSUE_KEY1).setRuleDescriptionContextKey(TEST_CONTEXT_KEY);
.setMessage("the message")
.setRuleUuid(RULE.getUuid())
.setComponentUuid(FILE_UUID)
- .setProjectUuid(PROJECT_UUID));
+ .setProjectUuid(PROJECT_UUID)
+ .addImpact(newIssueImpact(SoftwareQuality.RELIABILITY, Severity.MEDIUM))
+ .addImpact(newIssueImpact(SoftwareQuality.SECURITY, Severity.LOW)));
underTest.insert(db.getSession(), newIssueDto(ISSUE_KEY2)
.setRuleUuid(RULE.getUuid())
.setComponentUuid(FILE_UUID)
db.getSession().commit();
}
+ private static ImpactDto newIssueImpact(SoftwareQuality softwareQuality, Severity severity) {
+ return new ImpactDto()
+ .setUuid(UuidFactoryFast.getInstance().create())
+ .setSoftwareQuality(softwareQuality)
+ .setSeverity(severity);
+ }
+
private static RuleType randomRuleTypeExceptHotspot() {
return RULE_TYPES_EXCEPT_HOTSPOT[nextInt(RULE_TYPES_EXCEPT_HOTSPOT.length)];
}
import org.junit.runner.RunWith;
import org.sonar.api.impl.utils.AlwaysIncreasingSystem2;
import org.sonar.api.issue.Issue;
+import org.sonar.api.issue.impact.Severity;
+import org.sonar.api.issue.impact.SoftwareQuality;
import org.sonar.api.rules.RuleType;
import org.sonar.api.utils.DateUtils;
import org.sonar.api.utils.System2;
underTest.scrollClosedByComponentUuid(component.uuid(), NO_FILTERING_ON_CLOSE_DATE, resultHandler);
assertThat(resultHandler.issues)
- .hasSize(4)
+ .hasSize(1)
.extracting(IssueDto::getKey, t -> t.getClosedChangeData().get())
- .containsExactly(
- tuple(issue.getKey(), changes[2].getChangeData()),
- tuple(issue.getKey(), changes[3].getChangeData()),
- tuple(issue.getKey(), changes[0].getChangeData()),
- tuple(issue.getKey(), changes[1].getChangeData()));
+ // we are interested only in the latest closed issue change data
+ .containsExactly(tuple(issue.getKey(), changes[2].getChangeData()));
}
@Test
underTest.scrollClosedByComponentUuid(component.uuid(), NO_FILTERING_ON_CLOSE_DATE, resultHandler);
assertThat(resultHandler.issues)
- .hasSize(3)
+ .hasSize(1)
.extracting(IssueDto::getKey, t -> t.getClosedChangeData().get())
.containsExactly(
- tuple(issue.getKey(), changes[2].getChangeData()),
- tuple(issue.getKey(), changes[3].getChangeData()),
- tuple(issue.getKey(), changes[1].getChangeData()));
+ // we are interested only in the latest closed issue change data
+ tuple(issue.getKey(), changes[2].getChangeData()));
}
private IssueChangeDto insertToClosedDiff(IssueDto issueDto) {
}
@SafeVarargs
- private final IssueDto insertNewClosedIssue(ComponentDto component, RuleType ruleType, Consumer<IssueDto>... consumers) {
+ private IssueDto insertNewClosedIssue(ComponentDto component, RuleType ruleType, Consumer<IssueDto>... consumers) {
RuleDto rule = dbTester.rules().insert(t -> t.setType(ruleType));
return insertNewClosedIssue(component, rule, system2.now(), consumers);
}
}
@SafeVarargs
- private final IssueDto insertNewClosedIssue(ComponentDto component, RuleDto rule, long issueCloseTime, Consumer<IssueDto>... consumers) {
+ private IssueDto insertNewClosedIssue(ComponentDto component, RuleDto rule, long issueCloseTime, Consumer<IssueDto>... consumers) {
IssueDto res = new IssueDto()
.setKee(UuidFactoryFast.getInstance().create())
.setRuleUuid(rule.getUuid())
.setComponentUuid(component.uuid())
.setProjectUuid(component.branchUuid())
.setStatus(Issue.STATUS_CLOSED)
- .setIssueCloseTime(issueCloseTime);
+ .setIssueCloseTime(issueCloseTime)
+ .addImpact(new ImpactDto()
+ .setUuid(UuidFactoryFast.getInstance().create())
+ .setSeverity(Severity.HIGH)
+ .setSoftwareQuality(SoftwareQuality.MAINTAINABILITY))
+ .addImpact(new ImpactDto()
+ .setUuid(UuidFactoryFast.getInstance().create())
+ .setSeverity(Severity.LOW)
+ .setSoftwareQuality(SoftwareQuality.SECURITY));
Arrays.asList(consumers).forEach(c -> c.accept(res));
- underTest.insert(res);
+ dbTester.getDbClient().issueDao().insert(dbSession, res);
dbSession.commit();
return res;
}
import org.junit.Test;
import org.junit.runner.RunWith;
import org.sonar.api.impl.utils.AlwaysIncreasingSystem2;
+import org.sonar.api.issue.impact.Severity;
+import org.sonar.api.issue.impact.SoftwareQuality;
import org.sonar.api.utils.System2;
import org.sonar.core.util.UuidFactoryFast;
import org.sonar.db.DbTester;
import org.sonar.db.duplication.DuplicationUnitDto;
import org.sonar.db.entity.EntityDto;
import org.sonar.db.issue.AnticipatedTransitionDto;
+import org.sonar.db.issue.ImpactDto;
import org.sonar.db.issue.IssueDto;
import org.sonar.db.metric.MetricDto;
import org.sonar.db.newcodeperiod.NewCodePeriodType;
underTest.deleteIssues(projectOrView.uuid());
assertThat(dbTester.countSql("select count(uuid) from new_code_reference_issues where issue_key in (" +
- String.join(", ", issueKeys) + ")")).isZero();
+ String.join(", ", issueKeys) + ")")).isZero();
}
@Test
@DataProvider
public static Object[] projects() {
- return new Object[]{
+ return new Object[] {
ComponentTesting.newPrivateProjectDto(), ComponentTesting.newPublicProjectDto(),
};
}
@DataProvider
public static Object[] views() {
- return new Object[]{
+ return new Object[] {
ComponentTesting.newPortfolio(), ComponentTesting.newApplication()
};
}
assertThat(db.countRowsOfTable("issue_changes")).isZero();
assertThat(db.countRowsOfTable("new_code_reference_issues")).isZero();
+ assertThat(db.countRowsOfTable("issues_impacts")).isZero();
assertThat(db.select("select kee as \"KEE\" from issues")).extracting(i -> i.get("KEE")).containsOnly(issue1.getKey());
}
import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;
+import java.util.function.Function;
import org.apache.commons.lang.RandomStringUtils;
import org.apache.ibatis.exceptions.PersistenceException;
import org.jetbrains.annotations.NotNull;
import org.junit.Rule;
import org.junit.Test;
+import org.sonar.api.issue.impact.SoftwareQuality;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.rule.RuleStatus;
import org.sonar.api.rule.Severity;
import org.sonar.api.utils.DateUtils;
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.RowNotFoundException;
+import org.sonar.db.issue.ImpactDto;
import org.sonar.db.rule.RuleDto.Scope;
import static com.google.common.collect.Sets.newHashSet;
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.issue.impact.SoftwareQuality.MAINTAINABILITY;
+import static org.sonar.api.issue.impact.SoftwareQuality.RELIABILITY;
+import static org.sonar.api.issue.impact.SoftwareQuality.SECURITY;
import static org.sonar.api.rule.RuleStatus.REMOVED;
public class RuleDaoIT {
@Test
public void selectByKey() {
- RuleDto ruleDto = db.rules().insert();
+ RuleDto ruleDto = db.rules().insert(r -> r.addDefaultImpact(newRuleDefaultImpact(SECURITY, org.sonar.api.issue.impact.Severity.LOW)));
assertThat(underTest.selectByKey(db.getSession(), RuleKey.of("foo", "bar")))
.isEmpty();
RuleDto actualRule = underTest.selectByKey(db.getSession(), ruleDto.getKey()).get();
assertEquals(actualRule, ruleDto);
+
+ assertThat(actualRule.getDefaultImpacts())
+ .extracting(ImpactDto::getSoftwareQuality, ImpactDto::getSeverity)
+ .containsExactlyInAnyOrder(
+ tuple(MAINTAINABILITY, org.sonar.api.issue.impact.Severity.HIGH),
+ tuple(SECURITY, org.sonar.api.issue.impact.Severity.LOW));
}
@Test
assertThat(underTest.selectByKey(db.getSession(), ruleDto.getKey())).isNotEmpty();
}
-
@Test
public void selectByUuid() {
RuleDto ruleDto = db.rules().insert();
@Test
public void selectOrFailByKey_fails_if_rule_not_found() {
- assertThatThrownBy(() -> underTest.selectOrFailByKey(db.getSession(), RuleKey.of("NOT", "FOUND")))
- .isInstanceOf(RowNotFoundException.class)
- .hasMessage("Rule with key 'NOT:FOUND' does not exist");
- }
-
- @Test
- public void selectOrFailDefinitionByKey_fails_if_rule_not_found() {
- assertThatThrownBy(() -> underTest.selectOrFailByKey(db.getSession(), RuleKey.of("NOT", "FOUND")))
+ DbSession session = db.getSession();
+ RuleKey ruleKey = RuleKey.of("NOT", "FOUND");
+ assertThatThrownBy(() -> underTest.selectOrFailByKey(session, ruleKey))
.isInstanceOf(RowNotFoundException.class)
.hasMessage("Rule with key 'NOT:FOUND' does not exist");
}
@Test
public void selectAll() {
- RuleDto rule1 = db.rules().insertRule();
- RuleDto rule2 = db.rules().insertRule();
+ RuleDto rule1 = db.rules().insertRule(r -> r.addDefaultImpact(newRuleDefaultImpact(SECURITY, org.sonar.api.issue.impact.Severity.LOW)));
+ RuleDto rule2 = db.rules().insertRule(r -> r.addDefaultImpact(newRuleDefaultImpact(RELIABILITY, org.sonar.api.issue.impact.Severity.MEDIUM)));
RuleDto rule3 = db.rules().insertRule();
- assertThat(underTest.selectAll(db.getSession()))
+ List<RuleDto> ruleDtos = underTest.selectAll(db.getSession());
+ assertThat(ruleDtos)
.extracting(RuleDto::getUuid)
- .containsOnly(rule1.getUuid(), rule2.getUuid(), rule3.getUuid());
+ .containsExactlyInAnyOrder(rule1.getUuid(), rule2.getUuid(), rule3.getUuid());
+
+ assertThat(ruleDtos)
+ .filteredOn(ruleDto -> ruleDto.getUuid().equals(rule1.getUuid()))
+ .extracting(RuleDto::getDefaultImpacts)
+ .flatMap(Function.identity())
+ .extracting(ImpactDto::getSeverity, ImpactDto::getSoftwareQuality)
+ .containsExactlyInAnyOrder(
+ tuple(org.sonar.api.issue.impact.Severity.HIGH, MAINTAINABILITY),
+ tuple(org.sonar.api.issue.impact.Severity.LOW, SECURITY));
+
+ assertThat(ruleDtos)
+ .filteredOn(ruleDto -> ruleDto.getUuid().equals(rule2.getUuid()))
+ .extracting(RuleDto::getDefaultImpacts)
+ .flatMap(Function.identity())
+ .extracting(ImpactDto::getSeverity, ImpactDto::getSoftwareQuality)
+ .containsExactlyInAnyOrder(
+ tuple(org.sonar.api.issue.impact.Severity.HIGH, MAINTAINABILITY),
+ tuple(org.sonar.api.issue.impact.Severity.MEDIUM, RELIABILITY));
+
+ assertThat(ruleDtos)
+ .filteredOn(ruleDto -> ruleDto.getUuid().equals(rule3.getUuid()))
+ .extracting(RuleDto::getDefaultImpacts)
+ .flatMap(Function.identity())
+ .extracting(ImpactDto::getSeverity, ImpactDto::getSoftwareQuality)
+ .containsExactlyInAnyOrder(
+ tuple(org.sonar.api.issue.impact.Severity.HIGH, MAINTAINABILITY));
}
private void assertEquals(RuleDto actual, RuleDto expected) {
.extracting(RuleDto::getUuid, RuleDto::getLanguage, RuleDto::getType)
.containsExactlyInAnyOrder(
tuple(rule1.getUuid(), "java", RuleType.VULNERABILITY.getDbConstant()),
- tuple(rule3.getUuid(), "java", RuleType.BUG.getDbConstant())
- );
+ tuple(rule3.getUuid(), "java", RuleType.BUG.getDbConstant()));
assertThat(underTest.selectByLanguage(db.getSession(), "js")).hasSize(1);
@Test
public void insert() {
RuleDescriptionSectionDto sectionDto = createDefaultRuleDescriptionSection();
+ ImpactDto ruleDefaultImpactDto = newRuleDefaultImpact(SECURITY, org.sonar.api.issue.impact.Severity.HIGH);
RuleDto newRule = new RuleDto()
.setUuid("rule-uuid")
.setRuleKey("NewRuleKey")
.setName("new name")
.setDescriptionFormat(RuleDto.Format.MARKDOWN)
.addRuleDescriptionSectionDto(sectionDto)
+ .addDefaultImpact(ruleDefaultImpactDto)
.setStatus(RuleStatus.DEPRECATED)
.setConfigKey("NewConfigKey")
.setSeverity(Severity.INFO)
assertThat(ruleDto.getRuleDescriptionSectionDtos()).usingRecursiveFieldByFieldElementComparator()
.containsOnly(sectionDto);
assertThat(ruleDto.getCleanCodeAttribute()).isEqualTo(CleanCodeAttribute.CLEAR);
+ assertThat(ruleDto.getDefaultImpacts()).usingRecursiveFieldByFieldElementComparator()
+ .containsOnly(ruleDefaultImpactDto);
}
@Test
.isInstanceOf(PersistenceException.class);
}
+ private static ImpactDto newRuleDefaultImpact(SoftwareQuality softwareQuality, org.sonar.api.issue.impact.Severity severity) {
+ return new ImpactDto()
+ .setUuid(UuidFactoryFast.getInstance().create())
+ .setSoftwareQuality(softwareQuality)
+ .setSeverity(severity);
+ }
+
private static RuleDescriptionSectionDto createDefaultRuleDescriptionSection() {
return RuleDescriptionSectionDto.createDefaultRuleDescriptionSection(UuidFactoryFast.getInstance().create(), RandomStringUtils.randomAlphanumeric(1000));
}
import org.sonar.db.event.EventMapper;
import org.sonar.db.issue.AnticipatedTransitionDto;
import org.sonar.db.issue.AnticipatedTransitionMapper;
+import org.sonar.db.issue.ImpactDto;
import org.sonar.db.issue.IssueChangeDto;
import org.sonar.db.issue.IssueChangeMapper;
import org.sonar.db.issue.IssueDto;
confBuilder.loadAlias("InternalComponentProperty", InternalComponentPropertyDto.class);
confBuilder.loadAlias("IssueChange", IssueChangeDto.class);
confBuilder.loadAlias("KeyLongValue", KeyLongValue.class);
+ confBuilder.loadAlias("Impact", ImpactDto.class);
confBuilder.loadAlias("Issue", IssueDto.class);
confBuilder.loadAlias("NewCodeReferenceIssue", NewCodeReferenceIssueDto.class);
confBuilder.loadAlias("Measure", MeasureDto.class);
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.db.issue;
+
+import java.io.Serializable;
+import java.util.Objects;
+import org.sonar.api.issue.impact.Severity;
+import org.sonar.api.issue.impact.SoftwareQuality;
+
+public class ImpactDto implements Serializable {
+ private String uuid;
+ private SoftwareQuality softwareQuality;
+ private Severity severity;
+
+ public ImpactDto() {
+ // nothing to do
+ }
+
+ public String getUuid() {
+ return uuid;
+ }
+
+ public ImpactDto setUuid(String uuid) {
+ this.uuid = uuid;
+ return this;
+ }
+
+ public SoftwareQuality getSoftwareQuality() {
+ return softwareQuality;
+ }
+
+ public ImpactDto setSoftwareQuality(SoftwareQuality softwareQuality) {
+ this.softwareQuality = softwareQuality;
+ return this;
+ }
+
+ public Severity getSeverity() {
+ return severity;
+ }
+
+ public ImpactDto setSeverity(Severity severity) {
+ this.severity = severity;
+ return this;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ ImpactDto impactDto = (ImpactDto) o;
+ return Objects.equals(uuid, impactDto.uuid)
+ && Objects.equals(softwareQuality, impactDto.softwareQuality)
+ && Objects.equals(severity, impactDto.severity);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(uuid, softwareQuality, severity);
+ }
+}
public void insert(DbSession session, IssueDto dto) {
mapper(session).insert(dto);
+ updateIssueImpacts(dto, mapper(session));
+ }
+
+ private static void updateIssueImpacts(IssueDto issueDto, IssueMapper mapper) {
+ mapper.deleteIssueImpacts(issueDto.getKey());
+ insertInsertIssueImpacts(issueDto, mapper);
+ }
+
+ private static void insertInsertIssueImpacts(IssueDto issueDto, IssueMapper mapper) {
+ issueDto.getImpacts()
+ .forEach(impact -> mapper.insertIssueImpact(issueDto.getKey(), impact));
}
public void insert(DbSession session, IssueDto dto, IssueDto... others) {
- IssueMapper mapper = mapper(session);
- mapper.insert(dto);
+ insert(session, dto);
for (IssueDto other : others) {
- mapper.insert(other);
+ insert(session, other);
}
}
import com.google.protobuf.InvalidProtocolBufferException;
import java.io.Serializable;
import java.util.Collection;
+import java.util.Collections;
import java.util.Date;
+import java.util.EnumMap;
+import java.util.HashSet;
+import java.util.Map;
import java.util.Optional;
import java.util.Set;
+import java.util.stream.Collectors;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
+import org.sonar.api.issue.impact.Severity;
+import org.sonar.api.issue.impact.SoftwareQuality;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.rules.CleanCodeAttribute;
import org.sonar.api.rules.RuleType;
import org.sonar.db.rule.RuleDto;
import static com.google.common.base.Preconditions.checkArgument;
+import static java.lang.String.format;
import static org.sonar.api.utils.DateUtils.dateToLong;
import static org.sonar.api.utils.DateUtils.longToDate;
// populate only when retrieving closed issue for issue tracking
private String closedChangeData;
+ private Set<ImpactDto> impacts = new HashSet<>();
+ private Set<ImpactDto> ruleDefaultImpacts = new HashSet<>();
+
public IssueDto() {
// nothing to do
}
try {
return DbIssues.MessageFormattings.parseFrom(messageFormattings);
} catch (InvalidProtocolBufferException e) {
- throw new IllegalStateException(String.format("Fail to read ISSUES.MESSAGE_FORMATTINGS [KEE=%s]", kee), e);
+ throw new IllegalStateException(format("Fail to read ISSUES.MESSAGE_FORMATTINGS [KEE=%s]", kee), e);
}
}
return null;
try {
return DbIssues.Locations.parseFrom(locations);
} catch (InvalidProtocolBufferException e) {
- throw new IllegalStateException(String.format("Fail to read ISSUES.LOCATIONS [KEE=%s]", kee), e);
+ throw new IllegalStateException(format("Fail to read ISSUES.LOCATIONS [KEE=%s]", kee), e);
}
}
return null;
return this;
}
+ /**
+ * Return impacts defined on this issue.
+ *
+ * @return Collection of impacts
+ */
+ public Set<ImpactDto> getImpacts() {
+ return impacts;
+ }
+
+ public IssueDto addImpact(ImpactDto impact) {
+ impacts.stream().filter(impactDto -> impactDto.getSoftwareQuality() == impact.getSoftwareQuality()).findFirst()
+ .ifPresent(impactDto -> {
+ throw new IllegalStateException(format("Impact already defined on issue for Software Quality [%s]", impact.getSoftwareQuality()));
+ });
+
+ impacts.add(impact);
+ return this;
+ }
+
+ public IssueDto replaceAllImpacts(Collection<ImpactDto> newImpacts) {
+ Set<SoftwareQuality> newSoftwareQuality = newImpacts.stream().map(ImpactDto::getSoftwareQuality).collect(Collectors.toSet());
+ if (newSoftwareQuality.size() != newImpacts.size()) {
+ throw new IllegalStateException("Impacts must have unique Software Quality values");
+ }
+ impacts.clear();
+ impacts.addAll(newImpacts);
+ return this;
+ }
+
+ Set<ImpactDto> getRuleDefaultImpacts() {
+ return ruleDefaultImpacts;
+ }
+
+ /**
+ * Returns effective impacts defined on this issue along with default ones.
+ *
+ * @return Unmodifiable Map of impacts
+ */
+ public Map<SoftwareQuality, Severity> getEffectiveImpacts() {
+ EnumMap<SoftwareQuality, Severity> effectiveImpacts = new EnumMap<>(SoftwareQuality.class);
+ ruleDefaultImpacts.forEach(impact -> effectiveImpacts.put(impact.getSoftwareQuality(), impact.getSeverity()));
+ impacts.forEach(impact -> effectiveImpacts.put(impact.getSoftwareQuality(), impact.getSeverity()));
+ return Collections.unmodifiableMap(effectiveImpacts);
+ }
+
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
void insertAsNewCodeOnReferenceBranch(NewCodeReferenceIssueDto issue);
+ void insertIssueImpact(@Param("issueKey") String issueKey, @Param("dto") ImpactDto issue);
+
void deleteAsNewCodeOnReferenceBranch(String issueKey);
int updateIfBeforeSelectedDate(IssueDto issue);
List<IssueDto> selectByBranch(@Param("keys") Set<String> keys, @Nullable @Param("changedSince") Long changedSince);
-
List<String> selectRecentlyClosedIssues(@Param("queryParams") IssueQueryParams issueQueryParams);
List<String> selectIssueKeysByQuery(@Param("query") IssueListQuery issueListQuery, @Param("pagination") Pagination pagination);
+
+ void deleteIssueImpacts(String issueKey);
}
session.commit();
profiler.stop();
+ profiler.start("deleteIssues (issues_impacts)");
+ purgeMapper.deleteIssuesImpactsByProjectUuid(rootUuid);
+ session.commit();
+ profiler.stop();
+
profiler.start("deleteIssues (issues)");
purgeMapper.deleteIssuesByProjectUuid(rootUuid);
session.commit();
return emptyList();
});
+ executeLargeInputs(issueKeys, input -> {
+ mapper.deleteIssuesImpactsFromKeys(input);
+ return emptyList();
+ });
+
executeLargeInputs(issueKeys, input -> {
mapper.deleteIssuesFromKeys(input);
return emptyList();
void deleteNewCodeReferenceIssuesByProjectUuid(@Param("projectUuid") String projectUuid);
+ void deleteIssuesImpactsByProjectUuid(@Param("projectUuid") String projectUuid);
+
List<String> selectOldClosedIssueKeys(@Param("projectUuid") String projectUuid, @Nullable @Param("toDate") Long toDate);
List<String> selectStaleBranchesAndPullRequests(@Param("projectUuid") String projectUuid, @Param("toDate") Long toDate);
void deleteNewCodeReferenceIssuesFromKeys(@Param("issueKeys") List<String> keys);
+ void deleteIssuesImpactsFromKeys(@Param("issueKeys") List<String> keys);
+
void deleteFileSourcesByProjectUuid(String rootProjectUuid);
void deleteFileSourcesByFileUuid(@Param("fileUuids") List<String> fileUuids);
RuleMapper mapper = mapper(session);
mapper.insertRule(ruleDto);
updateRuleDescriptionSectionDtos(ruleDto, mapper);
+ updateRuleDefaultImpacts(ruleDto, mapper);
}
public void update(DbSession session, RuleDto ruleDto) {
RuleMapper mapper = mapper(session);
mapper.updateRule(ruleDto);
updateRuleDescriptionSectionDtos(ruleDto, mapper);
+ updateRuleDefaultImpacts(ruleDto, mapper);
}
private static void updateRuleDescriptionSectionDtos(RuleDto ruleDto, RuleMapper mapper) {
.forEach(section -> mapper.insertRuleDescriptionSection(ruleDto.getUuid(), section));
}
+ private static void updateRuleDefaultImpacts(RuleDto ruleDto, RuleMapper mapper) {
+ mapper.deleteRuleDefaultImpacts(ruleDto.getUuid());
+ insertRuleDefaultImpacts(ruleDto, mapper);
+ }
+
+ private static void insertRuleDefaultImpacts(RuleDto ruleDto, RuleMapper mapper) {
+ ruleDto.getDefaultImpacts()
+ .forEach(section -> mapper.insertRuleDefaultImpact(ruleDto.getUuid(), section));
+ }
+
public void scrollIndexingRuleExtensionsByIds(DbSession dbSession, Collection<String> ruleExtensionIds, Consumer<RuleExtensionForIndexingDto> consumer) {
RuleMapper mapper = mapper(dbSession);
import java.util.Optional;
import java.util.Set;
import java.util.TreeSet;
+import java.util.stream.Collectors;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.builder.HashCodeBuilder;
+import org.sonar.api.issue.impact.SoftwareQuality;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.rule.RuleStatus;
import org.sonar.api.rules.CleanCodeAttribute;
import org.sonar.api.rules.RuleType;
+import org.sonar.db.issue.ImpactDto;
import static com.google.common.base.Preconditions.checkArgument;
+import static java.lang.String.format;
import static java.util.Arrays.asList;
import static java.util.Collections.emptySet;
import static java.util.Optional.ofNullable;
*/
private RuleDto.Format descriptionFormat = null;
private RuleStatus status = null;
+
+ private Set<ImpactDto> defaultImpacts = new HashSet<>();
+
private String name = null;
private String configKey = null;
return this;
}
+ public Set<ImpactDto> getDefaultImpacts() {
+ return defaultImpacts;
+ }
+
+ public RuleDto addDefaultImpact(ImpactDto defaultImpactDto) {
+ defaultImpacts.stream().filter(impactDto -> impactDto.getSoftwareQuality() == defaultImpactDto.getSoftwareQuality()).findFirst()
+ .ifPresent(impactDto -> {
+ throw new IllegalStateException(format("Impact already defined on rule for Software Quality [%s]", defaultImpactDto.getSoftwareQuality()));
+ });
+ defaultImpacts.add(defaultImpactDto);
+ return this;
+ }
+
+ public RuleDto replaceAllDefaultImpacts(Collection<ImpactDto> newImpacts) {
+ Set<SoftwareQuality> newSoftwareQuality = newImpacts.stream().map(ImpactDto::getSoftwareQuality).collect(Collectors.toSet());
+ if (newSoftwareQuality.size() != newImpacts.size()) {
+ throw new IllegalStateException("Impacts must have unique Software Quality values");
+ }
+ defaultImpacts.clear();
+ defaultImpacts.addAll(newImpacts);
+ return this;
+ }
+
public String getName() {
return name;
}
return this;
}
-
@CheckForNull
public String getDefRemediationFunction() {
return defRemediationFunction;
import org.apache.ibatis.annotations.Param;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.rules.RuleQuery;
+import org.sonar.db.issue.ImpactDto;
public interface RuleMapper {
void insertRuleDescriptionSection(@Param("ruleUuid") String ruleUuid, @Param("dto") RuleDescriptionSectionDto ruleDescriptionSectionDto);
+ void insertRuleDefaultImpact(@Param("ruleUuid") String ruleUuid, @Param("dto") ImpactDto ruleDefaultImpactDto);
+
void updateRule(RuleDto ruleDefinitionDto);
void deleteRuleDescriptionSection(String ruleUuid);
void deleteDeprecatedRuleKeys(@Param("uuids") List<String> uuids);
void insertDeprecatedRuleKey(DeprecatedRuleKeyDto deprecatedRuleKeyDto);
+
+ void deleteRuleDefaultImpacts(String ruleUuid);
}
i.issue_type as type,
i.quick_fix_available as quickFixAvailable,
i.code_variants as codeVariantsString,
+ <include refid="issueImpactsColumns"/>
+ <include refid="ruleDefaultImpactsColumns"/>
<include refid="isNewCodeReferenceIssue"/>
</sql>
</if>
</sql>
+ <sql id="issueImpactsColumns">
+ ii.uuid as "ii_uuid",
+ ii.software_quality as "ii_softwareQuality",
+ ii.severity as "ii_severity",
+ </sql>
+ <sql id="ruleDefaultImpactsColumns">
+ rdi.uuid as "rdi_uuid",
+ rdi.software_quality as "rdi_softwareQuality",
+ rdi.severity as "rdi_severity",
+ </sql>
+
+ <resultMap id="issueResultMap" type="Issue" autoMapping="true">
+ <id property="kee" column="kee"/>
+
+ <collection property="impacts" column="ii_uuid" notNullColumn="ii_uuid"
+ javaType="java.util.Set" ofType="Impact">
+ <id property="uuid" column="ii_uuid"/>
+ <result property="softwareQuality" column="ii_softwareQuality"/>
+ <result property="severity" column="ii_severity"/>
+ </collection>
+ <collection property="ruleDefaultImpacts" column="rdi_uuid" notNullColumn="rdi_uuid"
+ javaType="java.util.Set" ofType="Impact">
+ <id property="uuid" column="rdi_uuid"/>
+ <result property="softwareQuality" column="rdi_softwareQuality"/>
+ <result property="severity" column="rdi_severity"/>
+ </collection>
+ </resultMap>
+
<insert id="insert" parameterType="Issue" useGeneratedKeys="false">
INSERT INTO issues (kee, rule_uuid, severity, manual_severity,
message, message_formattings, line, locations, gap, effort, status, tags, rule_description_context_key,
where kee = #{kee} and updated_at <= #{selectedAt}
</update>
- <select id="selectByKey" parameterType="String" resultType="Issue">
+ <select id="selectByKey" parameterType="String" resultMap="issueResultMap">
select
<include refid="issueColumns"/>,
u.login as assigneeLogin
where i.kee=#{kee,jdbcType=VARCHAR}
</select>
- <select id="scrollNonClosedByComponentUuid" parameterType="String" resultType="Issue" fetchSize="${_scrollFetchSize}"
- resultSetType="FORWARD_ONLY">
+ <select id="scrollNonClosedByComponentUuid" parameterType="String" resultMap="issueResultMap" fetchSize="${_scrollFetchSize}" resultSetType="FORWARD_ONLY" resultOrdered="true">
select
<include refid="issueColumns"/>
from issues i
where
i.component_uuid = #{componentUuid,jdbcType=VARCHAR} and
i.status <> 'CLOSED'
+ order by
+ i.kee
</select>
- <select id="scrollClosedByComponentUuid" resultType="Issue" fetchSize="${_scrollFetchSize}" resultSetType="FORWARD_ONLY">
+ <select id="scrollClosedByComponentUuid" resultMap="issueResultMap" fetchSize="${_scrollFetchSize}" resultSetType="FORWARD_ONLY" resultOrdered="true">
select
<include refid="issueColumns"/>,
ic.change_data as closedChangeData
where i.project_uuid=#{projectUuid,jdbcType=VARCHAR} and i.status <> 'CLOSED'
</select>
- <select id="selectByKeys" parameterType="map" resultType="Issue">
+ <select id="selectByKeys" parameterType="map" resultMap="issueResultMap">
select
<include refid="issueColumns"/>
from issues i
</foreach>
</select>
- <select id="selectByKeysIfNotUpdatedAt" parameterType="map" resultType="Issue">
+ <select id="selectByKeysIfNotUpdatedAt" parameterType="map" resultMap="issueResultMap">
select
<include refid="issueColumns"/>
from issues i
order by i.rn asc
</select>
- <select id="selectByComponentUuidPaginated" parameterType="map" resultType="Issue">
+ <select id="selectByComponentUuidPaginated" parameterType="map" resultMap="issueResultMap">
select
<include refid="issueColumns"/>,
u.login as assigneeLogin
limit #{pagination.pageSize,jdbcType=INTEGER} offset #{pagination.offset,jdbcType=INTEGER}
</select>
- <select id="selectByComponentUuidPaginated" parameterType="map" resultType="Issue" databaseId="mssql">
+ <select id="selectByComponentUuidPaginated" parameterType="map" resultMap="issueResultMap" databaseId="mssql">
select
<include refid="issueColumns"/>,
u.login as assigneeLogin
left outer join rules_default_impacts rdi on r.uuid = rdi.rule_uuid
</select>
- <select id="selectByComponentUuidPaginated" parameterType="map" resultType="Issue" databaseId="oracle">
+ <select id="selectByComponentUuidPaginated" parameterType="map" resultMap="issueResultMap" databaseId="oracle">
select
<include refid="issueColumns"/>,
u.login as assigneeLogin
)
</delete>
+ <delete id="deleteIssuesImpactsByProjectUuid" parameterType="map">
+ delete from issues_impacts
+ where
+ issue_key in (
+ select
+ kee
+ from issues
+ where
+ project_uuid = #{projectUuid,jdbcType=VARCHAR}
+ )
+ </delete>
+
<delete id="deleteFileSourcesByProjectUuid">
delete from file_sources where project_uuid=#{rootProjectUuid,jdbcType=VARCHAR}
</delete>
</foreach>
</delete>
+ <delete id="deleteIssuesImpactsFromKeys" parameterType="map">
+ DELETE FROM issues_impacts
+ WHERE issue_key IN
+ <foreach collection="issueKeys" open="(" close=")" item="issueKey" separator=",">
+ #{issueKey,jdbcType=VARCHAR}
+ </foreach>
+ </delete>
+
<delete id="deleteCeScannerContextOfCeActivityByRootUuidOrBefore">
delete from ce_scanner_context
where
rds.rule_uuid = r.uuid
</sql>
+ <sql id="leftOuterJoinRulesDefaultImpacts">
+ left outer join rules_default_impacts rdi on
+ rdi.rule_uuid = r.uuid
+ </sql>
+
<sql id="selectJoinedTablesColumns">
+ <!-- rule default impacts -->
+ rdi.uuid as "rdi_uuid",
+ rdi.rule_uuid as "rdi_ruleUuid",
+ rdi.software_quality as "rdi_softwareQuality",
+ rdi.severity as "rdi_severity",
+ <!-- rule description sections -->
rds.content as "rds_content",
rds.uuid as "rds_uuid",
rds.kee as "rds_kee",
rds.context_key as "rds_contextKey",
rds.context_display_name as "rds_contextDisplayName",
+ <!-- rule -->
r.uuid as "r_uuid",
-
<include refid="ruleColumns"/>
</sql>
from
rules r
<include refid="leftOuterJoinRulesDescriptionSections"/>
+ <include refid="leftOuterJoinRulesDefaultImpacts"/>
+ order by r.uuid
</select>
<resultMap id="ruleResultMap" type="org.sonar.db.rule.RuleDto" autoMapping="true">
</constructor>
</association>
</collection>
+ <collection property="defaultImpacts" column="rdi_uuid" notNullColumn="rdi_uuid" javaType="java.util.Set" ofType="Impact">
+ <id property="uuid" column="rdi_uuid"/>
+ <result property="softwareQuality" column="rdi_softwareQuality"/>
+ <result property="severity" column="rdi_severity"/>
+ </collection>
</resultMap>
-
-
<select id="selectEnabled" resultMap="ruleResultMap">
select
<include refid="selectJoinedTablesColumns"/>
from
rules r
<include refid="leftOuterJoinRulesDescriptionSections"/>
+ <include refid="leftOuterJoinRulesDefaultImpacts"/>
where
r.status != 'REMOVED'
+ order by r.uuid
</select>
<select id="selectByUuid" parameterType="map" resultMap="ruleResultMap">
from
rules r
<include refid="leftOuterJoinRulesDescriptionSections"/>
+ <include refid="leftOuterJoinRulesDefaultImpacts"/>
where
r.uuid=#{uuid,jdbcType=VARCHAR}
+ order by r.uuid
</select>
<select id="selectByUuids" parameterType="map" resultMap="ruleResultMap">
from
rules r
<include refid="leftOuterJoinRulesDescriptionSections"/>
+ <include refid="leftOuterJoinRulesDefaultImpacts"/>
where
<foreach collection="uuids" index="index" item="uuid" open="" separator=" or " close="">
r.uuid=#{uuid,jdbcType=VARCHAR}
</foreach>
+ order by r.uuid
</select>
<select id="selectByKey" parameterType="map" resultMap="ruleResultMap">
from
rules r
<include refid="leftOuterJoinRulesDescriptionSections"/>
+ <include refid="leftOuterJoinRulesDefaultImpacts"/>
where
r.plugin_name=#{ruleKey.repository,jdbcType=VARCHAR}
and r.plugin_rule_key=#{ruleKey.rule,jdbcType=VARCHAR}
+ order by r.uuid
</select>
<select id="selectIndexingRuleExtensionsByIds" parameterType="map" resultType="org.sonar.db.rule.RuleExtensionForIndexingDto">
from
rules r
<include refid="leftOuterJoinRulesDescriptionSections"/>
+ <include refid="leftOuterJoinRulesDefaultImpacts"/>
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>
+ order by r.uuid
</select>
<select id="selectByQuery" parameterType="map" resultMap="ruleResultMap">
from
rules r
<include refid="leftOuterJoinRulesDescriptionSections"/>
+ <include refid="leftOuterJoinRulesDefaultImpacts"/>
where
r.status != 'REMOVED'
<if test="query.repositoryKey!=null">
and r.plugin_config_key = #{query.configKey,jdbcType=VARCHAR}
</if>
order by
+ r.uuid,
r.updated_at desc
</select>
from
rules r
<include refid="leftOuterJoinRulesDescriptionSections"/>
+ <include refid="leftOuterJoinRulesDefaultImpacts"/>
where
r.status != 'REMOVED' and r.is_external=${_false} and r.is_template=${_false}
and r.rule_type in
<foreach collection="types" item="type" separator="," open="(" close=")">#{type, jdbcType=INTEGER}</foreach>
and r.language in
<foreach collection="languages" item="language" separator="," open="(" close=")">#{language, jdbcType=VARCHAR}</foreach>
+ order by r.uuid
</select>
<select id="selectByLanguage" parameterType="String" resultType="org.sonar.db.rule.RuleDto" fetchSize="${_scrollFetchSize}"
)
</insert>
+ <insert id="insertRuleDefaultImpact" parameterType="Map" useGeneratedKeys="false">
+ INSERT INTO rules_default_impacts (uuid, rule_uuid, software_quality, severity)
+ VALUES (
+ #{dto.uuid,jdbcType=VARCHAR},
+ #{ruleUuid,jdbcType=VARCHAR},
+ #{dto.softwareQuality,jdbcType=VARCHAR},
+ #{dto.severity,jdbcType=VARCHAR})
+ </insert>
+
<update id="updateRule" parameterType="org.sonar.db.rule.RuleDto">
update rules set
plugin_key=#{pluginKey,jdbcType=VARCHAR},
rule_uuid=#{ruleUuid,jdbcType=VARCHAR}
</delete>
+ <delete id="deleteRuleDefaultImpacts" parameterType="String">
+ delete from
+ rules_default_impacts
+ where
+ rule_uuid=#{ruleUuid,jdbcType=VARCHAR}
+ </delete>
+
<delete id="deleteParams" parameterType="String">
delete from
active_rule_parameters
import org.apache.commons.lang.time.DateUtils;
import org.junit.Test;
import org.sonar.api.issue.Issue;
+import org.sonar.api.issue.impact.Severity;
+import org.sonar.api.issue.impact.SoftwareQuality;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.rules.CleanCodeAttribute;
import org.sonar.api.rules.RuleType;
import org.sonar.api.utils.Duration;
import org.sonar.core.issue.DefaultIssue;
+import org.sonar.core.util.UuidFactoryFast;
import org.sonar.db.protobuf.DbIssues;
import org.sonar.db.rule.RuleDto;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.assertj.core.api.Assertions.tuple;
public class IssueDtoTest {
assertThat(dto.getTags()).isEmpty();
}
+ @Test
+ public void getEffectiveImpacts_whenNoIssueImpactsOverridden_shouldReturnRuleImpacts() {
+ IssueDto dto = new IssueDto();
+ dto.getRuleDefaultImpacts().add(newImpactDto(SoftwareQuality.MAINTAINABILITY, Severity.HIGH));
+ dto.getRuleDefaultImpacts().add(newImpactDto(SoftwareQuality.SECURITY, Severity.MEDIUM));
+ dto.getRuleDefaultImpacts().add(newImpactDto(SoftwareQuality.RELIABILITY, Severity.LOW));
+
+ assertThat(dto.getEffectiveImpacts())
+ .containsEntry(SoftwareQuality.MAINTAINABILITY, Severity.HIGH)
+ .containsEntry(SoftwareQuality.SECURITY, Severity.MEDIUM)
+ .containsEntry(SoftwareQuality.RELIABILITY, Severity.LOW);
+ }
+
+ @Test
+ public void getEffectiveImpacts_whenIssueImpactsOverridden_shouldReturnRuleImpactsOverriddenByIssueImpacts() {
+ IssueDto dto = new IssueDto();
+ dto.getRuleDefaultImpacts().add(newImpactDto(SoftwareQuality.MAINTAINABILITY, Severity.HIGH));
+ dto.getRuleDefaultImpacts().add(newImpactDto(SoftwareQuality.SECURITY, Severity.MEDIUM));
+ dto.getRuleDefaultImpacts().add(newImpactDto(SoftwareQuality.RELIABILITY, Severity.LOW));
+
+ dto.addImpact(newImpactDto(SoftwareQuality.MAINTAINABILITY, Severity.LOW));
+ dto.addImpact(newImpactDto(SoftwareQuality.RELIABILITY, Severity.HIGH));
+
+ assertThat(dto.getEffectiveImpacts())
+ .containsEntry(SoftwareQuality.MAINTAINABILITY, Severity.LOW)
+ .containsEntry(SoftwareQuality.SECURITY, Severity.MEDIUM)
+ .containsEntry(SoftwareQuality.RELIABILITY, Severity.HIGH);
+ }
+
+ @Test
+ public void addImpact_whenSoftwareQualityAlreadyDefined_shouldThrowISE() {
+ IssueDto dto = new IssueDto();
+ dto.addImpact(newImpactDto(SoftwareQuality.MAINTAINABILITY, Severity.LOW));
+
+ ImpactDto duplicatedImpact = newImpactDto(SoftwareQuality.MAINTAINABILITY, Severity.HIGH);
+
+ assertThatThrownBy(() -> dto.addImpact(duplicatedImpact))
+ .isInstanceOf(IllegalStateException.class)
+ .hasMessage("Impact already defined on issue for Software Quality [MAINTAINABILITY]");
+ }
+
+ @Test
+ public void replaceAllImpacts_whenSoftwareQualityAlreadyDuplicated_shouldThrowISE() {
+ IssueDto dto = new IssueDto();
+ dto.addImpact(newImpactDto(SoftwareQuality.MAINTAINABILITY, Severity.MEDIUM));
+ dto.addImpact(newImpactDto(SoftwareQuality.SECURITY, Severity.HIGH));
+ dto.addImpact(newImpactDto(SoftwareQuality.RELIABILITY, Severity.LOW));
+
+ Set<ImpactDto> duplicatedImpacts = Set.of(
+ newImpactDto(SoftwareQuality.MAINTAINABILITY, Severity.HIGH),
+ newImpactDto(SoftwareQuality.MAINTAINABILITY, Severity.LOW));
+ assertThatThrownBy(() -> dto.replaceAllImpacts(duplicatedImpacts))
+ .isInstanceOf(IllegalStateException.class)
+ .hasMessage("Impacts must have unique Software Quality values");
+ }
+
+ @Test
+ public void replaceAllImpacts_shouldReplaceExistingImpacts() {
+ IssueDto dto = new IssueDto();
+ dto.addImpact(newImpactDto(SoftwareQuality.MAINTAINABILITY, Severity.MEDIUM));
+ dto.addImpact(newImpactDto(SoftwareQuality.SECURITY, Severity.HIGH));
+ dto.addImpact(newImpactDto(SoftwareQuality.RELIABILITY, Severity.LOW));
+
+ Set<ImpactDto> duplicatedImpacts = Set.of(
+ newImpactDto(SoftwareQuality.MAINTAINABILITY, Severity.HIGH),
+ newImpactDto(SoftwareQuality.SECURITY, Severity.LOW));
+
+ dto.replaceAllImpacts(duplicatedImpacts);
+
+ assertThat(dto.getImpacts())
+ .extracting(ImpactDto::getSoftwareQuality, ImpactDto::getSeverity)
+ .containsExactlyInAnyOrder(
+ tuple(SoftwareQuality.MAINTAINABILITY, Severity.HIGH),
+ tuple(SoftwareQuality.SECURITY, Severity.LOW));
+
+ }
+
@Test
public void setCodeVariants_shouldReturnCodeVariants() {
IssueDto dto = new IssueDto();
.setCodeVariants(List.of("variant1", "variant2"));
return defaultIssue;
}
+
+ public static ImpactDto newImpactDto(SoftwareQuality softwareQuality, Severity severity) {
+ return new ImpactDto()
+ .setUuid(UuidFactoryFast.getInstance().create())
+ .setSoftwareQuality(softwareQuality)
+ .setSeverity(severity);
+ }
+
}
import java.util.TreeSet;
import org.jetbrains.annotations.NotNull;
import org.junit.Test;
+import org.sonar.api.issue.impact.Severity;
+import org.sonar.api.issue.impact.SoftwareQuality;
+import org.sonar.core.util.UuidFactoryFast;
import org.sonar.core.util.Uuids;
+import org.sonar.db.issue.ImpactDto;
import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
import static org.apache.commons.lang.StringUtils.repeat;
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.db.rule.RuleDto.ERROR_MESSAGE_SECTION_ALREADY_EXISTS;
import static org.sonar.db.rule.RuleTesting.newRule;
public class RuleDtoTest {
-
public static final String SECTION_KEY = "section key";
+
@Test
public void fail_if_key_is_too_long() {
assertThatThrownBy(() -> new RuleDto().setRuleKey(repeat("x", 250)))
}
+ @Test
+ public void addDefaultImpact_whenSoftwareQualityAlreadyDefined_shouldThrowISE() {
+ RuleDto dto = new RuleDto();
+ dto.addDefaultImpact(newImpactDto(SoftwareQuality.MAINTAINABILITY, Severity.LOW));
+
+ ImpactDto duplicatedImpact = newImpactDto(SoftwareQuality.MAINTAINABILITY, Severity.HIGH);
+
+ assertThatThrownBy(() -> dto.addDefaultImpact(duplicatedImpact))
+ .isInstanceOf(IllegalStateException.class)
+ .hasMessage("Impact already defined on rule for Software Quality [MAINTAINABILITY]");
+ }
+
+ @Test
+ public void replaceAllDefaultImpacts_whenSoftwareQualityAlreadyDuplicated_shouldThrowISE() {
+ RuleDto dto = new RuleDto();
+ dto.addDefaultImpact(newImpactDto(SoftwareQuality.MAINTAINABILITY, Severity.MEDIUM));
+ dto.addDefaultImpact(newImpactDto(SoftwareQuality.SECURITY, Severity.HIGH));
+ dto.addDefaultImpact(newImpactDto(SoftwareQuality.RELIABILITY, Severity.LOW));
+
+ Set<ImpactDto> duplicatedImpacts = Set.of(
+ newImpactDto(SoftwareQuality.MAINTAINABILITY, Severity.HIGH),
+ newImpactDto(SoftwareQuality.MAINTAINABILITY, Severity.LOW));
+ assertThatThrownBy(() -> dto.replaceAllDefaultImpacts(duplicatedImpacts))
+ .isInstanceOf(IllegalStateException.class)
+ .hasMessage("Impacts must have unique Software Quality values");
+ }
+
+ @Test
+ public void replaceAllImpacts_shouldReplaceExistingImpacts() {
+ RuleDto dto = new RuleDto();
+ dto.addDefaultImpact(newImpactDto(SoftwareQuality.MAINTAINABILITY, Severity.MEDIUM));
+ dto.addDefaultImpact(newImpactDto(SoftwareQuality.SECURITY, Severity.HIGH));
+ dto.addDefaultImpact(newImpactDto(SoftwareQuality.RELIABILITY, Severity.LOW));
+
+ Set<ImpactDto> duplicatedImpacts = Set.of(
+ newImpactDto(SoftwareQuality.MAINTAINABILITY, Severity.HIGH),
+ newImpactDto(SoftwareQuality.SECURITY, Severity.LOW));
+
+ dto.replaceAllDefaultImpacts(duplicatedImpacts);
+
+ assertThat(dto.getDefaultImpacts())
+ .extracting(ImpactDto::getSoftwareQuality, ImpactDto::getSeverity)
+ .containsExactlyInAnyOrder(
+ tuple(SoftwareQuality.MAINTAINABILITY, Severity.HIGH),
+ tuple(SoftwareQuality.SECURITY, Severity.LOW));
+
+ }
+
@NotNull
private static RuleDescriptionSectionDto createSection(String section_key, String contextKey, String contextDisplayName) {
return RuleDescriptionSectionDto.builder()
.key(section_key)
.build();
}
+
+ public static ImpactDto newImpactDto(SoftwareQuality softwareQuality, Severity severity) {
+ return new ImpactDto()
+ .setUuid(UuidFactoryFast.getInstance().create())
+ .setSoftwareQuality(softwareQuality)
+ .setSeverity(severity);
+ }
}
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Consumer;
import javax.annotation.Nullable;
+import org.sonar.api.issue.impact.SoftwareQuality;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.rule.RuleStatus;
import org.sonar.api.rule.Severity;
import org.sonar.api.server.rule.RuleParamType;
import org.sonar.core.util.UuidFactory;
import org.sonar.core.util.UuidFactoryFast;
+import org.sonar.db.issue.ImpactDto;
import org.sonar.db.rule.RuleDto.Scope;
import static com.google.common.base.Preconditions.checkNotNull;
// only static helpers
}
-
public static RuleDto newRule() {
return newRule(RuleKey.of(randomAlphanumeric(30), randomAlphanumeric(30)));
}
+
public static RuleDto newRule(RuleDescriptionSectionDto... ruleDescriptionSectionDtos) {
return newRule(randomRuleKey(), ruleDescriptionSectionDtos);
}
.setDescriptionFormat(RuleDto.Format.HTML)
.setType(CODE_SMELL)
.setCleanCodeAttribute(CleanCodeAttribute.CLEAR)
+ .addDefaultImpact(new ImpactDto().setUuid(uuidFactory.create())
+ .setSoftwareQuality(SoftwareQuality.MAINTAINABILITY)
+ .setSeverity(org.sonar.api.issue.impact.Severity.HIGH))
.setStatus(RuleStatus.READY)
.setConfigKey("configKey_" + ruleKey.rule())
.setSeverity(Severity.ALL.get(nextInt(Severity.ALL.size())))
.setLanguage("lang_" + randomAlphanumeric(3))
.setGapDescription("gapDescription_" + randomAlphanumeric(5))
.setDefRemediationBaseEffort(nextInt(10) + "h")
- //voluntarily offset the remediation to be able to detect issues
+ // voluntarily offset the remediation to be able to detect issues
.setDefRemediationGapMultiplier((nextInt(10) + 10) + "h")
.setDefRemediationFunction("LINEAR_OFFSET")
.setRemediationBaseEffort(nextInt(10) + "h")
return newRule(XOO_X2).setLanguage("xoo");
}
-
public static RuleDto newTemplateRule(RuleKey ruleKey) {
return newRule(ruleKey)
.setIsTemplate(true);