@@ -58,6 +58,7 @@ public final class SqTables { | |||
"internal_component_props", | |||
"internal_properties", | |||
"issues", | |||
"issues_impacts", | |||
"issue_changes", | |||
"live_measures", | |||
"metrics", | |||
@@ -96,6 +97,7 @@ public final class SqTables { | |||
"report_subscriptions", | |||
"rules", | |||
"rule_desc_sections", | |||
"rules_default_impacts", | |||
"rules_parameters", | |||
"rules_profiles", | |||
"rule_repositories", |
@@ -31,9 +31,12 @@ import javax.annotation.Nullable; | |||
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; | |||
@@ -101,6 +104,8 @@ public class IssueDaoIT { | |||
@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)); | |||
@@ -142,7 +147,7 @@ public class IssueDaoIT { | |||
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(); | |||
@@ -152,6 +157,18 @@ public class IssueDaoIT { | |||
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 | |||
@@ -169,8 +186,15 @@ public class IssueDaoIT { | |||
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 | |||
@@ -537,6 +561,32 @@ public class IssueDaoIT { | |||
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); | |||
@@ -783,7 +833,9 @@ public class IssueDaoIT { | |||
.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) | |||
@@ -792,6 +844,13 @@ public class IssueDaoIT { | |||
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)]; | |||
} |
@@ -38,6 +38,8 @@ import org.junit.Test; | |||
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; | |||
@@ -424,13 +426,10 @@ public class IssueMapperIT { | |||
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 | |||
@@ -450,12 +449,11 @@ public class IssueMapperIT { | |||
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) { | |||
@@ -484,7 +482,7 @@ public class IssueMapperIT { | |||
} | |||
@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); | |||
} | |||
@@ -495,7 +493,7 @@ public class IssueMapperIT { | |||
} | |||
@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()) | |||
@@ -503,9 +501,17 @@ public class IssueMapperIT { | |||
.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; | |||
} |
@@ -37,6 +37,8 @@ import org.junit.Rule; | |||
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; | |||
@@ -50,6 +52,7 @@ import org.sonar.db.dialect.Dialect; | |||
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; | |||
@@ -565,7 +568,7 @@ public class PurgeCommandsIT { | |||
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 | |||
@@ -957,14 +960,14 @@ public class PurgeCommandsIT { | |||
@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() | |||
}; | |||
} |
@@ -1226,6 +1226,7 @@ public class PurgeDaoIT { | |||
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()); | |||
} | |||
@@ -29,11 +29,13 @@ import java.util.List; | |||
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; | |||
@@ -44,8 +46,10 @@ 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.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; | |||
@@ -56,6 +60,9 @@ 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.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 { | |||
@@ -68,12 +75,18 @@ 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 | |||
@@ -83,7 +96,6 @@ public class RuleDaoIT { | |||
assertThat(underTest.selectByKey(db.getSession(), ruleDto.getKey())).isNotEmpty(); | |||
} | |||
@Test | |||
public void selectByUuid() { | |||
RuleDto ruleDto = db.rules().insert(); | |||
@@ -153,14 +165,9 @@ public class RuleDaoIT { | |||
@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"); | |||
} | |||
@@ -192,13 +199,40 @@ public class RuleDaoIT { | |||
@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) { | |||
@@ -313,8 +347,7 @@ public class RuleDaoIT { | |||
.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); | |||
@@ -436,6 +469,7 @@ public class RuleDaoIT { | |||
@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") | |||
@@ -443,6 +477,7 @@ public class RuleDaoIT { | |||
.setName("new name") | |||
.setDescriptionFormat(RuleDto.Format.MARKDOWN) | |||
.addRuleDescriptionSectionDto(sectionDto) | |||
.addDefaultImpact(ruleDefaultImpactDto) | |||
.setStatus(RuleStatus.DEPRECATED) | |||
.setConfigKey("NewConfigKey") | |||
.setSeverity(Severity.INFO) | |||
@@ -492,6 +527,8 @@ public class RuleDaoIT { | |||
assertThat(ruleDto.getRuleDescriptionSectionDtos()).usingRecursiveFieldByFieldElementComparator() | |||
.containsOnly(sectionDto); | |||
assertThat(ruleDto.getCleanCodeAttribute()).isEqualTo(CleanCodeAttribute.CLEAR); | |||
assertThat(ruleDto.getDefaultImpacts()).usingRecursiveFieldByFieldElementComparator() | |||
.containsOnly(ruleDefaultImpactDto); | |||
} | |||
@Test | |||
@@ -1148,6 +1185,13 @@ public class RuleDaoIT { | |||
.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)); | |||
} |
@@ -72,6 +72,7 @@ import org.sonar.db.event.EventDto; | |||
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; | |||
@@ -212,6 +213,7 @@ public class MyBatis { | |||
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); |
@@ -0,0 +1,83 @@ | |||
/* | |||
* 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); | |||
} | |||
} |
@@ -93,13 +93,23 @@ public class IssueDao implements Dao { | |||
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); | |||
} | |||
} | |||
@@ -26,13 +26,20 @@ import com.google.common.collect.ImmutableSet; | |||
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; | |||
@@ -43,6 +50,7 @@ import org.sonar.db.protobuf.DbIssues; | |||
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; | |||
@@ -103,6 +111,9 @@ public final class IssueDto implements Serializable { | |||
// 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 | |||
} | |||
@@ -293,7 +304,7 @@ public final class IssueDto implements Serializable { | |||
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; | |||
@@ -695,7 +706,7 @@ public final class IssueDto implements Serializable { | |||
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; | |||
@@ -769,6 +780,51 @@ public final class IssueDto implements Serializable { | |||
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); |
@@ -62,6 +62,8 @@ public interface IssueMapper { | |||
void insertAsNewCodeOnReferenceBranch(NewCodeReferenceIssueDto issue); | |||
void insertIssueImpact(@Param("issueKey") String issueKey, @Param("dto") ImpactDto issue); | |||
void deleteAsNewCodeOnReferenceBranch(String issueKey); | |||
int updateIfBeforeSelectedDate(IssueDto issue); | |||
@@ -74,8 +76,9 @@ public interface IssueMapper { | |||
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); | |||
} |
@@ -198,6 +198,11 @@ class PurgeCommands { | |||
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(); |
@@ -139,6 +139,11 @@ public class PurgeDao implements Dao { | |||
return emptyList(); | |||
}); | |||
executeLargeInputs(issueKeys, input -> { | |||
mapper.deleteIssuesImpactsFromKeys(input); | |||
return emptyList(); | |||
}); | |||
executeLargeInputs(issueKeys, input -> { | |||
mapper.deleteIssuesFromKeys(input); | |||
return emptyList(); |
@@ -92,6 +92,8 @@ public interface PurgeMapper { | |||
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); | |||
@@ -107,6 +109,8 @@ public interface PurgeMapper { | |||
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); |
@@ -102,12 +102,14 @@ public class RuleDao implements Dao { | |||
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) { | |||
@@ -120,6 +122,16 @@ public class RuleDao implements Dao { | |||
.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); | |||
@@ -27,16 +27,20 @@ import java.util.Objects; | |||
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; | |||
@@ -68,6 +72,9 @@ public class RuleDto { | |||
*/ | |||
private RuleDto.Format descriptionFormat = null; | |||
private RuleStatus status = null; | |||
private Set<ImpactDto> defaultImpacts = new HashSet<>(); | |||
private String name = null; | |||
private String configKey = null; | |||
@@ -267,6 +274,29 @@ public class RuleDto { | |||
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; | |||
} | |||
@@ -424,7 +454,6 @@ public class RuleDto { | |||
return this; | |||
} | |||
@CheckForNull | |||
public String getDefRemediationFunction() { | |||
return defRemediationFunction; |
@@ -25,6 +25,7 @@ import java.util.Set; | |||
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 { | |||
@@ -52,6 +53,8 @@ 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); | |||
@@ -77,4 +80,6 @@ public interface RuleMapper { | |||
void deleteDeprecatedRuleKeys(@Param("uuids") List<String> uuids); | |||
void insertDeprecatedRuleKey(DeprecatedRuleKeyDto deprecatedRuleKeyDto); | |||
void deleteRuleDefaultImpacts(String ruleUuid); | |||
} |
@@ -41,6 +41,8 @@ | |||
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> | |||
@@ -109,6 +111,34 @@ | |||
</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, | |||
@@ -225,7 +255,7 @@ | |||
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 | |||
@@ -240,8 +270,7 @@ | |||
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 | |||
@@ -254,9 +283,11 @@ | |||
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 | |||
@@ -292,7 +323,7 @@ | |||
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 | |||
@@ -308,7 +339,7 @@ | |||
</foreach> | |||
</select> | |||
<select id="selectByKeysIfNotUpdatedAt" parameterType="map" resultType="Issue"> | |||
<select id="selectByKeysIfNotUpdatedAt" parameterType="map" resultMap="issueResultMap"> | |||
select | |||
<include refid="issueColumns"/> | |||
from issues i | |||
@@ -617,7 +648,7 @@ | |||
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 | |||
@@ -634,7 +665,7 @@ | |||
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 | |||
@@ -656,7 +687,7 @@ | |||
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 |
@@ -291,6 +291,18 @@ | |||
) | |||
</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> | |||
@@ -425,6 +437,14 @@ | |||
</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 |
@@ -48,14 +48,25 @@ | |||
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> | |||
@@ -65,6 +76,8 @@ | |||
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"> | |||
@@ -81,18 +94,23 @@ | |||
</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"> | |||
@@ -101,8 +119,10 @@ | |||
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"> | |||
@@ -111,10 +131,12 @@ | |||
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"> | |||
@@ -123,9 +145,11 @@ | |||
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"> | |||
@@ -165,10 +189,12 @@ | |||
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"> | |||
@@ -177,6 +203,7 @@ | |||
from | |||
rules r | |||
<include refid="leftOuterJoinRulesDescriptionSections"/> | |||
<include refid="leftOuterJoinRulesDefaultImpacts"/> | |||
where | |||
r.status != 'REMOVED' | |||
<if test="query.repositoryKey!=null"> | |||
@@ -189,6 +216,7 @@ | |||
and r.plugin_config_key = #{query.configKey,jdbcType=VARCHAR} | |||
</if> | |||
order by | |||
r.uuid, | |||
r.updated_at desc | |||
</select> | |||
@@ -198,12 +226,14 @@ | |||
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}" | |||
@@ -321,6 +351,15 @@ | |||
) | |||
</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}, | |||
@@ -370,6 +409,13 @@ | |||
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 |
@@ -29,15 +29,20 @@ import java.util.Set; | |||
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 { | |||
@@ -153,6 +158,83 @@ 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(); | |||
@@ -279,4 +361,12 @@ public class IssueDtoTest { | |||
.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); | |||
} | |||
} |
@@ -25,19 +25,24 @@ import java.util.Set; | |||
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))) | |||
@@ -164,6 +169,54 @@ public class RuleDtoTest { | |||
} | |||
@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() | |||
@@ -178,4 +231,11 @@ public class RuleDtoTest { | |||
.key(section_key) | |||
.build(); | |||
} | |||
public static ImpactDto newImpactDto(SoftwareQuality softwareQuality, Severity severity) { | |||
return new ImpactDto() | |||
.setUuid(UuidFactoryFast.getInstance().create()) | |||
.setSoftwareQuality(softwareQuality) | |||
.setSeverity(severity); | |||
} | |||
} |
@@ -23,6 +23,7 @@ import java.util.Set; | |||
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; | |||
@@ -31,6 +32,7 @@ import org.sonar.api.rules.RuleType; | |||
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; | |||
@@ -62,10 +64,10 @@ public class RuleTesting { | |||
// 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); | |||
} | |||
@@ -94,6 +96,9 @@ public class RuleTesting { | |||
.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()))) | |||
@@ -104,7 +109,7 @@ public class RuleTesting { | |||
.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") | |||
@@ -151,7 +156,6 @@ public class RuleTesting { | |||
return newRule(XOO_X2).setLanguage("xoo"); | |||
} | |||
public static RuleDto newTemplateRule(RuleKey ruleKey) { | |||
return newRule(ruleKey) | |||
.setIsTemplate(true); |