import org.sonar.core.issue.DefaultIssue;
import org.sonar.core.issue.DefaultIssueComment;
import org.sonar.core.issue.IssueChangeContext;
+import org.sonar.core.rule.ImpactSeverityMapper;
import org.sonar.db.protobuf.DbIssues;
import org.sonar.db.user.UserDto;
import org.sonar.db.user.UserIdDto;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.base.Strings.isNullOrEmpty;
import static java.util.Objects.requireNonNull;
+import static org.sonar.api.server.rule.internal.ImpactMapper.convertToSoftwareQuality;
/**
* Updates issue fields and chooses if changes must be kept in history.
.stream().filter(DefaultImpact::manualSeverity)
.forEach(i -> issue.addImpact(i.softwareQuality(), i.severity(), true));
+ // If the severity of the issue is manually set but not the impact, we need to update the impacts with the same severity.
+ // This happens for user migrating from lower than 10.8 and having already customized the rule severity
+ if (issue.manualSeverity()
+ && issue.severity() != null
+ && issue.getImpacts().stream().noneMatch(DefaultImpact::manualSeverity)) {
+ issue.getImpacts()
+ .stream()
+ .filter(i -> convertToSoftwareQuality(issue.type()).equals(i.softwareQuality()))
+ .forEach(i -> issue.addImpact(i.softwareQuality(), ImpactSeverityMapper.mapImpactSeverity(issue.severity()), true));
+ }
+
if (!previousImpacts.equals(issue.getImpacts())) {
issue.setUpdateDate(context.date());
issue.setChanged(true);
new DefaultImpact(SoftwareQuality.RELIABILITY, Severity.HIGH, false)));
}
+ @Test
+ void setImpacts_whenIssueHasManualSeverityAndHasEquivalentImpact_ImpactShouldBeSetToManualSeverity() {
+ Set<DefaultImpact> currentImpacts = Set.of(new DefaultImpact(SoftwareQuality.MAINTAINABILITY, Severity.HIGH, false));
+ Set<DefaultImpact> newImpacts = Set.of(new DefaultImpact(SoftwareQuality.MAINTAINABILITY, Severity.LOW, false));
+
+ newImpacts
+ .forEach(e -> issue.addImpact(e.softwareQuality(), e.severity(), e.manualSeverity()));
+ issue.setManualSeverity(true);
+ issue.setSeverity(org.sonar.api.rule.Severity.BLOCKER);
+ issue.setType(RuleType.CODE_SMELL);
+ boolean updated = underTest.setImpacts(issue, currentImpacts, context);
+ assertThat(updated).isTrue();
+ assertThat(issue.getImpacts()).isEqualTo(Set.of(new DefaultImpact(SoftwareQuality.MAINTAINABILITY, Severity.BLOCKER, true)));
+ }
+
+ @Test
+ void setImpacts_whenIssueHasManualSeverityAndHasNoEquivalentImpact_ImpactShouldNotBeUpdated() {
+ 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()));
+ issue.setManualSeverity(true);
+ issue.setSeverity(org.sonar.api.rule.Severity.BLOCKER);
+ issue.setType(RuleType.BUG);
+ boolean updated = underTest.setImpacts(issue, currentImpacts, context);
+ assertThat(updated).isFalse();
+ }
+
+ @Test
+ void setImpacts_whenIssueHasManualSeverityAndImpactHasManualSeverity_ImpactShouldNotBeUpdated() {
+ Set<DefaultImpact> currentImpacts = Set.of(new DefaultImpact(SoftwareQuality.MAINTAINABILITY, Severity.LOW, true));
+ Set<DefaultImpact> newImpacts = Set.of(new DefaultImpact(SoftwareQuality.MAINTAINABILITY, Severity.LOW, true));
+
+ newImpacts
+ .forEach(e -> issue.addImpact(e.softwareQuality(), e.severity(), e.manualSeverity()));
+ issue.setManualSeverity(true);
+ issue.setSeverity(org.sonar.api.rule.Severity.BLOCKER);
+ issue.setType(RuleType.BUG);
+ boolean updated = underTest.setImpacts(issue, currentImpacts, context);
+ assertThat(updated).isFalse();
+ }
+
@Test
void setCodeVariants_whenCodeVariantsUnchanged_shouldNotBeUpdated() {
Set<String> currentCodeVariants = new HashSet<>(Arrays.asList("linux", "windows"));
*/
package org.sonar.server.qualityprofile;
-import com.google.common.collect.BiMap;
-import com.google.common.collect.ImmutableBiMap;
import java.util.EnumMap;
import java.util.Map;
import javax.annotation.CheckForNull;
import org.sonar.api.issue.impact.SoftwareQuality;
import org.sonar.api.rules.RuleType;
import org.sonar.api.server.rule.internal.ImpactMapper;
+import org.sonar.core.rule.ImpactSeverityMapper;
/**
* Class to map impact severity and rule severity during the override of severity of quality profile.
*/
public class QProfileImpactSeverityMapper {
- private static final BiMap<Severity, String> SEVERITY_MAPPING = new ImmutableBiMap.Builder<Severity, String>()
- .put(Severity.INFO, org.sonar.api.rule.Severity.INFO)
- .put(Severity.LOW, org.sonar.api.rule.Severity.MINOR)
- .put(Severity.MEDIUM, org.sonar.api.rule.Severity.MAJOR)
- .put(Severity.HIGH, org.sonar.api.rule.Severity.CRITICAL)
- .put(Severity.BLOCKER, org.sonar.api.rule.Severity.BLOCKER)
- .build();
-
private QProfileImpactSeverityMapper() {
}
}
SoftwareQuality softwareQuality = ImpactMapper.convertToSoftwareQuality(ruleType);
if (ruleImpacts.containsKey(softwareQuality)) {
- result.put(softwareQuality, SEVERITY_MAPPING.inverse().get(severity));
+ result.put(softwareQuality, ImpactSeverityMapper.mapImpactSeverity(severity));
} else if (ruleImpacts.size() == 1) {
- result.replaceAll((sq, sev) -> SEVERITY_MAPPING.inverse().get(severity));
+ result.replaceAll((sq, sev) -> ImpactSeverityMapper.mapImpactSeverity(severity));
}
return result;
}
public static String mapSeverity(Map<SoftwareQuality, Severity> impacts, RuleType ruleType, @Nullable String ruleSeverity) {
SoftwareQuality softwareQuality = ImpactMapper.convertToSoftwareQuality(ruleType);
if (impacts.containsKey(softwareQuality)) {
- return SEVERITY_MAPPING.get(impacts.get(softwareQuality));
+ return ImpactSeverityMapper.mapRuleSeverity(impacts.get(softwareQuality));
} else if (impacts.size() == 1) {
- return SEVERITY_MAPPING.get(impacts.entrySet().iterator().next().getValue());
+ return ImpactSeverityMapper.mapRuleSeverity(impacts.entrySet().iterator().next().getValue());
}
return ruleSeverity;
}
--- /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.rule;
+
+import com.google.common.collect.BiMap;
+import com.google.common.collect.ImmutableBiMap;
+import java.util.Optional;
+import org.sonar.api.issue.impact.Severity;
+
+public class ImpactSeverityMapper {
+ private static final BiMap<Severity, String> SEVERITY_MAPPING = new ImmutableBiMap.Builder<Severity, String>()
+ .put(Severity.INFO, org.sonar.api.rule.Severity.INFO)
+ .put(Severity.LOW, org.sonar.api.rule.Severity.MINOR)
+ .put(Severity.MEDIUM, org.sonar.api.rule.Severity.MAJOR)
+ .put(Severity.HIGH, org.sonar.api.rule.Severity.CRITICAL)
+ .put(Severity.BLOCKER, org.sonar.api.rule.Severity.BLOCKER)
+ .build();
+
+ private ImpactSeverityMapper() {
+
+ }
+
+ public static Severity mapImpactSeverity(String severity) {
+ return Optional.ofNullable(SEVERITY_MAPPING.inverse().get(severity))
+ .orElseThrow(() -> new IllegalArgumentException("Severity not supported: " + severity));
+ }
+
+ public static String mapRuleSeverity(Severity severity) {
+ return Optional.ofNullable(SEVERITY_MAPPING.get(severity))
+ .orElseThrow(() -> new IllegalArgumentException("Impact Severity not supported: " + severity));
+ }
+
+}
--- /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.rule;
+
+import org.junit.jupiter.api.Test;
+import org.sonar.api.issue.impact.Severity;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+class ImpactSeverityMapperTest {
+
+ @Test
+ void mapImpactSeverity_shouldReturnExpectedValues() {
+
+ assertThat(ImpactSeverityMapper.mapRuleSeverity(Severity.INFO)).isEqualTo(org.sonar.api.rule.Severity.INFO);
+ }
+
+ @Test
+ void mapImpactSeverity_shouldThrowExceptionWhenValueIsUnknownOrNull() {
+
+ assertThatThrownBy(() -> ImpactSeverityMapper.mapImpactSeverity("unknown")).isInstanceOf(IllegalArgumentException.class)
+ .hasMessage("Severity not supported: unknown");
+
+ assertThatThrownBy(() -> ImpactSeverityMapper.mapImpactSeverity(null)).isInstanceOf(IllegalArgumentException.class)
+ .hasMessage("Severity not supported: null");
+ }
+
+ @Test
+ void mapRuleSeverity_shouldReturnExpectedValues() {
+ assertThat(ImpactSeverityMapper.mapImpactSeverity(org.sonar.api.rule.Severity.INFO)).isEqualTo(Severity.INFO);
+ }
+
+ @Test
+ void mapRuleSeverity_shouldThrowExceptionWhenValueIsUnknownOrNull() {
+ assertThatThrownBy(() -> ImpactSeverityMapper.mapRuleSeverity(null)).isInstanceOf(IllegalArgumentException.class)
+ .hasMessage("Impact Severity not supported: null");
+ }
+}