]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-21770 Accept new metrics 'new_maintainability_issues', 'new_reliability_issues...
authorDejan Milisavljevic <dejan.milisavljevic@sonarsource.com>
Mon, 18 Mar 2024 10:09:18 +0000 (11:09 +0100)
committersonartech <sonartech@sonarsource.com>
Mon, 25 Mar 2024 20:02:42 +0000 (20:02 +0000)
18 files changed:
gradle.properties
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/IssueCounter.java
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/IssueCounterTest.java
server/sonar-db-dao/src/it/java/org/sonar/db/issue/IssueDaoIT.java
server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueDao.java
server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueImpactGroupDto.java
server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueMapper.java
server/sonar-db-dao/src/main/resources/org/sonar/db/issue/IssueMapper.xml
server/sonar-webserver-webapi/src/it/java/org/sonar/server/measure/ws/ComponentTreeActionIT.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/live/IssueCounter.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/live/LiveMeasureTreeUpdaterImpl.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/live/MeasureUpdateFormulaFactoryImpl.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/ComponentAction.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/ComponentTreeAction.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/SearchAction.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/SearchHistoryAction.java
server/sonar-webserver-webapi/src/test/java/org/sonar/server/measure/live/MeasureUpdateFormulaFactoryImplTest.java
sonar-core/src/main/resources/org/sonar/l10n/core.properties

index f4c8ae87ca5f1dcf27dd29f8fff5f79e4bd05a15..73cf05430600b9fedaaebeb9ebc11016d6f1e088 100644 (file)
@@ -1,6 +1,6 @@
 group=org.sonarsource.sonarqube
 version=10.5
-pluginApiVersion=10.6.0.2114
+pluginApiVersion=10.7.0.2175
 description=Open source platform for continuous inspection of code quality
 projectTitle=SonarQube
 org.gradle.jvmargs=-Xmx2048m
index b2b8f2cd0e753f7112b9ea18e0ee741c3d39668d..f2e4de123add08c0fae92b0df26e62ae2c31404d 100644 (file)
@@ -50,7 +50,7 @@ import static org.sonar.api.measures.CoreMetrics.CRITICAL_VIOLATIONS_KEY;
 import static org.sonar.api.measures.CoreMetrics.FALSE_POSITIVE_ISSUES_KEY;
 import static org.sonar.api.measures.CoreMetrics.HIGH_IMPACT_ACCEPTED_ISSUES_KEY;
 import static org.sonar.api.measures.CoreMetrics.INFO_VIOLATIONS_KEY;
-import static org.sonar.api.measures.CoreMetrics.MAINTAINABILITY_ISSUES;
+import static org.sonar.api.measures.CoreMetrics.MAINTAINABILITY_ISSUES_KEY;
 import static org.sonar.api.measures.CoreMetrics.MAJOR_VIOLATIONS_KEY;
 import static org.sonar.api.measures.CoreMetrics.MINOR_VIOLATIONS_KEY;
 import static org.sonar.api.measures.CoreMetrics.NEW_ACCEPTED_ISSUES_KEY;
@@ -59,16 +59,19 @@ import static org.sonar.api.measures.CoreMetrics.NEW_BUGS_KEY;
 import static org.sonar.api.measures.CoreMetrics.NEW_CODE_SMELLS_KEY;
 import static org.sonar.api.measures.CoreMetrics.NEW_CRITICAL_VIOLATIONS_KEY;
 import static org.sonar.api.measures.CoreMetrics.NEW_INFO_VIOLATIONS_KEY;
+import static org.sonar.api.measures.CoreMetrics.NEW_MAINTAINABILITY_ISSUES_KEY;
 import static org.sonar.api.measures.CoreMetrics.NEW_MAJOR_VIOLATIONS_KEY;
 import static org.sonar.api.measures.CoreMetrics.NEW_MINOR_VIOLATIONS_KEY;
+import static org.sonar.api.measures.CoreMetrics.NEW_RELIABILITY_ISSUES_KEY;
 import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_HOTSPOTS_KEY;
+import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_ISSUES_KEY;
 import static org.sonar.api.measures.CoreMetrics.NEW_VIOLATIONS_KEY;
 import static org.sonar.api.measures.CoreMetrics.NEW_VULNERABILITIES_KEY;
 import static org.sonar.api.measures.CoreMetrics.OPEN_ISSUES_KEY;
-import static org.sonar.api.measures.CoreMetrics.RELIABILITY_ISSUES;
+import static org.sonar.api.measures.CoreMetrics.RELIABILITY_ISSUES_KEY;
 import static org.sonar.api.measures.CoreMetrics.REOPENED_ISSUES_KEY;
 import static org.sonar.api.measures.CoreMetrics.SECURITY_HOTSPOTS_KEY;
-import static org.sonar.api.measures.CoreMetrics.SECURITY_ISSUES;
+import static org.sonar.api.measures.CoreMetrics.SECURITY_ISSUES_KEY;
 import static org.sonar.api.measures.CoreMetrics.VIOLATIONS_KEY;
 import static org.sonar.api.measures.CoreMetrics.VULNERABILITIES_KEY;
 import static org.sonar.api.rule.Severity.BLOCKER;
@@ -109,9 +112,14 @@ public class IssueCounter extends IssueVisitor {
     INFO, NEW_INFO_VIOLATIONS_KEY);
 
   static final Map<String, String> IMPACT_TO_METRIC_KEY = Map.of(
-    SoftwareQuality.SECURITY.name(), SECURITY_ISSUES.key(),
-    SoftwareQuality.RELIABILITY.name(), RELIABILITY_ISSUES.key(),
-    SoftwareQuality.MAINTAINABILITY.name(), MAINTAINABILITY_ISSUES.key());
+    SoftwareQuality.SECURITY.name(), SECURITY_ISSUES_KEY,
+    SoftwareQuality.RELIABILITY.name(), RELIABILITY_ISSUES_KEY,
+    SoftwareQuality.MAINTAINABILITY.name(), MAINTAINABILITY_ISSUES_KEY);
+
+  static final Map<String, String> IMPACT_TO_NEW_METRIC_KEY = Map.of(
+    SoftwareQuality.SECURITY.name(), NEW_SECURITY_ISSUES_KEY,
+    SoftwareQuality.RELIABILITY.name(), NEW_RELIABILITY_ISSUES_KEY,
+    SoftwareQuality.MAINTAINABILITY.name(), NEW_MAINTAINABILITY_ISSUES_KEY);
 
   private static final Map<RuleType, String> TYPE_TO_METRIC_KEY = ImmutableMap.<RuleType, String>builder()
     .put(CODE_SMELL, CODE_SMELLS_KEY)
@@ -222,9 +230,7 @@ public class IssueCounter extends IssueVisitor {
       String severity = entry.getKey();
       String metricKey = entry.getValue();
       Multiset<String> bag = currentCounters.counterForPeriod().severityBag;
-      Metric metric = metricRepository.getByKey(metricKey);
-      measureRepository.add(component, metric, Measure.newMeasureBuilder()
-        .create(bag.count(severity)));
+      addMeasure(component, metricKey, bag.count(severity));
     }
 
     // waiting for Java 8 lambda in order to factor this loop with the previous one
@@ -233,9 +239,12 @@ public class IssueCounter extends IssueVisitor {
       RuleType type = entry.getKey();
       String metricKey = entry.getValue();
       Multiset<RuleType> bag = currentCounters.counterForPeriod().typeBag;
-      Metric metric = metricRepository.getByKey(metricKey);
-      measureRepository.add(component, metric, Measure.newMeasureBuilder()
-        .create(bag.count(type)));
+      addMeasure(component, metricKey, bag.count(type));
+    }
+
+    for (Map.Entry<String, Map<String, Long>> impactEntry : currentCounters.counterForPeriod().impactsBag.entrySet()) {
+      String json = ImpactMeasureBuilder.fromMap(impactEntry.getValue()).buildAsString();
+      addMeasure(component, IMPACT_TO_NEW_METRIC_KEY.get(impactEntry.getKey()), json);
     }
 
     addMeasure(component, NEW_ACCEPTED_ISSUES_KEY, currentCounters.counterForPeriod().accepted);
index 49d8ca5bd2f22f1260f0d80c93c25232d15b15cd..7ad66ed9b4089475415a74510ad14624bfaba938 100644 (file)
@@ -27,8 +27,8 @@ import java.util.Map;
 import java.util.Set;
 import javax.annotation.Nullable;
 import org.assertj.core.data.MapEntry;
-import org.junit.Rule;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
 import org.sonar.api.issue.impact.Severity;
 import org.sonar.api.issue.impact.SoftwareQuality;
 import org.sonar.api.rules.RuleType;
@@ -92,11 +92,14 @@ import static org.sonar.api.measures.CoreMetrics.NEW_CODE_SMELLS_KEY;
 import static org.sonar.api.measures.CoreMetrics.NEW_CRITICAL_VIOLATIONS;
 import static org.sonar.api.measures.CoreMetrics.NEW_CRITICAL_VIOLATIONS_KEY;
 import static org.sonar.api.measures.CoreMetrics.NEW_INFO_VIOLATIONS;
