import org.sonar.api.rules.CleanCodeAttribute;
import org.sonar.api.rules.RuleType;
import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolder;
+import org.sonar.core.issue.DefaultImpact;
import org.sonar.core.issue.DefaultIssue;
import org.sonar.core.issue.DefaultIssueComment;
import org.sonar.core.issue.FieldDiffs;
to.setManualSeverity(true);
to.setSeverity(from.severity());
}
+
+ from.getImpacts()
+ .stream().filter(DefaultImpact::manualSeverity)
+ .forEach(i -> to.addImpact(i.softwareQuality(), i.severity(), true));
to.setCleanCodeAttribute(from.getCleanCodeAttribute());
copyChangesOfIssueFromOtherBranch(to, from);
}
updater.setPastGap(raw, base.gap(), changeContext);
updater.setPastEffort(raw, base.effort(), changeContext);
updater.setCodeVariants(raw, requireNonNull(base.codeVariants()), changeContext);
- updater.setImpacts(raw, base.impacts(), changeContext);
+ updater.setImpacts(raw, base.getImpacts(), changeContext);
updater.setCleanCodeAttribute(raw, base.getCleanCodeAttribute(), changeContext);
updater.setPrioritizedRule(raw, base.isPrioritizedRule(), changeContext);
}
import org.sonar.api.rules.RuleType;
import org.sonar.api.utils.Duration;
import org.sonar.api.utils.System2;
+import org.sonar.core.issue.DefaultImpact;
import org.sonar.core.issue.DefaultIssue;
import org.sonar.core.issue.DefaultIssueComment;
import org.sonar.core.issue.FieldDiffs;
}
for (IssueCache.Impact impact : next.getImpactsList()) {
- defaultIssue.addImpact(SoftwareQuality.valueOf(impact.getSoftwareQuality()), Severity.valueOf(impact.getSeverity()));
+ defaultIssue.addImpact(SoftwareQuality.valueOf(impact.getSoftwareQuality()), Severity.valueOf(impact.getSeverity()), impact.getManualSeverity());
}
for (IssueCache.FieldDiffs protoFieldDiffs : next.getChangesList()) {
defaultIssue.addChange(toDefaultIssueChanges(protoFieldDiffs));
builder.setIsNoLongerNewCodeReferenceIssue(defaultIssue.isNoLongerNewCodeReferenceIssue());
defaultIssue.getAnticipatedTransitionUuid().ifPresent(builder::setAnticipatedTransitionUuid);
-
- for (Map.Entry<SoftwareQuality, Severity> impact : defaultIssue.impacts().entrySet()) {
+ for (DefaultImpact impact : defaultIssue.getImpacts()) {
builder.addImpacts(IssueCache.Impact.newBuilder()
- .setSoftwareQuality(impact.getKey().name())
- .setSeverity(impact.getValue().name())
+ .setSoftwareQuality(impact.softwareQuality().name())
+ .setSeverity(impact.severity().name())
+ .setManualSeverity(impact.manualSeverity())
.build());
}
for (FieldDiffs fieldDiffs : defaultIssue.changes()) {
message Impact {
required string software_quality = 1;
required string severity = 2;
+ required bool manual_severity = 3;
}
import org.sonar.api.utils.Duration;
import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolderRule;
import org.sonar.ce.task.projectanalysis.analysis.Branch;
+import org.sonar.core.issue.DefaultImpact;
import org.sonar.core.issue.DefaultIssue;
import org.sonar.core.issue.DefaultIssueComment;
import org.sonar.core.issue.FieldDiffs;
.setRuleDescriptionContextKey("spring")
.setCleanCodeAttribute(CleanCodeAttribute.IDENTIFIABLE)
.setCodeVariants(Set.of("foo", "bar"))
- .addImpact(SoftwareQuality.MAINTAINABILITY, Severity.HIGH)
+ .addImpact(SoftwareQuality.MAINTAINABILITY, Severity.LOW)
+ .addImpact(SoftwareQuality.RELIABILITY, Severity.HIGH)
.setCreationDate(parseDate("2015-10-01"))
.setUpdateDate(parseDate("2015-10-02"))
.setCloseDate(parseDate("2015-10-03"));
.setGap(15d)
.setRuleDescriptionContextKey("hibernate")
.setCodeVariants(Set.of("donut"))
- .addImpact(SoftwareQuality.RELIABILITY, Severity.LOW)
+ .addImpact(SoftwareQuality.RELIABILITY, Severity.LOW, true)
.setEffort(Duration.create(15L))
.setManualSeverity(false)
.setLocations(issueLocations)
.containsOnly(entry("foo", new FieldDiffs.Diff<>("bar", "donut")));
assertThat(raw.changes().get(1).diffs())
.containsOnly(entry("file", new FieldDiffs.Diff<>("A", "B")));
- assertThat(raw.impacts())
- .containsEntry(SoftwareQuality.MAINTAINABILITY, Severity.HIGH);
+ verifyUpdater(raw, messageFormattings, issueLocations);
+ }
+
+ private void verifyUpdater(DefaultIssue raw, DbIssues.MessageFormattings messageFormattings, DbIssues.Locations issueLocations) {
verify(updater).setPastSeverity(raw, BLOCKER, issueChangeContext);
verify(updater).setPastLine(raw, 10);
verify(updater).setRuleDescriptionContextKey(raw, "hibernate");
verify(updater).setPastEffort(raw, Duration.create(15L), issueChangeContext);
verify(updater).setPastLocations(raw, issueLocations);
verify(updater).setCleanCodeAttribute(raw, CleanCodeAttribute.FOCUSED, issueChangeContext);
+ verify(updater).setImpacts(raw, Set.of(new DefaultImpact(SoftwareQuality.RELIABILITY, Severity.LOW, true)), issueChangeContext);
}
@Test
package org.sonar.ce.task.projectanalysis.util.cache;
import java.util.Date;
-import java.util.Map;
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.CleanCodeAttribute;
import org.sonar.api.rules.RuleType;
+import org.sonar.core.issue.DefaultImpact;
import org.sonar.core.issue.DefaultIssue;
import static org.assertj.core.api.Assertions.assertThat;
@Test
public void toDefaultIssue_whenImpactIsSet_shouldSetItInDefaultIssue() {
IssueCache.Issue issue = prepareIssueWithCompulsoryFields()
- .addImpacts(toImpact(SoftwareQuality.MAINTAINABILITY, Severity.HIGH))
- .addImpacts(toImpact(SoftwareQuality.RELIABILITY, Severity.LOW))
+ .addImpacts(toImpact(SoftwareQuality.MAINTAINABILITY, Severity.HIGH, true))
+ .addImpacts(toImpact(SoftwareQuality.RELIABILITY, Severity.LOW, false))
.build();
DefaultIssue defaultIssue = ProtobufIssueDiskCache.toDefaultIssue(issue);
- assertThat(defaultIssue.impacts()).containsExactlyInAnyOrderEntriesOf(Map.of(SoftwareQuality.MAINTAINABILITY, Severity.HIGH, SoftwareQuality.RELIABILITY, Severity.LOW));
+ assertThat(defaultIssue.getImpacts())
+ .containsExactly(
+ new DefaultImpact(SoftwareQuality.MAINTAINABILITY, Severity.HIGH, true),
+ new DefaultImpact(SoftwareQuality.RELIABILITY, Severity.LOW, false));
}
@Test
@Test
public void toProto_whenRuleDescriptionContextKeyIsSet_shouldCopyToIssueProto() {
DefaultIssue defaultIssue = createDefaultIssueWithMandatoryFields();
- defaultIssue.addImpact(SoftwareQuality.MAINTAINABILITY, Severity.HIGH);
- defaultIssue.addImpact(SoftwareQuality.RELIABILITY, Severity.LOW);
+ defaultIssue.addImpact(SoftwareQuality.MAINTAINABILITY, Severity.HIGH, true);
+ defaultIssue.addImpact(SoftwareQuality.RELIABILITY, Severity.LOW, false);
IssueCache.Issue issue = ProtobufIssueDiskCache.toProto(IssueCache.Issue.newBuilder(), defaultIssue);
assertThat(issue.getImpactsList()).containsExactly(
- toImpact(SoftwareQuality.MAINTAINABILITY, Severity.HIGH),
- toImpact(SoftwareQuality.RELIABILITY, Severity.LOW)
- );
+ toImpact(SoftwareQuality.MAINTAINABILITY, Severity.HIGH, true),
+ toImpact(SoftwareQuality.RELIABILITY, Severity.LOW, false));
}
@Test
assertThat(issue.getCleanCodeAttribute()).isEqualTo(CleanCodeAttribute.FOCUSED.name());
}
- private IssueCache.Impact toImpact(SoftwareQuality softwareQuality, Severity severity) {
- return IssueCache.Impact.newBuilder().setSoftwareQuality(softwareQuality.name()).setSeverity(severity.name()).build();
+ private IssueCache.Impact toImpact(SoftwareQuality softwareQuality, Severity severity, boolean manualSeverity) {
+ return IssueCache.Impact.newBuilder().setSoftwareQuality(softwareQuality.name()).setSeverity(severity.name()).setManualSeverity(manualSeverity).build();
}
private static DefaultIssue createDefaultIssueWithMandatoryFields() {
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
+import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import javax.annotation.Nullable;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
-import org.jetbrains.annotations.NotNull;
import org.sonar.api.issue.IssueStatus;
import org.sonar.api.issue.impact.Severity;
import org.sonar.api.issue.impact.SoftwareQuality;
// populate only when retrieving closed issue for issue tracking
private String closedChangeData;
- private Set<ImpactDto> impacts = new HashSet<>();
+ private Set<ImpactDto> impacts = new LinkedHashSet<>();
- //non-persisted fields
- private Set<ImpactDto> ruleDefaultImpacts = new HashSet<>();
+ // non-persisted fields
+ private Set<ImpactDto> ruleDefaultImpacts = new LinkedHashSet<>();
private CleanCodeAttribute cleanCodeAttribute;
private CleanCodeAttribute ruleCleanCodeAttribute;
- //issues dependency fields, one-one relationship
+ // issues dependency fields, one-one relationship
private String cveId;
public IssueDto() {
* On batch side, component keys and uuid are useless
*/
public static IssueDto toDtoForComputationInsert(DefaultIssue issue, String ruleUuid, long now) {
- return new IssueDto()
+ IssueDto issueDto = new IssueDto()
.setKee(issue.key())
.setType(issue.type())
.setLine(issue.line())
.setQuickFixAvailable(issue.isQuickFixAvailable())
.setIsNewCodeReferenceIssue(issue.isNewCodeReferenceIssue())
.setCodeVariants(issue.codeVariants())
- .replaceAllImpacts(mapToImpactDto(issue.impacts()))
.setCleanCodeAttribute(issue.getCleanCodeAttribute())
// technical dates
.setCreatedAt(now)
.setUpdatedAt(now)
.setCveId(issue.getCveId());
- }
- @NotNull
- private static Set<ImpactDto> mapToImpactDto(Map<SoftwareQuality, Severity> impacts) {
- return impacts.entrySet().stream().map(e -> new ImpactDto()
- .setSoftwareQuality(e.getKey())
- .setSeverity(e.getValue()))
- .collect(Collectors.toSet());
+ issue.getImpacts().forEach(i -> issueDto.addImpact(new ImpactDto(i.softwareQuality(), i.severity(), i.manualSeverity())));
+ return issueDto;
}
/**
public static IssueDto toDtoForUpdate(DefaultIssue issue, long now) {
// Invariant fields, like key and rule, can't be updated
- return new IssueDto()
+ IssueDto issueDto = new IssueDto()
.setKee(issue.key())
.setType(issue.type())
.setLine(issue.line())
.setQuickFixAvailable(issue.isQuickFixAvailable())
.setIsNewCodeReferenceIssue(issue.isNewCodeReferenceIssue())
.setCodeVariants(issue.codeVariants())
- .replaceAllImpacts(mapToImpactDto(issue.impacts()))
.setCleanCodeAttribute(issue.getCleanCodeAttribute())
.setPrioritizedRule(issue.isPrioritizedRule())
// technical date
.setUpdatedAt(now);
+
+ issue.getImpacts().forEach(i -> issueDto.addImpact(new ImpactDto(i.softwareQuality(), i.severity(), i.manualSeverity())));
+ return issueDto;
+
}
public String getKey() {
return this;
}
-
public IssueDto setRuleDefaultImpacts(Set<ImpactDto> ruleDefaultImpacts) {
this.ruleDefaultImpacts = new HashSet<>(ruleDefaultImpacts);
return this;
}
-
public IssueDto replaceAllImpacts(Collection<ImpactDto> newImpacts) {
Set<SoftwareQuality> newSoftwareQuality = newImpacts.stream().map(ImpactDto::getSoftwareQuality).collect(Collectors.toSet());
if (newSoftwareQuality.size() != newImpacts.size()) {
issue.setIsNewCodeReferenceIssue(isNewCodeReferenceIssue);
issue.setCodeVariants(getCodeVariants());
issue.setCleanCodeAttribute(cleanCodeAttribute);
- impacts.forEach(i -> issue.addImpact(i.getSoftwareQuality(), i.getSeverity()));
+ impacts.forEach(i -> issue.addImpact(i.getSoftwareQuality(), i.getSeverity(), i.isManualSeverity()));
issue.setCveId(cveId);
return issue;
}
.setIssueUpdateDate(updatedAt)
.setIssueCloseDate(closedAt)
.setRuleDescriptionContextKey(TEST_CONTEXT_KEY)
- .addImpact(new ImpactDto().setSoftwareQuality(MAINTAINABILITY).setSeverity(HIGH));
+ .addImpact(new ImpactDto().setSoftwareQuality(MAINTAINABILITY).setSeverity(HIGH).setManualSeverity(true))
+ .addImpact(new ImpactDto().setSoftwareQuality(RELIABILITY).setSeverity(LOW).setManualSeverity(false));
DefaultIssue expected = new DefaultIssue()
.setKey("100")
.setRuleDescriptionContextKey(TEST_CONTEXT_KEY)
.setCodeVariants(Set.of())
.setTags(Set.of())
- .addImpact(MAINTAINABILITY, HIGH);
+ .addImpact(MAINTAINABILITY, HIGH, true)
+ .addImpact(RELIABILITY, LOW, false);
DefaultIssue issue = dto.toDefaultIssue();
RuleType.BUG.getDbConstant(), RuleKey.of("repo", "rule"));
assertThat(issueDto).extracting(IssueDto::getIssueCreationDate, IssueDto::getIssueCloseDate,
- IssueDto::getIssueUpdateDate, IssueDto::getSelectedAt, IssueDto::getUpdatedAt, IssueDto::getCreatedAt)
+ IssueDto::getIssueUpdateDate, IssueDto::getSelectedAt, IssueDto::getUpdatedAt, IssueDto::getCreatedAt)
.containsExactly(dateNow, dateNow, dateNow, dateNow.getTime(), now, now);
assertThat(issueDto).extracting(IssueDto::getLine, IssueDto::getMessage,
- IssueDto::getGap, IssueDto::getEffort, IssueDto::getResolution, IssueDto::getStatus, IssueDto::getSeverity)
+ IssueDto::getGap, IssueDto::getEffort, IssueDto::getResolution, IssueDto::getStatus, IssueDto::getSeverity)
.containsExactly(1, "message", 1.0, 1L, Issue.RESOLUTION_FALSE_POSITIVE, Issue.STATUS_CLOSED, "BLOCKER");
assertThat(issueDto).extracting(IssueDto::getTags, IssueDto::getCodeVariants, IssueDto::getAuthorLogin)
.containsExactly(Set.of("todo"), Set.of("variant1", "variant2"), "admin");
assertThat(issueDto).extracting(IssueDto::isManualSeverity, IssueDto::getChecksum, IssueDto::getAssigneeUuid,
- IssueDto::isExternal, IssueDto::getComponentUuid, IssueDto::getComponentKey,
- IssueDto::getProjectUuid, IssueDto::getProjectKey, IssueDto::getRuleUuid)
+ IssueDto::isExternal, IssueDto::getComponentUuid, IssueDto::getComponentKey,
+ IssueDto::getProjectUuid, IssueDto::getProjectKey, IssueDto::getRuleUuid)
.containsExactly(true, "123", "123", true, "123", "componentKey", "123", "projectKey", "ruleUuid");
assertThat(issueDto.isQuickFixAvailable()).isTrue();
assertThat(issueDto.isNewCodeReferenceIssue()).isTrue();
assertThat(issueDto.getOptionalRuleDescriptionContextKey()).contains(TEST_CONTEXT_KEY);
- assertThat(issueDto.getImpacts()).extracting(ImpactDto::getSoftwareQuality, ImpactDto::getSeverity)
- .containsExactlyInAnyOrder(tuple(MAINTAINABILITY, HIGH), tuple(RELIABILITY, LOW));
+ assertThat(issueDto.getImpacts()).extracting(ImpactDto::getSoftwareQuality, ImpactDto::getSeverity, ImpactDto::isManualSeverity)
+ .containsExactlyInAnyOrder(tuple(MAINTAINABILITY, HIGH, true), tuple(RELIABILITY, LOW, false));
}
@Test
RuleType.BUG.getDbConstant(), RuleKey.of("repo", "rule"));
assertThat(issueDto).extracting(IssueDto::getIssueCreationDate, IssueDto::getIssueCloseDate,
- IssueDto::getIssueUpdateDate, IssueDto::getSelectedAt, IssueDto::getUpdatedAt)
+ IssueDto::getIssueUpdateDate, IssueDto::getSelectedAt, IssueDto::getUpdatedAt)
.containsExactly(dateNow, dateNow, dateNow, dateNow.getTime(), now);
assertThat(issueDto).extracting(IssueDto::getLine, IssueDto::getMessage,
- IssueDto::getGap, IssueDto::getEffort, IssueDto::getResolution, IssueDto::getStatus, IssueDto::getSeverity)
+ IssueDto::getGap, IssueDto::getEffort, IssueDto::getResolution, IssueDto::getStatus, IssueDto::getSeverity)
.containsExactly(1, "message", 1.0, 1L, Issue.RESOLUTION_FALSE_POSITIVE, Issue.STATUS_CLOSED, "BLOCKER");
assertThat(issueDto).extracting(IssueDto::getTags, IssueDto::getCodeVariants, IssueDto::getAuthorLogin)
.containsExactly(Set.of("todo"), Set.of("variant1", "variant2"), "admin");
assertThat(issueDto).extracting(IssueDto::isManualSeverity, IssueDto::getChecksum, IssueDto::getAssigneeUuid,
- IssueDto::isExternal, IssueDto::getComponentUuid, IssueDto::getComponentKey, IssueDto::getProjectUuid, IssueDto::getProjectKey)
+ IssueDto::isExternal, IssueDto::getComponentUuid, IssueDto::getComponentKey, IssueDto::getProjectUuid, IssueDto::getProjectKey)
.containsExactly(true, "123", "123", true, "123", "componentKey", "123", "projectKey");
assertThat(issueDto.isQuickFixAvailable()).isTrue();
assertThat(issueDto.isNewCodeReferenceIssue()).isTrue();
assertThat(issueDto.getOptionalRuleDescriptionContextKey()).contains(TEST_CONTEXT_KEY);
- assertThat(issueDto.getImpacts()).extracting(ImpactDto::getSoftwareQuality, ImpactDto::getSeverity)
- .containsExactlyInAnyOrder(tuple(MAINTAINABILITY, HIGH), tuple(RELIABILITY, LOW));
+ assertThat(issueDto.getImpacts()).extracting(ImpactDto::getSoftwareQuality, ImpactDto::getSeverity, ImpactDto::isManualSeverity)
+ .containsExactlyInAnyOrder(tuple(MAINTAINABILITY, HIGH, true), tuple(RELIABILITY, LOW, false));
}
@Test
.setIsNewCodeReferenceIssue(true)
.setRuleDescriptionContextKey(TEST_CONTEXT_KEY)
.setCodeVariants(List.of("variant1", "variant2"))
- .addImpact(MAINTAINABILITY, HIGH)
- .addImpact(RELIABILITY, LOW);
+ .addImpact(MAINTAINABILITY, HIGH, true)
+ .addImpact(RELIABILITY, LOW, false);
return defaultIssue;
}
import java.time.temporal.ChronoUnit;
import java.util.Collection;
import java.util.Date;
-import java.util.EnumMap;
import java.util.HashSet;
-import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
import org.sonar.api.ce.ComputeEngineSide;
import org.sonar.api.issue.IssueStatus;
-import org.sonar.api.issue.impact.Severity;
-import org.sonar.api.issue.impact.SoftwareQuality;
import org.sonar.api.rules.CleanCodeAttribute;
import org.sonar.api.rules.RuleType;
import org.sonar.api.server.ServerSide;
import org.sonar.api.server.rule.RuleTagFormat;
import org.sonar.api.utils.Duration;
+import org.sonar.core.issue.DefaultImpact;
import org.sonar.core.issue.DefaultIssue;
import org.sonar.core.issue.DefaultIssueComment;
import org.sonar.core.issue.IssueChangeContext;
public boolean setIssueStatus(DefaultIssue issue, @Nullable IssueStatus previousIssueStatus, @Nullable IssueStatus newIssueStatus, IssueChangeContext context) {
if (!Objects.equals(newIssueStatus, previousIssueStatus)) {
- //Currently, issue status is not persisted in database, but is considered as an issue change
+ // Currently, issue status is not persisted in database, but is considered as an issue change
issue.setFieldChange(context, ISSUE_STATUS, previousIssueStatus, issue.issueStatus());
return true;
}
for (int i = 0; i < l1c.getMessageFormattingCount(); i++) {
if (l1c.getMessageFormatting(i).getStart() != l2.getMessageFormatting(i).getStart()
- || l1c.getMessageFormatting(i).getEnd() != l2.getMessageFormatting(i).getEnd()
- || l1c.getMessageFormatting(i).getType() != l2.getMessageFormatting(i).getType()) {
+ || l1c.getMessageFormatting(i).getEnd() != l2.getMessageFormatting(i).getEnd()
+ || l1c.getMessageFormatting(i).getType() != l2.getMessageFormatting(i).getType()) {
return false;
}
}
public void setPrioritizedRule(DefaultIssue issue, boolean prioritizedRule, IssueChangeContext context) {
if (!Objects.equals(prioritizedRule, issue.isPrioritizedRule())) {
issue.setPrioritizedRule(prioritizedRule);
- if (!issue.isNew()){
+ if (!issue.isNew()) {
issue.setUpdateDate(context.date());
issue.setChanged(true);
}
return false;
}
- public boolean setImpacts(DefaultIssue issue, Map<SoftwareQuality, Severity> previousImpacts, IssueChangeContext context) {
- Map<SoftwareQuality, Severity> currentImpacts = new EnumMap<>(issue.impacts());
- if (!previousImpacts.equals(currentImpacts)) {
- issue.replaceImpacts(currentImpacts);
+ public boolean setImpacts(DefaultIssue issue, Set<DefaultImpact> previousImpacts, IssueChangeContext context) {
+ previousImpacts
+ .stream().filter(DefaultImpact::manualSeverity)
+ .forEach(i -> issue.addImpact(i.softwareQuality(), i.severity(), true));
+
+ if (!previousImpacts.equals(issue.getImpacts())) {
issue.setUpdateDate(context.date());
issue.setChanged(true);
return true;
}
+
return false;
}
import java.util.Date;
import java.util.HashSet;
import java.util.List;
-import java.util.Map;
import java.util.Random;
import java.util.Set;
import org.apache.commons.lang3.time.DateUtils;
import org.sonar.api.rules.CleanCodeAttribute;
import org.sonar.api.rules.RuleType;
import org.sonar.api.utils.Duration;
+import org.sonar.core.issue.DefaultImpact;
import org.sonar.core.issue.DefaultIssue;
import org.sonar.core.issue.FieldDiffs;
import org.sonar.core.issue.IssueChangeContext;
@Test
void setImpacts_whenImpactAdded_shouldBeUpdated() {
- Map<SoftwareQuality, Severity> currentImpacts = Map.of(SoftwareQuality.RELIABILITY, Severity.LOW);
- Map<SoftwareQuality, Severity> newImpacts = Map.of(SoftwareQuality.MAINTAINABILITY, Severity.HIGH);
+ Set<DefaultImpact> currentImpacts = Set.of(new DefaultImpact(SoftwareQuality.RELIABILITY, Severity.LOW, false));
+ Set<DefaultImpact> newImpacts = Set.of(new DefaultImpact(SoftwareQuality.MAINTAINABILITY, Severity.HIGH, false));
- issue.replaceImpacts(newImpacts);
+ newImpacts
+ .forEach(e -> issue.addImpact(e.softwareQuality(), e.severity(), e.manualSeverity()));
boolean updated = underTest.setImpacts(issue, currentImpacts, context);
assertThat(updated).isTrue();
- assertThat(issue.impacts()).isEqualTo(newImpacts);
+ assertThat(issue.getImpacts()).isEqualTo(newImpacts);
+ }
+
+ @Test
+ void setImpacts_whenImpactExists_shouldNotBeUpdated() {
+ Set<DefaultImpact> currentImpacts = Set.of(new DefaultImpact(SoftwareQuality.MAINTAINABILITY, Severity.LOW, false));
+ Set<DefaultImpact> newImpacts = Set.of(new DefaultImpact(SoftwareQuality.MAINTAINABILITY, Severity.LOW, false));
+
+ newImpacts
+ .forEach(e -> issue.addImpact(e.softwareQuality(), e.severity(), e.manualSeverity()));
+ boolean updated = underTest.setImpacts(issue, currentImpacts, context);
+ assertThat(updated).isFalse();
+ assertThat(issue.getImpacts()).isEqualTo(newImpacts);
+ }
+
+ @Test
+ void setImpacts_whenImpactExistsWithManualSeverity_shouldNotBeUpdated() {
+ Set<DefaultImpact> currentImpacts = Set.of(new DefaultImpact(SoftwareQuality.MAINTAINABILITY, Severity.HIGH, true));
+ Set<DefaultImpact> newImpacts = Set.of(new DefaultImpact(SoftwareQuality.MAINTAINABILITY, Severity.LOW, false));
+
+ newImpacts
+ .forEach(e -> issue.addImpact(e.softwareQuality(), e.severity(), e.manualSeverity()));
+ boolean updated = underTest.setImpacts(issue, currentImpacts, context);
+ assertThat(updated).isFalse();
+ assertThat(issue.getImpacts()).isEqualTo(currentImpacts);
+ }
+
+ @Test
+ void setImpacts_whenImpactExistsWithManualSeverityAndNewImpact_shouldBeUpdated() {
+ Set<DefaultImpact> currentImpacts = Set.of(new DefaultImpact(SoftwareQuality.MAINTAINABILITY, Severity.HIGH, true));
+ Set<DefaultImpact> newImpacts = Set.of(new DefaultImpact(SoftwareQuality.MAINTAINABILITY, Severity.LOW, false),
+ new DefaultImpact(SoftwareQuality.RELIABILITY, Severity.HIGH, false));
+
+ newImpacts
+ .forEach(e -> issue.addImpact(e.softwareQuality(), e.severity(), e.manualSeverity()));
+ boolean updated = underTest.setImpacts(issue, currentImpacts, context);
+ assertThat(updated).isTrue();
+ assertThat(issue.getImpacts()).isEqualTo(Set.of(new DefaultImpact(SoftwareQuality.MAINTAINABILITY, Severity.HIGH, true),
+ new DefaultImpact(SoftwareQuality.RELIABILITY, Severity.HIGH, false)));
}
@Test
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2024 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.core.issue;
+
+import org.sonar.api.issue.impact.Severity;
+import org.sonar.api.issue.impact.SoftwareQuality;
+
+public record DefaultImpact(SoftwareQuality softwareQuality, Severity severity, boolean manualSeverity) {
+}
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
-import java.util.EnumMap;
import java.util.HashMap;
+import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
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.lang3.StringUtils;
private String anticipatedTransitionUuid = null;
- private Map<SoftwareQuality, org.sonar.api.issue.impact.Severity> impacts = new EnumMap<>(SoftwareQuality.class);
+ private final Map<SoftwareQuality, DefaultImpact> impacts = new LinkedHashMap<>();
private CleanCodeAttribute cleanCodeAttribute = null;
private String cveId = null;
@Override
public Map<SoftwareQuality, org.sonar.api.issue.impact.Severity> impacts() {
- return impacts;
+ return impacts.values().stream().collect(Collectors.toMap(DefaultImpact::softwareQuality, DefaultImpact::severity));
+ }
+
+ public Set<DefaultImpact> getImpacts() {
+ return new LinkedHashSet<>(this.impacts.values());
}
public DefaultIssue replaceImpacts(Map<SoftwareQuality, org.sonar.api.issue.impact.Severity> impacts) {
this.impacts.clear();
- this.impacts.putAll(impacts);
+ this.impacts.putAll(impacts.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, e -> new DefaultImpact(e.getKey(), e.getValue(), false))));
return this;
}
public DefaultIssue addImpact(SoftwareQuality softwareQuality, org.sonar.api.issue.impact.Severity severity) {
- impacts.put(softwareQuality, severity);
+ impacts.put(softwareQuality, new DefaultImpact(softwareQuality, severity, false));
+ return this;
+ }
+
+ public DefaultIssue addImpact(SoftwareQuality softwareQuality, org.sonar.api.issue.impact.Severity severity, boolean manualSeverity) {
+ impacts.put(softwareQuality, new DefaultImpact(softwareQuality, severity, manualSeverity));
return this;
}
assertThat(issue.impacts()).containsExactlyEntriesOf(Map.of(SoftwareQuality.SECURITY, Severity.LOW));
}
+ @Test
+ void addImpact_shouldReplaceExistingSoftwareQuality() {
+ issue.addImpact(SoftwareQuality.MAINTAINABILITY, Severity.HIGH);
+ issue.addImpact(SoftwareQuality.MAINTAINABILITY, Severity.LOW, true);
+ assertThat(issue.getImpacts())
+ .containsExactly(new DefaultImpact(SoftwareQuality.MAINTAINABILITY, Severity.LOW, true));
+ }
+
@Test
void prioritizedRule_shouldHaveCorrectDefaultValue() {
assertThat(issue.isPrioritizedRule()).isFalse();