+import static org.sonar.api.measures.CoreMetrics.NEW_MAINTAINABILITY_ISSUES;
 import static org.sonar.api.measures.CoreMetrics.NEW_MAJOR_VIOLATIONS;
 import static org.sonar.api.measures.CoreMetrics.NEW_MAJOR_VIOLATIONS_KEY;
 import static org.sonar.api.measures.CoreMetrics.NEW_MINOR_VIOLATIONS;
+import static org.sonar.api.measures.CoreMetrics.NEW_RELIABILITY_ISSUES;
 import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_HOTSPOTS;
 import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_HOTSPOTS_KEY;
+import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_ISSUES;
 import static org.sonar.api.measures.CoreMetrics.NEW_VIOLATIONS;
 import static org.sonar.api.measures.CoreMetrics.NEW_VIOLATIONS_KEY;
 import static org.sonar.api.measures.CoreMetrics.NEW_VULNERABILITIES;
@@ -120,24 +123,25 @@ import static org.sonar.api.rules.RuleType.CODE_SMELL;
 import static org.sonar.api.rules.RuleType.SECURITY_HOTSPOT;
 import static org.sonar.ce.task.projectanalysis.component.ReportComponent.builder;
 import static org.sonar.ce.task.projectanalysis.issue.IssueCounter.IMPACT_TO_METRIC_KEY;
+import static org.sonar.ce.task.projectanalysis.issue.IssueCounter.IMPACT_TO_NEW_METRIC_KEY;
 import static org.sonar.ce.task.projectanalysis.measure.Measure.newMeasureBuilder;
 import static org.sonar.ce.task.projectanalysis.measure.MeasureRepoEntry.entryOf;
 
-public class IssueCounterTest {
+class IssueCounterTest {
 
   private static final Component FILE1 = builder(Component.Type.FILE, 1).build();
   private static final Component FILE2 = builder(Component.Type.FILE, 2).build();
   private static final Component FILE3 = builder(Component.Type.FILE, 3).build();
   private static final Component PROJECT = builder(Component.Type.PROJECT, 4).addChildren(FILE1, FILE2, FILE3).build();
 
-  @Rule
-  public BatchReportReaderRule reportReader = new BatchReportReaderRule();
+  @RegisterExtension
+  private final BatchReportReaderRule reportReader = new BatchReportReaderRule();
 
-  @Rule
-  public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
+  @RegisterExtension
+  private final TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
 
-  @Rule
-  public MetricRepositoryRule metricRepository = new MetricRepositoryRule()
+  @RegisterExtension
+  private final MetricRepositoryRule metricRepository = new MetricRepositoryRule()
     .add(VIOLATIONS)
     .add(OPEN_ISSUES)
     .add(REOPENED_ISSUES)
@@ -167,15 +171,19 @@ public class IssueCounterTest {
     .add(HIGH_IMPACT_ACCEPTED_ISSUES)
     .add(RELIABILITY_ISSUES)
     .add(MAINTAINABILITY_ISSUES)
-    .add(SECURITY_ISSUES);
+    .add(SECURITY_ISSUES)
+    .add(NEW_RELIABILITY_ISSUES)
+    .add(NEW_MAINTAINABILITY_ISSUES)
+    .add(NEW_SECURITY_ISSUES);
 
-  @Rule
-  public MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository);
-  private NewIssueClassifier newIssueClassifier = mock(NewIssueClassifier.class);
-  private IssueCounter underTest = new IssueCounter(metricRepository, measureRepository, newIssueClassifier);
+  @RegisterExtension
+  private final MeasureRepositoryRule measureRepository = MeasureRepositoryRule.create(treeRootHolder, metricRepository);
+  private final NewIssueClassifier newIssueClassifier = mock(NewIssueClassifier.class);
+  private final IssueCounter underTest = new IssueCounter(metricRepository, measureRepository, newIssueClassifier);
+  private static int issueCounter;
 
   @Test
-  public void count_issues_by_status() {
+  void count_issues_by_status() {
     // bottom-up traversal -> from files to project
     underTest.beforeComponent(FILE1);
     underTest.onIssue(FILE1, createIssue(null, STATUS_OPEN, BLOCKER));
@@ -203,7 +211,7 @@ public class IssueCounterTest {
   }
 
   @Test
-  public void count_issues_by_resolution() {
+  void count_issues_by_resolution() {
     // bottom-up traversal -> from files to project
     underTest.beforeComponent(FILE1);
     underTest.onIssue(FILE1, createIssue(null, STATUS_OPEN, BLOCKER));
@@ -233,7 +241,7 @@ public class IssueCounterTest {
   }
 
   @Test
-  public void count_unresolved_issues_by_severity() {
+  void count_unresolved_issues_by_severity() {
     // bottom-up traversal -> from files to project
     underTest.beforeComponent(FILE1);
     underTest.onIssue(FILE1, createIssue(null, STATUS_OPEN, BLOCKER));
@@ -257,7 +265,7 @@ public class IssueCounterTest {
   }
 
   @Test
-  public void count_unresolved_issues_by_type() {
+  void count_unresolved_issues_by_type() {
     // bottom-up traversal -> from files to project
     // file1 : one open code smell, one closed code smell (which will be excluded from metric)
     underTest.beforeComponent(FILE1);
@@ -286,7 +294,7 @@ public class IssueCounterTest {
   }
 
   @Test
-  public void count_new_issues() {
+  void count_new_issues() {
     when(newIssueClassifier.isEnabled()).thenReturn(true);
 
     underTest.beforeComponent(FILE1);
@@ -315,7 +323,7 @@ public class IssueCounterTest {
   }
 
   @Test
-  public void count_new_accepted_issues() {
+  void count_new_accepted_issues() {
     when(newIssueClassifier.isEnabled()).thenReturn(true);
 
     underTest.beforeComponent(FILE1);
@@ -338,7 +346,7 @@ public class IssueCounterTest {
   }
 
   @Test
-  public void count_impacts() {
+  void onIssue_shouldCountOverallSoftwareQualitiesMeasures() {
     when(newIssueClassifier.isEnabled()).thenReturn(true);
 
     underTest.beforeComponent(FILE1);
@@ -360,9 +368,43 @@ public class IssueCounterTest {
 
     Set<Map.Entry<String, Measure>> entries = measureRepository.getRawMeasures(FILE1).entrySet();
 
-    assertSoftwareQualityMeasures(SoftwareQuality.MAINTAINABILITY, getImpactMeasure(4, 2, 2, 0), entries);
-    assertSoftwareQualityMeasures(SoftwareQuality.SECURITY, getImpactMeasure(2, 1, 1, 0), entries);
-    assertSoftwareQualityMeasures(SoftwareQuality.RELIABILITY, getImpactMeasure(0, 0, 0, 0), entries);
+    assertOverallSoftwareQualityMeasures(SoftwareQuality.MAINTAINABILITY, getImpactMeasure(4, 2, 2, 0), entries);
+    assertOverallSoftwareQualityMeasures(SoftwareQuality.SECURITY, getImpactMeasure(2, 1, 1, 0), entries);
+    assertOverallSoftwareQualityMeasures(SoftwareQuality.RELIABILITY, getImpactMeasure(0, 0, 0, 0), entries);
+  }
+
+  @Test
+  void onIssue_shouldCountNewSoftwareQualitiesMeasures() {
+    when(newIssueClassifier.isEnabled()).thenReturn(true);
+
+    underTest.beforeComponent(FILE1);
+    underTest.onIssue(FILE1, createIssue(RESOLUTION_WONT_FIX, STATUS_OPEN, SoftwareQuality.MAINTAINABILITY,  HIGH));
+    underTest.onIssue(FILE1, createNewIssue(RESOLUTION_WONT_FIX, STATUS_OPEN, SoftwareQuality.MAINTAINABILITY, HIGH));
+    underTest.onIssue(FILE1, createNewIssue(RESOLUTION_WONT_FIX, STATUS_RESOLVED, SoftwareQuality.MAINTAINABILITY, HIGH));
+    underTest.onIssue(FILE1, createNewIssue(RESOLUTION_WONT_FIX, STATUS_OPEN, SoftwareQuality.MAINTAINABILITY, MEDIUM));
+
+    underTest.onIssue(FILE1, createIssue(RESOLUTION_WONT_FIX, STATUS_OPEN, SoftwareQuality.RELIABILITY,  HIGH));
+    underTest.onIssue(FILE1, createNewIssue(RESOLUTION_WONT_FIX, STATUS_OPEN, SoftwareQuality.RELIABILITY, LOW));
+    underTest.onIssue(FILE1, createNewIssue(RESOLUTION_WONT_FIX, STATUS_RESOLVED, SoftwareQuality.RELIABILITY, HIGH));
+    underTest.onIssue(FILE1, createNewIssue(RESOLUTION_WONT_FIX, STATUS_OPEN, SoftwareQuality.RELIABILITY, MEDIUM));
+
+    underTest.onIssue(FILE1, createIssue(RESOLUTION_WONT_FIX, STATUS_OPEN, SoftwareQuality.SECURITY,  MEDIUM));
+    underTest.onIssue(FILE1, createNewIssue(RESOLUTION_WONT_FIX, STATUS_OPEN, SoftwareQuality.SECURITY, LOW));
+    underTest.onIssue(FILE1, createNewIssue(RESOLUTION_WONT_FIX, STATUS_OPEN, SoftwareQuality.SECURITY, HIGH));
+    underTest.onIssue(FILE1, createNewIssue(RESOLUTION_WONT_FIX, STATUS_OPEN, SoftwareQuality.SECURITY, HIGH));
+    underTest.onIssue(FILE1, createNewIssue(RESOLUTION_WONT_FIX, STATUS_RESOLVED, SoftwareQuality.SECURITY, HIGH));
+    underTest.onIssue(FILE1, createNewIssue(RESOLUTION_WONT_FIX, STATUS_OPEN, SoftwareQuality.SECURITY, MEDIUM));
+
+    underTest.afterComponent(FILE1);
+
+    underTest.beforeComponent(PROJECT);
+    underTest.afterComponent(PROJECT);
+
+    Set<Map.Entry<String, Measure>> entries = measureRepository.getRawMeasures(FILE1).entrySet();
+
+    assertNewSoftwareQualityMeasures(SoftwareQuality.MAINTAINABILITY, getImpactMeasure(2, 1, 1, 0), entries);
+    assertNewSoftwareQualityMeasures(SoftwareQuality.RELIABILITY, getImpactMeasure(2, 0, 1, 1), entries);
+    assertNewSoftwareQualityMeasures(SoftwareQuality.SECURITY, getImpactMeasure(4, 2, 1, 1), entries);
   }
 
   private static Map<String, Long> getImpactMeasure(long total, long high, long medium, long low) {
@@ -374,11 +416,21 @@ public class IssueCounterTest {
     return map;
   }
 
-  private void assertSoftwareQualityMeasures(SoftwareQuality softwareQuality, Map<? extends String, Long> expectedMap,
+  private void assertOverallSoftwareQualityMeasures(SoftwareQuality softwareQuality, Map<? extends String, Long> expectedMap,
     Set<Map.Entry<String, Measure>> actualRaw) {
+    assertSoftwareQualityMeasures(softwareQuality, expectedMap, actualRaw, IMPACT_TO_METRIC_KEY);
+  }
+
+  private void assertNewSoftwareQualityMeasures(SoftwareQuality softwareQuality, Map<? extends String, Long> expectedMap,
+    Set<Map.Entry<String, Measure>> actualRaw) {
+    assertSoftwareQualityMeasures(softwareQuality, expectedMap, actualRaw, IMPACT_TO_NEW_METRIC_KEY);
+  }
+
+  private void assertSoftwareQualityMeasures(SoftwareQuality softwareQuality, Map<? extends String, Long> expectedMap,
+    Set<Map.Entry<String, Measure>> actualRaw, Map<String, String> impactToMetricMap) {
 
     Map.Entry<String, Measure> softwareQualityMap = actualRaw.stream()
-      .filter(e -> e.getKey().equals(IMPACT_TO_METRIC_KEY.get(softwareQuality.name())))
+      .filter(e -> e.getKey().equals(impactToMetricMap.get(softwareQuality.name())))
       .findFirst()
       .get();
 
@@ -386,7 +438,7 @@ public class IssueCounterTest {
   }
 
   @Test
-  public void count_high_impact_accepted_issues() {
+  void count_high_impact_accepted_issues() {
     when(newIssueClassifier.isEnabled()).thenReturn(true);
 
     underTest.beforeComponent(FILE1);
@@ -413,7 +465,7 @@ public class IssueCounterTest {
   }
 
   @Test
-  public void exclude_hotspots_from_issue_counts() {
+  void exclude_hotspots_from_issue_counts() {
     // bottom-up traversal -> from files to project
     underTest.beforeComponent(FILE1);
     underTest.onIssue(FILE1, createSecurityHotspot());
@@ -437,7 +489,7 @@ public class IssueCounterTest {
   }
 
   @Test
-  public void exclude_new_hotspots_from_issue_counts() {
+  void exclude_new_hotspots_from_issue_counts() {
     when(newIssueClassifier.isEnabled()).thenReturn(true);
 
     underTest.beforeComponent(FILE1);
@@ -496,7 +548,7 @@ public class IssueCounterTest {
 
   private DefaultIssue createNewIssue(@Nullable String resolution, String status, SoftwareQuality softwareQuality, Severity impactSeverity) {
     DefaultIssue issue = createNewIssue(resolution, status, MAJOR, CODE_SMELL);
-    issue.addImpact(SoftwareQuality.MAINTAINABILITY, impactSeverity);
+    issue.addImpact(softwareQuality, impactSeverity);
     return issue;
   }
 
@@ -522,6 +574,7 @@ public class IssueCounterTest {
 
   private static DefaultIssue createIssue(@Nullable String resolution, String status, String severity, RuleType ruleType) {
     return new DefaultIssue()
+      .setKey(String.valueOf(++issueCounter))
       .setResolution(resolution).setStatus(status)
       .setSeverity(severity).setRuleKey(RuleTesting.XOO_X1)
       .setType(ruleType);
index 015f87d4271db7a3af3ba4c9d2d45f74e1ad0c66..29c6932e53f032e8981b6f1c9c020d0b07cc1c0d 100644 (file)
@@ -37,6 +37,8 @@ import org.jetbrains.annotations.NotNull;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.RegisterExtension;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
 import org.sonar.api.issue.Issue;
 import org.sonar.api.issue.impact.Severity;
 import org.sonar.api.issue.impact.SoftwareQuality;
@@ -562,8 +564,10 @@ class IssueDaoIT {
     assertThat(result.stream().filter(g -> !g.isInLeak()).mapToLong(IssueGroupDto::getCount).sum()).isOne();
   }
 
-  @Test
-  void selectIssueImpactGroupsByComponent_shouldReturnImpactGroups() {
+  @ParameterizedTest
+  @ValueSource(booleans =  {true, false})
+  void selectIssueImpactGroupsByComponent_shouldReturnImpactGroups(boolean inLeak) {
+
     ComponentDto project = db.components().insertPublicProject().getMainBranchComponent();
     ComponentDto file = db.components().insertComponent(ComponentTesting.newFileDto(project));
     RuleDto rule = db.rules().insert();
@@ -581,9 +585,10 @@ class IssueDaoIT {
     db.issues().insert(rule, project, file,
       i -> i.setStatus(STATUS_RESOLVED).setResolution(RESOLUTION_FALSE_POSITIVE).replaceAllImpacts(List.of(createImpact(SECURITY, HIGH))));
 
-    Collection<IssueImpactGroupDto> result = underTest.selectIssueImpactGroupsByComponent(db.getSession(), file);
+    Collection<IssueImpactGroupDto> result = underTest.selectIssueImpactGroupsByComponent(db.getSession(), file, inLeak ? 1L : Long.MAX_VALUE);
 
     assertThat(result).hasSize(5);
+    assertThat(result.stream().filter(IssueImpactGroupDto::isInLeak)).hasSize(inLeak ? 5 : 0);
     assertThat(result.stream().mapToLong(IssueImpactGroupDto::getCount).sum()).isEqualTo(6);
 
     assertThat(result.stream().filter(g -> g.getSoftwareQuality() == SECURITY).mapToLong(IssueImpactGroupDto::getCount).sum()).isEqualTo(4);
@@ -603,6 +608,44 @@ class IssueDaoIT {
     assertThat(result.stream().noneMatch(g -> RESOLUTION_FALSE_POSITIVE.equals(g.getResolution()))).isTrue();
     assertThat(result.stream().noneMatch(g -> RELIABILITY == g.getSoftwareQuality())).isTrue();
     assertThat(result.stream().noneMatch(g -> MEDIUM == g.getSeverity())).isTrue();
+
+  }
+
+  @Test
+  void selectIssueImpactGroupsByComponent_whenNewCodeFromReferenceBranch_shouldReturnImpactGroups() {
+
+    ComponentDto project = db.components().insertPublicProject().getMainBranchComponent();
+    ComponentDto file = db.components().insertComponent(ComponentTesting.newFileDto(project));
+    RuleDto rule = db.rules().insert();
+    IssueDto issueInNewCodePeriod = db.issues().insert(rule, project, file,
+      i -> i.setStatus(STATUS_OPEN).replaceAllImpacts(List.of(createImpact(SECURITY, HIGH), createImpact(MAINTAINABILITY, LOW))));
+    db.issues().insert(rule, project, file,
+      i -> i.setStatus(STATUS_RESOLVED).setResolution(RESOLUTION_WONT_FIX).replaceAllImpacts(List.of(createImpact(SECURITY, HIGH))));
+
+    db.issues().insertNewCodeReferenceIssue(issueInNewCodePeriod);
+
+    Collection<IssueImpactGroupDto> result = underTest.selectIssueImpactGroupsByComponent(db.getSession(), file, -1L);
+
+    assertThat(result).hasSize(3);
+    assertThat(result.stream().filter(IssueImpactGroupDto::isInLeak)).hasSize(2);
+    assertThat(result.stream().mapToLong(IssueImpactGroupDto::getCount).sum()).isEqualTo(3);
+
+    assertThat(result.stream().filter(g -> g.getSoftwareQuality() == SECURITY).mapToLong(IssueImpactGroupDto::getCount).sum()).isEqualTo(2);
+    assertThat(result.stream().filter(g -> g.getSoftwareQuality() == MAINTAINABILITY).mapToLong(IssueImpactGroupDto::getCount).sum()).isEqualTo(1);
+
+    assertThat(result.stream().filter(g -> g.getSeverity() == HIGH).mapToLong(IssueImpactGroupDto::getCount).sum()).isEqualTo(2);
+    assertThat(result.stream().filter(g -> g.getSeverity() == LOW).mapToLong(IssueImpactGroupDto::getCount).sum()).isOne();
+
+    assertThat(result.stream().filter(g -> STATUS_OPEN.equals(g.getStatus())).mapToLong(IssueImpactGroupDto::getCount).sum()).isEqualTo(2);
+    assertThat(result.stream().filter(g -> STATUS_REOPENED.equals(g.getStatus())).mapToLong(IssueImpactGroupDto::getCount).sum()).isZero();
+    assertThat(result.stream().filter(g -> STATUS_RESOLVED.equals(g.getStatus())).mapToLong(IssueImpactGroupDto::getCount).sum()).isOne();
+
+    assertThat(result.stream().filter(g -> RESOLUTION_WONT_FIX.equals(g.getResolution())).mapToLong(IssueImpactGroupDto::getCount).sum()).isOne();
+
+
+    assertThat(result.stream().filter(IssueImpactGroupDto::isInLeak).mapToLong(IssueImpactGroupDto::getCount).sum()).isEqualTo(2);
+    assertThat(result.stream().filter(g -> g.isInLeak() && g.getSoftwareQuality() == SECURITY).mapToLong(IssueImpactGroupDto::getCount).sum()).isEqualTo(1);
+    assertThat(result.stream().filter(g -> g.isInLeak() && g.getSoftwareQuality() == MAINTAINABILITY).mapToLong(IssueImpactGroupDto::getCount).sum()).isEqualTo(1);
   }
 
   @NotNull
@@ -615,7 +658,7 @@ class IssueDaoIT {
     ComponentDto project = db.components().insertPublicProject().getMainBranchComponent();
     ComponentDto file = db.components().insertComponent(ComponentTesting.newFileDto(project));
 
-    Collection<IssueImpactGroupDto> groups = underTest.selectIssueImpactGroupsByComponent(db.getSession(), file);
+    Collection<IssueImpactGroupDto> groups = underTest.selectIssueImpactGroupsByComponent(db.getSession(), file, 1_000L);
 
     assertThat(groups).isEmpty();
   }
index d4708ab2fbda4f0334ec22232726a1b7dba1e2a1..e533bf62a1b33033e3be554f8751eb58dfd79182 100644 (file)
@@ -90,8 +90,8 @@ public class IssueDao implements Dao {
     return mapper(dbSession).selectIssueGroupsByComponent(component, leakPeriodBeginningDate);
   }
 
-  public Collection<IssueImpactGroupDto> selectIssueImpactGroupsByComponent(DbSession dbSession, ComponentDto component) {
-    return mapper(dbSession).selectIssueImpactGroupsByComponent(component);
+  public Collection<IssueImpactGroupDto> selectIssueImpactGroupsByComponent(DbSession dbSession, ComponentDto component, long leakPeriodBeginningDate) {
+    return mapper(dbSession).selectIssueImpactGroupsByComponent(component, leakPeriodBeginningDate);
   }
 
   public Cursor<IndexedIssueDto> scrollIssuesForIndexation(DbSession dbSession, @Nullable @Param("branchUuid") String branchUuid,
index 9455e3a66ec8f2859e1885e901afbac870e08c57..dbf05f35843c9f2dc6e90d0cc22e0b661222be06 100644 (file)
@@ -31,6 +31,7 @@ public class IssueImpactGroupDto {
   private SoftwareQuality softwareQuality;
   private Severity severity;
   private long count;
+  private boolean inLeak;
 
   public IssueImpactGroupDto() {
     // nothing to do
@@ -76,4 +77,12 @@ public class IssueImpactGroupDto {
   public void setCount(long count) {
     this.count = count;
   }
+
+  public boolean isInLeak() {
+    return inLeak;
+  }
+
+  public void setInLeak(boolean inLeak) {
+    this.inLeak = inLeak;
+  }
 }
index 21f43b1773f3b2e4f5c283175ec48b5062039035..b1b3d7f760d8fc0c1a6f66af83fd79be1a8c22ea 100644 (file)
@@ -74,7 +74,7 @@ public interface IssueMapper {
 
   Collection<IssueGroupDto> selectIssueGroupsByComponent(@Param("component") ComponentDto component, @Param("leakPeriodBeginningDate") long leakPeriodBeginningDate);
 
-  Collection<IssueImpactGroupDto> selectIssueImpactGroupsByComponent(@Param("component") ComponentDto component);
+  Collection<IssueImpactGroupDto> selectIssueImpactGroupsByComponent(@Param("component") ComponentDto component, @Param("leakPeriodBeginningDate") long leakPeriodBeginningDate);
 
   List<IssueDto> selectByBranch(@Param("keys") Set<String> keys, @Nullable @Param("changedSince") Long changedSince);
 
index 1b399c4a812fca90566ab669e422ce054ad78e4a..12aafee88dd0fbc112b75e9ccef959f91b8425e7 100644 (file)
 
   <select id="selectIssueImpactGroupsByComponent" resultType="org.sonar.db.issue.IssueImpactGroupDto" parameterType="map">
     select
-    i.status as status,
-    i.resolution as resolution,
-    ii.software_quality as softwareQuality,
-    ii.severity as severity,
-    count(i.kee) as "count"
-    from issues i
-    inner join issues_impacts ii on i.kee = ii.issue_key
-    where i.status &lt;&gt; 'CLOSED'
-    and (i.resolution is null or i.resolution = 'WONTFIX')
-    and i.component_uuid = #{component.uuid,jdbcType=VARCHAR}
-    group by i.status, i.resolution, ii.software_quality, ii.severity
+      i2.status as status,
+      i2.resolution as resolution,
+      i2.software_quality as softwareQuality,
+      i2.severity as severity,
+      count(i2.kee) as "count",
+      i2.inLeak as inLeak
+      from (
+        select
+          i.status,
+          i.resolution,
+          i.kee,
+          ii.software_quality,
+          ii.severity,
+          <if test="leakPeriodBeginningDate &gt;= 0">
+            case when i.issue_creation_date &gt; #{leakPeriodBeginningDate,jdbcType=BIGINT} then ${_true} else ${_false} end as inLeak
+          </if>
+          <if test="leakPeriodBeginningDate &lt; 0">
+            case when n.uuid is null then  ${_false} else ${_true} end as inLeak
+          </if>
+          from issues i
+            inner join issues_impacts ii on i.kee = ii.issue_key
+          <if test="leakPeriodBeginningDate &lt; 0">
+            left join new_code_reference_issues n on n.issue_key = i.kee
+          </if>
+          where i.status &lt;&gt; 'CLOSED'
+          and (i.resolution is null or i.resolution = 'WONTFIX')
+          and i.component_uuid = #{component.uuid,jdbcType=VARCHAR}
+      ) i2
+    group by i2.status, i2.resolution, i2.software_quality, i2.severity, i2.inLeak
   </select>
 
   <select id="selectIssueKeysByComponentUuid" parameterType="string" resultType="string">
index 00535396b66aea150dc171e39c1678cded7aaba0..62165b7ab7a2b1e1013c7b9dc37e8c4c44b81ae0 100644 (file)
@@ -60,6 +60,9 @@ 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.measures.CoreMetrics.MAINTAINABILITY_ISSUES_KEY;
+import static org.sonar.api.measures.CoreMetrics.NEW_MAINTAINABILITY_ISSUES_KEY;
+import static org.sonar.api.measures.CoreMetrics.NEW_RELIABILITY_ISSUES_KEY;
+import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_ISSUES_KEY;
 import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_RATING_KEY;
 import static org.sonar.api.measures.CoreMetrics.RELIABILITY_ISSUES_KEY;
 import static org.sonar.api.measures.CoreMetrics.SECURITY_ISSUES_KEY;
@@ -867,12 +870,10 @@ class ComponentTreeActionIT {
     ComponentDto mainBranch = projectData.getMainBranchComponent();
     addProjectPermission(projectData);
     db.getDbClient().snapshotDao().insert(dbSession, newAnalysis(mainBranch));
-    MetricDto dataMetric = dbClient.metricDao().insert(dbSession, newDataMetricDto(SECURITY_ISSUES_KEY));
-    db.measures().insertLiveMeasure(mainBranch, dataMetric, c -> c.setData(SECURITY_ISSUES_KEY + "_data"));
-    dataMetric = dbClient.metricDao().insert(dbSession, newDataMetricDto(MAINTAINABILITY_ISSUES_KEY));
-    db.measures().insertLiveMeasure(mainBranch, dataMetric, c -> c.setData(MAINTAINABILITY_ISSUES_KEY + "_data"));
-    dataMetric = dbClient.metricDao().insert(dbSession, newDataMetricDto(RELIABILITY_ISSUES_KEY));
-    db.measures().insertLiveMeasure(mainBranch, dataMetric, c -> c.setData(RELIABILITY_ISSUES_KEY + "_data"));
+
+    insertMetricAndLiveMeasure(mainBranch, SECURITY_ISSUES_KEY, "_data");
+    insertMetricAndLiveMeasure(mainBranch, MAINTAINABILITY_ISSUES_KEY, "_data");
+    insertMetricAndLiveMeasure(mainBranch, RELIABILITY_ISSUES_KEY, "_data");
 
     ComponentTreeWsResponse response = ws.newRequest()
       .setParam(PARAM_COMPONENT, mainBranch.getKey())
@@ -891,6 +892,40 @@ class ComponentTreeActionIT {
         tuple(RELIABILITY_ISSUES_KEY, RELIABILITY_ISSUES_KEY + "_data"));
   }
 
+  @Test
+  void execute_whenUsingSupportedNewDATAMetrics_shouldReturnMetrics() {
+    ProjectData projectData = db.components().insertPrivateProject();
+    ComponentDto mainBranch = projectData.getMainBranchComponent();
+    addProjectPermission(projectData);
+    db.components().insertSnapshot(mainBranch);
+    ComponentDto file1 = db.components().insertComponent(newFileDto(mainBranch));
+
+    insertMetricAndLiveMeasure(file1, NEW_SECURITY_ISSUES_KEY, "_data");
+    insertMetricAndLiveMeasure(file1, NEW_MAINTAINABILITY_ISSUES_KEY, "_data");
+    insertMetricAndLiveMeasure(file1, NEW_RELIABILITY_ISSUES_KEY, "_data");
+
+    ComponentTreeWsResponse response = ws.newRequest()
+      .setParam(PARAM_COMPONENT, mainBranch.getKey())
+      .setParam(PARAM_METRIC_KEYS, format("%s,%s,%s",
+        NEW_SECURITY_ISSUES_KEY,
+        NEW_MAINTAINABILITY_ISSUES_KEY,
+        NEW_RELIABILITY_ISSUES_KEY
+      ))
+      .setParam(PARAM_ADDITIONAL_FIELDS, "metrics,period")
+      .executeProtobuf(ComponentTreeWsResponse.class);
+
+    assertThat(response.getComponents(0).getMeasuresCount()).isEqualTo(3);
+
+    List<Measure> dataMeasures = response.getComponents(0).getMeasuresList();
+
+    assertThat(dataMeasures)
+      .extracting(Measure::getMetric, m-> m.getPeriod().getValue())
+      .containsExactlyInAnyOrder(tuple(NEW_SECURITY_ISSUES_KEY, NEW_SECURITY_ISSUES_KEY + "_data"),
+        tuple(NEW_MAINTAINABILITY_ISSUES_KEY, NEW_MAINTAINABILITY_ISSUES_KEY + "_data"),
+        tuple(NEW_RELIABILITY_ISSUES_KEY, NEW_RELIABILITY_ISSUES_KEY + "_data")
+      );
+  }
+
   @Test
   void fail_when_setting_more_than_15_metric_keys() {
     ComponentDto mainBranch = db.components().insertPrivateProject().getMainBranchComponent();
@@ -1125,6 +1160,12 @@ class ComponentTreeActionIT {
       .setOptimizedBestValue(false);
   }
 
+  private void insertMetricAndLiveMeasure(ComponentDto dto,  String key, String additionalData) {
+    MetricDto dataMetric = dbClient.metricDao().insert(dbSession, newDataMetricDto(key));
+    db.measures().insertLiveMeasure(dto, dataMetric, c -> c.setData(key + additionalData));
+
+  }
+
   private static MetricDto newDataMetricDto(String key) {
     return newMetricDto().setValueType(DATA.name()).setKey(key);
   }
index 11cc56ac4545ef02ea1077a234ff204f654b5b26..e2f3840e667dae2292ba182340a711f393506f9e 100644 (file)
@@ -167,16 +167,16 @@ class IssueCounter {
     return onlyInLeak ? count.leak : count.absolute;
   }
 
-  public String getBySoftwareQuality(SoftwareQuality softwareQuality) {
+  public String getBySoftwareQuality(SoftwareQuality softwareQuality, boolean onlyInLeak) {
     Map<Severity, Count> severityToCount = bySoftwareQualityAndSeverity.get(softwareQuality);
 
     ImpactMeasureBuilder impactMeasureBuilder;
     if (severityToCount != null) {
       impactMeasureBuilder = ImpactMeasureBuilder.newInstance();
       for (Severity severity : Severity.values()) {
-        impactMeasureBuilder = impactMeasureBuilder.setSeverity(severity, Optional.ofNullable(severityToCount.get(severity)).map(count -> count.absolute).orElse(0L));
+        impactMeasureBuilder = impactMeasureBuilder.setSeverity(severity, value(severityToCount.get(severity), onlyInLeak));
       }
-      impactMeasureBuilder = impactMeasureBuilder.setTotal(severityToCount.values().stream().mapToLong(count -> count.absolute).sum());
+      impactMeasureBuilder = impactMeasureBuilder.setTotal(severityToCount.values().stream().mapToLong(count -> value(count, onlyInLeak)).sum());
     } else {
       impactMeasureBuilder = ImpactMeasureBuilder.createEmpty();
     }
@@ -197,6 +197,9 @@ class IssueCounter {
 
     public void add(IssueImpactGroupDto group) {
       absolute += group.getCount();
+      if (group.isInLeak()) {
+        leak += group.getCount();
+      }
     }
   }
 
index 71eb40833c831ddb8cb53270bdabd75958d704a4..7cff81bb3d9e98536460406d4dad6d2314c9670d 100644 (file)
@@ -89,7 +89,7 @@ public class LiveMeasureTreeUpdaterImpl implements LiveMeasureTreeUpdater {
 
     components.getSortedTree().forEach(c -> {
       IssueCounter issueCounter = new IssueCounter(dbClient.issueDao().selectIssueGroupsByComponent(dbSession, c, beginningOfLeak),
-        dbClient.issueDao().selectIssueImpactGroupsByComponent(dbSession, c));
+        dbClient.issueDao().selectIssueImpactGroupsByComponent(dbSession, c, beginningOfLeak));
       for (MeasureUpdateFormula formula : formulaFactory.getFormulas()) {
         if (shouldComputeMetric(formula, useLeakFormulas, components.getBranch(), matrix)) {
           context.change(c, formula);
index f70214658aeb401f0e553bf20eb023f510a186e7..24c02872a7973c73bd250c3e4761085051783a0b 100644 (file)
@@ -60,13 +60,22 @@ public class MeasureUpdateFormulaFactoryImpl implements MeasureUpdateFormulaFact
       (context, issues) -> context.setValue(issues.countUnresolvedByType(RuleType.SECURITY_HOTSPOT, false))),
 
     new MeasureUpdateFormula(CoreMetrics.RELIABILITY_ISSUES, false, true, new ImpactAddChildren(),
-      (context, issues) -> context.setValue(issues.getBySoftwareQuality(SoftwareQuality.RELIABILITY))),
+      (context, issues) -> context.setValue(issues.getBySoftwareQuality(SoftwareQuality.RELIABILITY, false))),
 
     new MeasureUpdateFormula(CoreMetrics.MAINTAINABILITY_ISSUES, false, true, new ImpactAddChildren(),
-      (context, issues) -> context.setValue(issues.getBySoftwareQuality(SoftwareQuality.MAINTAINABILITY))),
+      (context, issues) -> context.setValue(issues.getBySoftwareQuality(SoftwareQuality.MAINTAINABILITY, false))),
 
     new MeasureUpdateFormula(CoreMetrics.SECURITY_ISSUES, false, true, new ImpactAddChildren(),
-      (context, issues) -> context.setValue(issues.getBySoftwareQuality(SoftwareQuality.SECURITY))),
+      (context, issues) -> context.setValue(issues.getBySoftwareQuality(SoftwareQuality.SECURITY, false))),
+
+    new MeasureUpdateFormula(CoreMetrics.NEW_RELIABILITY_ISSUES, true, true, new ImpactAddChildren(),
+      (context, issues) -> context.setValue(issues.getBySoftwareQuality(SoftwareQuality.RELIABILITY, true))),
+
+    new MeasureUpdateFormula(CoreMetrics.NEW_MAINTAINABILITY_ISSUES, true, true, new ImpactAddChildren(),
+      (context, issues) -> context.setValue(issues.getBySoftwareQuality(SoftwareQuality.MAINTAINABILITY, true))),
+
+    new MeasureUpdateFormula(CoreMetrics.NEW_SECURITY_ISSUES, true, true, new ImpactAddChildren(),
+      (context, issues) -> context.setValue(issues.getBySoftwareQuality(SoftwareQuality.SECURITY, true))),
 
     new MeasureUpdateFormula(CoreMetrics.VIOLATIONS, false, new AddChildren(),
       (context, issues) -> context.setValue(issues.countUnresolved(false))),
index 3356cd598e857d98b86dd1e26b5cd22096089714..98f87ca1bfb6bdee6b882232fb602fd477d53cdc 100644 (file)
@@ -99,6 +99,7 @@ public class ComponentAction implements MeasuresWsAction {
         new Change("10.5", String.format("The metrics %s are now deprecated " +
                                          "without exact replacement. Use 'maintainability_issues', 'reliability_issues' and 'security_issues' instead.",
           MeasuresWsModule.getDeprecatedMetricsInSonarQube105())),
+        new Change("10.5", "Added new accepted values for the 'metricKeys' param: 'new_maintainability_issues', 'new_reliability_issues', 'new_security_issues'"),
         new Change("10.4", String.format("The metrics %s are now deprecated " +
           "without exact replacement. Use 'maintainability_issues', 'reliability_issues' and 'security_issues' instead.",
           MeasuresWsModule.getDeprecatedMetricsInSonarQube104())),
index 3e65aee8c97d36631770bf4653e90ce083dba50c..845cf6ccc2c1fbd27165fe81d4a53eac84cede4e 100644 (file)
@@ -79,6 +79,9 @@ import static java.util.Collections.emptyList;
 import static java.util.Collections.emptyMap;
 import static java.util.Optional.ofNullable;
 import static org.sonar.api.measures.CoreMetrics.MAINTAINABILITY_ISSUES_KEY;
+import static org.sonar.api.measures.CoreMetrics.NEW_MAINTAINABILITY_ISSUES_KEY;
+import static org.sonar.api.measures.CoreMetrics.NEW_RELIABILITY_ISSUES_KEY;
+import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_ISSUES_KEY;
 import static org.sonar.api.measures.CoreMetrics.RELIABILITY_ISSUES_KEY;
 import static org.sonar.api.measures.CoreMetrics.SECURITY_ISSUES_KEY;
 import static org.sonar.api.measures.Metric.ValueType.DATA;
@@ -184,6 +187,7 @@ public class ComponentTreeAction implements MeasuresWsAction {
       .setHandler(this)
       .addPagingParams(100, MAX_SIZE)
       .setChangelog(
+        new Change("10.5", "Added new accepted values for the 'metricKeys' param: 'new_maintainability_issues', 'new_reliability_issues', 'new_security_issues'"),
         new Change("10.5", String.format("The metrics %s are now deprecated " +
                                          "without exact replacement. Use 'maintainability_issues', 'reliability_issues' and 'security_issues' instead.",
           MeasuresWsModule.getDeprecatedMetricsInSonarQube105())),
@@ -683,7 +687,14 @@ public class ComponentTreeAction implements MeasuresWsAction {
 
     static final Set<String> FORBIDDEN_METRIC_TYPES = Set.of(DISTRIB.name());
     static final Map<String, Set<String>> PARTIALLY_SUPPORTED_METRICS= Map. of(
-      DATA.name(), Set.of(SECURITY_ISSUES_KEY, MAINTAINABILITY_ISSUES_KEY, RELIABILITY_ISSUES_KEY));
+      DATA.name(),
+      Set.of(
+        SECURITY_ISSUES_KEY,
+        MAINTAINABILITY_ISSUES_KEY,
+        RELIABILITY_ISSUES_KEY,
+        NEW_SECURITY_ISSUES_KEY,
+        NEW_MAINTAINABILITY_ISSUES_KEY,
+        NEW_RELIABILITY_ISSUES_KEY));
 
     @Override
     public boolean test(@Nonnull MetricDto input) {
index fc9743db4ea6e167822330626fe03e8b955f2951..6acd198f96444034a37231c4fc9fae9a7b182648 100644 (file)
@@ -90,6 +90,7 @@ public class SearchAction implements MeasuresWsAction {
         new Change("10.5", String.format("The metrics %s are now deprecated " +
                                          "without exact replacement. Use 'maintainability_issues', 'reliability_issues' and 'security_issues' instead.",
           MeasuresWsModule.getDeprecatedMetricsInSonarQube105())),
+        new Change("10.5", "Added new accepted values for the 'metricKeys' param: 'new_maintainability_issues', 'new_reliability_issues', 'new_security_issues'"),
         new Change("10.4", String.format("The metrics %s are now deprecated " +
             "without exact replacement. Use 'maintainability_issues', 'reliability_issues' and 'security_issues' instead.",
           MeasuresWsModule.getDeprecatedMetricsInSonarQube104())),
index 1c63a887e3f53cf6298f73a350a041b95aada309..2f47507fd31c275f5ecf0aadeea5d50cb01938d7 100644 (file)
@@ -98,6 +98,7 @@ public class SearchHistoryAction implements MeasuresWsAction {
         new Change("10.5", String.format("The metrics %s are now deprecated " +
                                          "without exact replacement. Use 'maintainability_issues', 'reliability_issues' and 'security_issues' instead.",
           MeasuresWsModule.getDeprecatedMetricsInSonarQube105())),
+        new Change("10.5", "Added new accepted values for the 'metricKeys' param: 'new_maintainability_issues', 'new_reliability_issues', 'new_security_issues'"),
         new Change("10.4", String.format("The metrics %s are now deprecated " +
             "without exact replacement. Use 'maintainability_issues', 'reliability_issues' and 'security_issues' instead.",
           MeasuresWsModule.getDeprecatedMetricsInSonarQube104())),
index b8e35b8882b970f952d4848a1b458ea75eb89b81..b3ef284a41dc191e54a7b524c65506d10b38f024 100644 (file)
@@ -31,7 +31,7 @@ import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
 import javax.annotation.Nullable;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
 import org.sonar.api.issue.Issue;
 import org.sonar.api.issue.impact.SoftwareQuality;
 import org.sonar.api.measures.CoreMetrics;
@@ -62,13 +62,13 @@ import static org.sonar.api.measures.CoreMetrics.SECURITY_HOTSPOTS_TO_REVIEW_STA
 import static org.sonar.api.measures.CoreMetrics.SECURITY_REVIEW_RATING;
 import static org.sonar.test.JsonAssert.assertJson;
 
-public class MeasureUpdateFormulaFactoryImplTest {
+class MeasureUpdateFormulaFactoryImplTest {
 
-  public static final Gson GSON = new GsonBuilder().create();
+  private static final Gson GSON = new GsonBuilder().create();
   private final MeasureUpdateFormulaFactoryImpl underTest = new MeasureUpdateFormulaFactoryImpl();
 
   @Test
-  public void getFormulaMetrics_include_the_dependent_metrics() {
+  void getFormulaMetrics_include_the_dependent_metrics() {
     for (MeasureUpdateFormula formula : underTest.getFormulas()) {
       assertThat(underTest.getFormulaMetrics()).contains(formula.getMetric());
       for (Metric<?> dependentMetric : formula.getDependentMetrics()) {
@@ -78,7 +78,7 @@ public class MeasureUpdateFormulaFactoryImplTest {
   }
 
   @Test
-  public void hierarchy_adding_numbers() {
+  void hierarchy_adding_numbers() {
     new HierarchyTester(CoreMetrics.VIOLATIONS)
       .withValue(1d)
       .withChildrenValues(2d, 3d)
@@ -95,7 +95,7 @@ public class MeasureUpdateFormulaFactoryImplTest {
   }
 
   @Test
-  public void hierarchy_highest_rating() {
+  void hierarchy_highest_rating() {
     new HierarchyTester(CoreMetrics.RELIABILITY_RATING)
       .withValue(1d)
       .withChildrenValues(2d, 3d)
@@ -113,7 +113,7 @@ public class MeasureUpdateFormulaFactoryImplTest {
   }
 
   @Test
-  public void hierarchy_combining_other_metrics() {
+  void hierarchy_combining_other_metrics() {
     new HierarchyTester(SECURITY_HOTSPOTS_TO_REVIEW_STATUS)
       .withValue(SECURITY_HOTSPOTS_TO_REVIEW_STATUS, 1d)
       .withChildrenHotspotsCounts(10, 10, 2, 10)
@@ -152,7 +152,7 @@ public class MeasureUpdateFormulaFactoryImplTest {
   }
 
   @Test
-  public void test_violations() {
+  void test_violations() {
     withNoIssues().assertThatValueIs(CoreMetrics.VIOLATIONS, 0);
     with(newGroup(), newGroup().setCount(4)).assertThatValueIs(CoreMetrics.VIOLATIONS, 5);
 
@@ -166,7 +166,7 @@ public class MeasureUpdateFormulaFactoryImplTest {
   }
 
   @Test
-  public void test_bugs() {
+  void test_bugs() {
     withNoIssues().assertThatValueIs(CoreMetrics.BUGS, 0);
     with(
       newGroup(RuleType.BUG).setSeverity(Severity.MAJOR).setCount(3),
@@ -179,7 +179,7 @@ public class MeasureUpdateFormulaFactoryImplTest {
   }
 
   @Test
-  public void test_code_smells() {
+  void test_code_smells() {
     withNoIssues().assertThatValueIs(CoreMetrics.CODE_SMELLS, 0);
     with(
       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.MAJOR).setCount(3),
@@ -192,7 +192,7 @@ public class MeasureUpdateFormulaFactoryImplTest {
   }
 
   @Test
-  public void test_vulnerabilities() {
+  void test_vulnerabilities() {
     withNoIssues().assertThatValueIs(CoreMetrics.VULNERABILITIES, 0);
     with(
       newGroup(RuleType.VULNERABILITY).setSeverity(Severity.MAJOR).setCount(3),
@@ -205,7 +205,7 @@ public class MeasureUpdateFormulaFactoryImplTest {
   }
 
   @Test
-  public void test_security_hotspots() {
+  void test_security_hotspots() {
     withNoIssues().assertThatValueIs(CoreMetrics.SECURITY_HOTSPOTS, 0);
     with(
       newGroup(RuleType.SECURITY_HOTSPOT).setSeverity(Severity.MAJOR).setCount(3),
@@ -218,7 +218,7 @@ public class MeasureUpdateFormulaFactoryImplTest {
   }
 
   @Test
-  public void test_security_review_rating() {
+  void test_security_review_rating() {
     with(
       newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED).setCount(3),
       newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW).setCount(1))
@@ -229,7 +229,7 @@ public class MeasureUpdateFormulaFactoryImplTest {
   }
 
   @Test
-  public void test_security_hotspots_reviewed() {
+  void test_security_hotspots_reviewed() {
     with(
       newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED).setCount(3),
       newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW).setCount(1))
@@ -240,7 +240,7 @@ public class MeasureUpdateFormulaFactoryImplTest {
   }
 
   @Test
-  public void test_security_hotspots_reviewed_status() {
+  void test_security_hotspots_reviewed_status() {
     with(
       newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED).setCount(3),
       newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW).setCount(1))
@@ -251,7 +251,7 @@ public class MeasureUpdateFormulaFactoryImplTest {
   }
 
   @Test
-  public void test_security_hotspots_to_review_status() {
+  void test_security_hotspots_to_review_status() {
     with(
       newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED).setCount(3),
       newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW).setCount(1))
@@ -262,7 +262,7 @@ public class MeasureUpdateFormulaFactoryImplTest {
   }
 
   @Test
-  public void count_unresolved_by_severity() {
+  void count_unresolved_by_severity() {
     withNoIssues()
       .assertThatValueIs(CoreMetrics.BLOCKER_VIOLATIONS, 0)
       .assertThatValueIs(CoreMetrics.CRITICAL_VIOLATIONS, 0)
@@ -291,7 +291,7 @@ public class MeasureUpdateFormulaFactoryImplTest {
   }
 
   @Test
-  public void count_resolved() {
+  void count_resolved() {
     withNoIssues()
       .assertThatValueIs(CoreMetrics.FALSE_POSITIVE_ISSUES, 0)
       .assertThatValueIs(CoreMetrics.ACCEPTED_ISSUES, 0);
@@ -312,7 +312,7 @@ public class MeasureUpdateFormulaFactoryImplTest {
   }
 
   @Test
-  public void count_by_status() {
+  void count_by_status() {
     withNoIssues()
       .assertThatValueIs(CoreMetrics.CONFIRMED_ISSUES, 0)
       .assertThatValueIs(CoreMetrics.OPEN_ISSUES, 0)
@@ -333,7 +333,7 @@ public class MeasureUpdateFormulaFactoryImplTest {
   }
 
   @Test
-  public void test_technical_debt() {
+  void test_technical_debt() {
     withNoIssues().assertThatValueIs(CoreMetrics.TECHNICAL_DEBT, 0);
 
     with(
@@ -350,7 +350,7 @@ public class MeasureUpdateFormulaFactoryImplTest {
   }
 
   @Test
-  public void test_reliability_remediation_effort() {
+  void test_reliability_remediation_effort() {
     withNoIssues().assertThatValueIs(CoreMetrics.RELIABILITY_REMEDIATION_EFFORT, 0);
 
     with(
@@ -364,7 +364,7 @@ public class MeasureUpdateFormulaFactoryImplTest {
   }
 
   @Test
-  public void test_security_remediation_effort() {
+  void test_security_remediation_effort() {
     withNoIssues().assertThatValueIs(CoreMetrics.SECURITY_REMEDIATION_EFFORT, 0);
 
     with(
@@ -378,7 +378,7 @@ public class MeasureUpdateFormulaFactoryImplTest {
   }
 
   @Test
-  public void test_sqale_debt_ratio_and_sqale_rating() {
+  void test_sqale_debt_ratio_and_sqale_rating() {
     withNoIssues()
       .assertThatValueIs(CoreMetrics.SQALE_DEBT_RATIO, 0)
       .assertThatValueIs(CoreMetrics.SQALE_RATING, Rating.A);
@@ -449,7 +449,7 @@ public class MeasureUpdateFormulaFactoryImplTest {
   }
 
   @Test
-  public void test_effort_to_reach_maintainability_rating_A() {
+  void test_effort_to_reach_maintainability_rating_A() {
     withNoIssues()
       .assertThatValueIs(CoreMetrics.EFFORT_TO_REACH_MAINTAINABILITY_RATING_A, 0.0);
 
@@ -493,7 +493,7 @@ public class MeasureUpdateFormulaFactoryImplTest {
   }
 
   @Test
-  public void test_reliability_rating() {
+  void test_reliability_rating() {
     withNoIssues()
       .assertThatValueIs(CoreMetrics.RELIABILITY_RATING, Rating.A);
 
@@ -513,7 +513,7 @@ public class MeasureUpdateFormulaFactoryImplTest {
   }
 
   @Test
-  public void test_security_rating() {
+  void test_security_rating() {
     withNoIssues()
       .assertThatValueIs(CoreMetrics.SECURITY_RATING, Rating.A);
 
@@ -533,7 +533,7 @@ public class MeasureUpdateFormulaFactoryImplTest {
   }
 
   @Test
-  public void test_new_bugs() {
+  void test_new_bugs() {
     withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_BUGS, 0.0);
 
     with(
@@ -548,7 +548,7 @@ public class MeasureUpdateFormulaFactoryImplTest {
   }
 
   @Test
-  public void test_new_code_smells() {
+  void test_new_code_smells() {
     withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_CODE_SMELLS, 0.0);
 
     with(
@@ -562,7 +562,7 @@ public class MeasureUpdateFormulaFactoryImplTest {
   }
 
   @Test
-  public void test_new_vulnerabilities() {
+  void test_new_vulnerabilities() {
     withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_VULNERABILITIES, 0.0);
 
     with(
@@ -576,7 +576,7 @@ public class MeasureUpdateFormulaFactoryImplTest {
   }
 
   @Test
-  public void test_new_security_hotspots() {
+  void test_new_security_hotspots() {
     withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_HOTSPOTS, 0);
 
     with(
@@ -590,7 +590,7 @@ public class MeasureUpdateFormulaFactoryImplTest {
   }
 
   @Test
-  public void test_new_violations() {
+  void test_new_violations() {
     withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_VIOLATIONS, 0.0);
 
     with(
@@ -605,7 +605,7 @@ public class MeasureUpdateFormulaFactoryImplTest {
   }
 
   @Test
-  public void test_new_blocker_violations() {
+  void test_new_blocker_violations() {
     withNoIssues()
       .assertThatLeakValueIs(CoreMetrics.NEW_BLOCKER_VIOLATIONS, 0.0);
 
@@ -622,7 +622,7 @@ public class MeasureUpdateFormulaFactoryImplTest {
   }
 
   @Test
-  public void test_new_critical_violations() {
+  void test_new_critical_violations() {
     withNoIssues()
       .assertThatLeakValueIs(CoreMetrics.NEW_CRITICAL_VIOLATIONS, 0.0);
 
@@ -639,7 +639,7 @@ public class MeasureUpdateFormulaFactoryImplTest {
   }
 
   @Test
-  public void test_new_major_violations() {
+  void test_new_major_violations() {
     withNoIssues()
       .assertThatLeakValueIs(CoreMetrics.NEW_MAJOR_VIOLATIONS, 0.0);
 
@@ -656,7 +656,7 @@ public class MeasureUpdateFormulaFactoryImplTest {
   }
 
   @Test
-  public void test_new_minor_violations() {
+  void test_new_minor_violations() {
     withNoIssues()
       .assertThatLeakValueIs(CoreMetrics.NEW_MINOR_VIOLATIONS, 0.0);
 
@@ -673,7 +673,7 @@ public class MeasureUpdateFormulaFactoryImplTest {
   }
 
   @Test
-  public void test_new_info_violations() {
+  void test_new_info_violations() {
     withNoIssues()
       .assertThatLeakValueIs(CoreMetrics.NEW_INFO_VIOLATIONS, 0.0);
 
@@ -690,7 +690,7 @@ public class MeasureUpdateFormulaFactoryImplTest {
   }
 
   @Test
-  public void test_new_accepted_issues() {
+  void test_new_accepted_issues() {
     withNoIssues()
       .assertThatLeakValueIs(CoreMetrics.NEW_ACCEPTED_ISSUES, 0);
 
@@ -706,7 +706,7 @@ public class MeasureUpdateFormulaFactoryImplTest {
   }
 
   @Test
-  public void test_new_technical_debt() {
+  void test_new_technical_debt() {
     withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_TECHNICAL_DEBT, 0.0);
 
     with(
@@ -722,7 +722,7 @@ public class MeasureUpdateFormulaFactoryImplTest {
   }
 
   @Test
-  public void test_new_reliability_remediation_effort() {
+  void test_new_reliability_remediation_effort() {
     withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_RELIABILITY_REMEDIATION_EFFORT, 0.0);
 
     with(
@@ -737,7 +737,7 @@ public class MeasureUpdateFormulaFactoryImplTest {
   }
 
   @Test
-  public void test_new_security_remediation_effort() {
+  void test_new_security_remediation_effort() {
     withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_REMEDIATION_EFFORT, 0.0);
 
     with(
@@ -752,7 +752,7 @@ public class MeasureUpdateFormulaFactoryImplTest {
   }
 
   @Test
-  public void test_new_reliability_rating() {
+  void test_new_reliability_rating() {
     withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_RELIABILITY_RATING, Rating.A);
 
     with(
@@ -769,7 +769,7 @@ public class MeasureUpdateFormulaFactoryImplTest {
   }
 
   @Test
-  public void test_new_security_rating() {
+  void test_new_security_rating() {
     withNoIssues().assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_RATING, Rating.A);
 
     with(
@@ -786,7 +786,7 @@ public class MeasureUpdateFormulaFactoryImplTest {
   }
 
   @Test
-  public void test_new_security_review_rating() {
+  void test_new_security_review_rating() {
     with(
       newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED).setCount(3).setInLeak(true),
       newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW).setCount(1).setInLeak(true),
@@ -799,7 +799,7 @@ public class MeasureUpdateFormulaFactoryImplTest {
   }
 
   @Test
-  public void test_new_security_hotspots_reviewed() {
+  void test_new_security_hotspots_reviewed() {
     with(
       newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED).setCount(3).setInLeak(true),
       newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW).setCount(1).setInLeak(true),
@@ -812,7 +812,7 @@ public class MeasureUpdateFormulaFactoryImplTest {
   }
 
   @Test
-  public void test_new_security_hotspots_reviewed_status() {
+  void test_new_security_hotspots_reviewed_status() {
     with(
       newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED).setCount(3).setInLeak(true),
       newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW).setCount(1).setInLeak(true),
@@ -825,7 +825,7 @@ public class MeasureUpdateFormulaFactoryImplTest {
   }
 
   @Test
-  public void test_new_security_hotspots_to_review_status() {
+  void test_new_security_hotspots_to_review_status() {
     with(
       newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED).setCount(3).setInLeak(true),
       newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW).setCount(1).setInLeak(true),
@@ -838,7 +838,7 @@ public class MeasureUpdateFormulaFactoryImplTest {
   }
 
   @Test
-  public void test_new_sqale_debt_ratio_and_new_maintainability_rating() {
+  void test_new_sqale_debt_ratio_and_new_maintainability_rating() {
     withNoIssues()
       .assertThatLeakValueIs(CoreMetrics.NEW_SQALE_DEBT_RATIO, 0)
       .assertThatLeakValueIs(CoreMetrics.NEW_MAINTAINABILITY_RATING, Rating.A);
@@ -909,7 +909,7 @@ public class MeasureUpdateFormulaFactoryImplTest {
   }
 
   @Test
-  public void compute_shouldComputeHighImpactAcceptedIssues() {
+  void compute_shouldComputeHighImpactAcceptedIssues() {
     withNoIssues()
       .assertThatValueIs(CoreMetrics.HIGH_IMPACT_ACCEPTED_ISSUES, 0);
 
@@ -927,7 +927,7 @@ public class MeasureUpdateFormulaFactoryImplTest {
   }
 
   @Test
-  public void computeHierarchy_shouldComputeImpactMeasures() {
+  void computeHierarchy_shouldComputeImpactMeasures() {
     new HierarchyTester(CoreMetrics.RELIABILITY_ISSUES)
       .withValue(impactMeasureToJson(6, 1, 2, 3))
       .withChildrenValues(impactMeasureToJson(6, 1, 2, 3), impactMeasureToJson(10, 5, 3, 2))
@@ -939,7 +939,7 @@ public class MeasureUpdateFormulaFactoryImplTest {
   }
 
   @Test
-  public void compute_shouldComputeImpactMeasures() {
+  void compute_shouldComputeImpactMeasures() {
     with(
       newImpactGroup(RELIABILITY, HIGH, 3),
       newImpactGroup(RELIABILITY, MEDIUM, 4),
@@ -953,11 +953,14 @@ public class MeasureUpdateFormulaFactoryImplTest {
   }
 
   @Test
-  public void compute_whenNoIssues_shouldComputeImpactMeasures() {
+  void compute_whenNoIssues_shouldComputeImpactMeasures() {
     withNoIssues()
       .assertThatJsonValueIs(CoreMetrics.RELIABILITY_ISSUES, impactMeasureToJson(0, 0, 0, 0))
       .assertThatJsonValueIs(CoreMetrics.MAINTAINABILITY_ISSUES, impactMeasureToJson(0, 0, 0, 0))
-      .assertThatJsonValueIs(CoreMetrics.SECURITY_ISSUES, impactMeasureToJson(0, 0, 0, 0));
+      .assertThatJsonValueIs(CoreMetrics.SECURITY_ISSUES, impactMeasureToJson(0, 0, 0, 0))
+      .assertThatLeakValueIs(CoreMetrics.NEW_RELIABILITY_ISSUES, impactMeasureToJson(0, 0, 0, 0))
+      .assertThatLeakValueIs(CoreMetrics.NEW_MAINTAINABILITY_ISSUES, impactMeasureToJson(0, 0, 0, 0))
+      .assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_ISSUES, impactMeasureToJson(0, 0, 0, 0));
   }
 
   private static String impactMeasureToJson(long total, long high, long medium, long low) {
@@ -1031,6 +1034,12 @@ public class MeasureUpdateFormulaFactoryImplTest {
       return this;
     }
 
+    Verifier assertThatLeakValueIs(Metric metric, String expectedValue) {
+      TestContext context = run(metric, true);
+      assertJson(context.stringValue).isSimilarTo(expectedValue);
+      return this;
+    }
+
     Verifier assertNoLeakValue(Metric metric) {
       TestContext context = run(metric, true);
       assertThat(context.ratingValue).isNull();
index 7d30247113949811a78e4b1f856e4bce707b86b3..b92c78759adeced31b0aaf3ed96dc98726937bd6 100644 (file)
@@ -3295,6 +3295,15 @@ metric.reliability_issues.short_name=Reliability
 metric.security_issues.name=Security Issues
 metric.security_issues.description=Security issues
 metric.security_issues.short_name=Security
+metric.new_maintainability_issues.name=New Maintainability Issues
+metric.new_maintainability_issues.description=New maintainability issues
+metric.new_maintainability_issues.short_name=New Maintainability
+metric.new_reliability_issues.name=New Reliability Issues
+metric.new_reliability_issues.description=New reliability issues
+metric.new_reliability_issues.short_name=New Reliability
+metric.new_security_issues.name=New Security Issues
+metric.new_security_issues.description=New security issues
+metric.new_security_issues.short_name=New Security
 
 #------------------------------------------------------------------------------
 #