]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-22951 Use 5 levels severities for Software Impact
authorDejan Milisavljevic <130993898+dejan-milisavljevic-sonarsource@users.noreply.github.com>
Wed, 18 Sep 2024 12:03:50 +0000 (14:03 +0200)
committersonartech <sonartech@sonarsource.com>
Wed, 18 Sep 2024 20:02:59 +0000 (20:02 +0000)
Co-authored-by: Léo Geoffroy <leo.geoffroy@sonarsource.com>
Co-authored-by: Stanislav <31501873+stanislavhh@users.noreply.github.com>
Co-authored-by: Viktor Vorona <viktor.vorona@sonarsource.com>
Co-authored-by: OrlovAlexander <35396155+OrlovAlexander85@users.noreply.github.com>
Co-authored-by: stanislavh <stanislav.honcharov@sonarsource.com>
132 files changed:
gradle.properties
plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/OneIssuePerLineSensor.java
plugins/sonar-xoo-plugin/src/main/java/org/sonar/xoo/rule/XooRulesDefinition.java
server/sonar-ce-task-projectanalysis/src/it/java/org/sonar/ce/task/projectexport/issue/ExportIssuesStepIT.java
server/sonar-ce-task-projectanalysis/src/it/java/org/sonar/ce/task/projectexport/rule/ExportAdHocRulesStepIT.java
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/qualitymodel/MaintainabilityMeasuresVisitor.java
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/qualitymodel/NewMaintainabilityMeasuresVisitor.java
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/qualitymodel/NewSecurityReviewMeasuresVisitor.java
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/qualitymodel/SecurityReviewMeasuresVisitor.java
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/PersistLiveMeasuresStep.java
server/sonar-ce-task-projectanalysis/src/main/protobuf/project_dump.proto
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/IssueCounterTest.java
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/qualitymodel/MaintainabilityMeasuresVisitorTest.java
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/qualitymodel/NewMaintainabilityMeasuresVisitorTest.java
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/qualitymodel/NewReliabilityAndSecurityRatingMeasuresVisitorTest.java
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/qualitymodel/NewSecurityReviewMeasuresVisitorTest.java
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/qualitymodel/ReliabilityAndSecurityRatingMeasuresVisitorTest.java
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/qualitymodel/SecurityReviewMeasuresVisitorTest.java
server/sonar-db-dao/src/main/java/org/sonar/db/measure/ProjectMeasuresIndexerIterator.java
server/sonar-server-common/src/it/java/org/sonar/server/issue/index/IssueIndexerIT.java
server/sonar-server-common/src/it/java/org/sonar/server/rule/index/RuleIndexIT.java
server/sonar-server-common/src/main/java/org/sonar/server/measure/DebtRatingGrid.java
server/sonar-server-common/src/main/java/org/sonar/server/measure/ImpactMeasureBuilder.java
server/sonar-server-common/src/main/java/org/sonar/server/measure/Rating.java
server/sonar-server-common/src/main/java/org/sonar/server/measure/index/ProjectMeasuresSoftwareQualityRatingsInitializer.java
server/sonar-server-common/src/main/java/org/sonar/server/security/SecurityReviewRating.java
server/sonar-server-common/src/test/java/org/sonar/server/measure/DebtRatingGridTest.java
server/sonar-server-common/src/test/java/org/sonar/server/measure/ImpactMeasureBuilderTest.java
server/sonar-server-common/src/test/java/org/sonar/server/measure/index/ProjectMeasuresSoftwareQualityRatingsInitializerTest.java
server/sonar-server-common/src/test/java/org/sonar/server/security/SecurityReviewRatingTest.java
server/sonar-web/config/jest/SetupReactTestingLibrary.ts
server/sonar-web/design-system/src/components/ColorsLegend.tsx
server/sonar-web/design-system/src/components/FacetBox.tsx
server/sonar-web/design-system/src/components/Pill.tsx
server/sonar-web/design-system/src/components/__tests__/Pill-test.tsx
server/sonar-web/design-system/src/components/__tests__/__snapshots__/HotspotRating-test.tsx.snap
server/sonar-web/design-system/src/components/icons/SoftwareImpactSeverityBlockerIcon.tsx [new file with mode: 0644]
server/sonar-web/design-system/src/components/icons/SoftwareImpactSeverityHighIcon.tsx
server/sonar-web/design-system/src/components/icons/SoftwareImpactSeverityInfoIcon.tsx [new file with mode: 0644]
server/sonar-web/design-system/src/components/icons/SoftwareImpactSeverityLowIcon.tsx
server/sonar-web/design-system/src/components/icons/SoftwareImpactSeverityMediumIcon.tsx
server/sonar-web/design-system/src/components/icons/index.ts
server/sonar-web/design-system/src/components/index.ts
server/sonar-web/design-system/src/sonar-aligned/components/MetricsRatingBadge.tsx
server/sonar-web/design-system/src/theme/light.ts
server/sonar-web/src/main/js/app/components/ChangeInCalculationPill.tsx
server/sonar-web/src/main/js/app/components/metrics/RatingComponent.tsx
server/sonar-web/src/main/js/apps/code/__tests__/__snapshots__/utils-test.tsx.snap
server/sonar-web/src/main/js/apps/code/components/CodeAppRenderer.tsx
server/sonar-web/src/main/js/apps/code/utils.ts
server/sonar-web/src/main/js/apps/coding-rules/__tests__/CodingRules-it.ts
server/sonar-web/src/main/js/apps/coding-rules/__tests__/CustomRule-it.ts
server/sonar-web/src/main/js/apps/coding-rules/components/AttributeCategoryFacet.tsx
server/sonar-web/src/main/js/apps/coding-rules/components/CustomRuleFormFieldsCCT.tsx
server/sonar-web/src/main/js/apps/coding-rules/components/Facet.tsx [deleted file]
server/sonar-web/src/main/js/apps/coding-rules/components/FacetsList.tsx
server/sonar-web/src/main/js/apps/coding-rules/components/InheritanceFacet.tsx
server/sonar-web/src/main/js/apps/coding-rules/components/PrioritizedRulesFacet.tsx
server/sonar-web/src/main/js/apps/coding-rules/components/RepositoryFacet.tsx
server/sonar-web/src/main/js/apps/coding-rules/components/SeverityFacet.tsx [deleted file]
server/sonar-web/src/main/js/apps/coding-rules/components/SoftwareQualityFacet.tsx
server/sonar-web/src/main/js/apps/coding-rules/components/StatusFacet.tsx
server/sonar-web/src/main/js/apps/coding-rules/components/TagFacet.tsx
server/sonar-web/src/main/js/apps/coding-rules/components/TemplateFacet.tsx
server/sonar-web/src/main/js/apps/coding-rules/components/TypeFacet.tsx
server/sonar-web/src/main/js/apps/component-measures/components/MeasureContent.tsx
server/sonar-web/src/main/js/apps/component-measures/drilldown/BubbleChartView.tsx
server/sonar-web/src/main/js/apps/component-measures/drilldown/ColorRatingsLegend.tsx
server/sonar-web/src/main/js/apps/component-measures/drilldown/TreeMapView.tsx
server/sonar-web/src/main/js/apps/component-measures/utils.ts
server/sonar-web/src/main/js/apps/issues/__tests__/IssueHeader-it.tsx
server/sonar-web/src/main/js/apps/issues/sidebar/SeverityFacet.tsx [deleted file]
server/sonar-web/src/main/js/apps/issues/sidebar/Sidebar.tsx
server/sonar-web/src/main/js/apps/issues/sidebar/__tests__/Sidebar-it.tsx
server/sonar-web/src/main/js/apps/issues/test-utils.tsx
server/sonar-web/src/main/js/apps/overview/branches/OverallCodeMeasuresPanel.tsx
server/sonar-web/src/main/js/apps/overview/branches/SoftwareImpactMeasureBreakdownCard.tsx [deleted file]
server/sonar-web/src/main/js/apps/overview/branches/SoftwareImpactMeasureCard.tsx
server/sonar-web/src/main/js/apps/overview/branches/__tests__/BranchOverview-it.tsx
server/sonar-web/src/main/js/apps/overview/branches/test-utils.ts
server/sonar-web/src/main/js/apps/overview/utils.tsx
server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityGraphs.tsx
server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/ProjectActivityApp-it.tsx
server/sonar-web/src/main/js/apps/projects/components/__tests__/PageSidebar-test.tsx
server/sonar-web/src/main/js/apps/projects/components/project-card/__tests__/ProjectCard-test.tsx
server/sonar-web/src/main/js/apps/projects/filters/RatingFacet.tsx
server/sonar-web/src/main/js/apps/projects/utils.ts
server/sonar-web/src/main/js/apps/quality-gates/components/AddConditionModal.tsx
server/sonar-web/src/main/js/components/activity-graph/GraphHistory.tsx
server/sonar-web/src/main/js/components/activity-graph/GraphsHistory.tsx
server/sonar-web/src/main/js/components/charts/AdvancedTimeline.tsx
server/sonar-web/src/main/js/components/charts/__tests__/AdvancedTimeline-test.tsx
server/sonar-web/src/main/js/components/charts/__tests__/__snapshots__/AdvancedTimeline-test.tsx.snap
server/sonar-web/src/main/js/components/facets/Facet.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/facets/SeverityFacet.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/icon-mappers/SoftwareImpactSeverityIcon.tsx
server/sonar-web/src/main/js/components/measure/utils.ts
server/sonar-web/src/main/js/components/shared/CleanCodeAttributePill.tsx
server/sonar-web/src/main/js/components/shared/SoftwareImpactPill.tsx
server/sonar-web/src/main/js/components/shared/SoftwareImpactPillList.tsx
server/sonar-web/src/main/js/helpers/activity-graph.ts
server/sonar-web/src/main/js/helpers/constants.ts
server/sonar-web/src/main/js/sonar-aligned/types/metrics.ts
server/sonar-web/src/main/js/types/clean-code-taxonomy.ts
server/sonar-web/src/main/js/types/jest.d.ts
server/sonar-webserver-es/src/main/java/org/sonar/server/measure/index/ProjectMeasuresIndex.java
server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueIndexFacetsTest.java
server/sonar-webserver-es/src/test/java/org/sonar/server/measure/index/ProjectMeasuresIndexTest.java
server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/rule/enums/ImpactSeverityRestEnum.java
server/sonar-webserver-webapi/src/it/java/org/sonar/server/component/ws/SearchProjectsActionIT.java
server/sonar-webserver-webapi/src/it/java/org/sonar/server/issue/ws/ListActionIT.java
server/sonar-webserver-webapi/src/it/java/org/sonar/server/issue/ws/PullTaintActionIT.java
server/sonar-webserver-webapi/src/it/java/org/sonar/server/issue/ws/SearchActionIT.java
server/sonar-webserver-webapi/src/it/java/org/sonar/server/rule/ws/CreateActionIT.java
server/sonar-webserver-webapi/src/it/java/org/sonar/server/rule/ws/SearchActionIT.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ImpactFormatter.java [new file with mode: 0644]
server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SearchResponseFormat.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/pull/PullTaintActionProtobufObjectGenerator.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/MeasuresWsModule.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/rule/ws/RuleMapper.java
server/sonar-webserver-webapi/src/test/java/org/sonar/server/issue/ImpactFormatterTest.java [new file with mode: 0644]
server/sonar-webserver-webapi/src/test/java/org/sonar/server/measure/live/MeasureUpdateFormulaFactoryImplTest.java
server/sonar-webserver-webapi/src/test/java/org/sonar/server/measure/ws/MeasuresWsModuleTest.java
sonar-core/src/main/java/org/sonar/core/metric/SoftwareQualitiesMetrics.java
sonar-core/src/main/java/org/sonar/core/util/ProtobufJsonFormat.java
sonar-core/src/main/resources/org/sonar/l10n/core.properties
sonar-core/src/test/java/org/sonar/core/metric/SoftwareQualitiesMetricsTest.java
sonar-core/src/test/java/org/sonar/core/util/ProtobufJsonFormatTest.java
sonar-core/src/test/protobuf/test.proto
sonar-scanner-engine/src/test/java/org/sonar/scanner/externalissue/ExternalIssueImporterTest.java
sonar-ws/src/main/protobuf/ws-commons.proto

index 435bf57a80ca84ab2026719b7fa7dcf1e6bed966..fe52ff22787a1c7f068ee90fd9459315cca63286 100644 (file)
@@ -6,7 +6,7 @@ version=10.7
 # 30 months from the release date for LTA versions
 # No change required for patch versions
 versionEOL=2025-03-27
-pluginApiVersion=10.10.0.2391
+pluginApiVersion=10.11.0.2468
 description=Open source platform for continuous inspection of code quality
 projectTitle=SonarQube
 org.gradle.jvmargs=-Xmx2048m
index 9110916977dfe4cbb77661e03ca6e65c0b972322..9ca57ae518ddaf91dffe94242f492c9c5954805f 100644 (file)
@@ -96,14 +96,16 @@ public class OneIssuePerLineSensor implements Sensor {
       return null;
     }
     switch (severity) {
-      case CRITICAL:
       case BLOCKER:
+        return org.sonar.api.issue.impact.Severity.BLOCKER;
+      case CRITICAL:
         return org.sonar.api.issue.impact.Severity.HIGH;
       case MAJOR:
         return org.sonar.api.issue.impact.Severity.MEDIUM;
       case MINOR:
-      case INFO:
         return org.sonar.api.issue.impact.Severity.LOW;
+      case INFO:
+        return org.sonar.api.issue.impact.Severity.INFO;
       default:
         return null;
     }
index 6468f7ef0d93b2f33c1509faa8384440e2db5bac..4dbd8085f626495531e94685fd92b7d683c09a8b 100644 (file)
@@ -127,7 +127,7 @@ public class XooRulesDefinition implements RulesDefinition {
 
     NewRule oneIssuePerLine = repo.createRule(OneIssuePerLineSensor.RULE_KEY).setName("One Issue Per Line")
       .setCleanCodeAttribute(CleanCodeAttribute.COMPLETE)
-      .addDefaultImpact(SoftwareQuality.MAINTAINABILITY, Severity.MEDIUM)
+      .addDefaultImpact(SoftwareQuality.MAINTAINABILITY, Severity.INFO)
       .setTags("line");
     addDescriptionSectionsWithoutContexts(oneIssuePerLine, "Generate an issue on each line of a file. It requires the metric \"lines\".");
     addHowToFixSectionsWithContexts(oneIssuePerLine);
@@ -138,7 +138,7 @@ public class XooRulesDefinition implements RulesDefinition {
 
     NewRule oneQuickFixPerLine = repo.createRule(OneQuickFixPerLineSensor.RULE_KEY).setName("One Quick Fix Per Line")
       .setCleanCodeAttribute(CleanCodeAttribute.DISTINCT)
-      .addDefaultImpact(SoftwareQuality.MAINTAINABILITY, Severity.MEDIUM)
+      .addDefaultImpact(SoftwareQuality.MAINTAINABILITY, Severity.BLOCKER)
       .setTags("line");
     addAllDescriptionSections(oneQuickFixPerLine,
       "Generate an issue with quick fix available on each line of a file. It requires the metric \"lines\".");
index 3feed4fa16e2081a638ac76997340eb56e2a2f64..3dae30abdb8e75dc862a8bb980c11379eb8e9cfb 100644 (file)
@@ -233,6 +233,7 @@ public class ExportIssuesStepIT {
       .setIssueCreationTime(963L)
       .setIssueUpdateTime(852L)
       .addImpact(new ImpactDto().setSoftwareQuality(SoftwareQuality.MAINTAINABILITY).setSeverity(Severity.HIGH))
+      .addImpact(new ImpactDto().setSoftwareQuality(SoftwareQuality.SECURITY).setSeverity(Severity.BLOCKER))
       .setIssueCloseTime(741L)
       .setCodeVariants(List.of("v1", "v2"));
 
@@ -270,7 +271,7 @@ public class ExportIssuesStepIT {
     assertThat(issue.getIssueClosedAt()).isEqualTo(issueDto.getIssueCloseTime());
     assertThat(issue.getLocations()).isNotEmpty();
     assertThat(issue.getImpactsList()).extracting(ProjectDump.Impact::getSoftwareQuality, ProjectDump.Impact::getSeverity)
-      .containsOnly(tuple(ProjectDump.SoftwareQuality.MAINTAINABILITY, ProjectDump.Severity.HIGH));
+      .containsOnly(tuple(ProjectDump.SoftwareQuality.MAINTAINABILITY, ProjectDump.Severity.HIGH), tuple(ProjectDump.SoftwareQuality.SECURITY, ProjectDump.Severity.BLOCKER));
     assertThat(issue.getMessageFormattingsList())
       .isEqualTo(ExportIssuesStep.dbToDumpMessageFormatting(messageFormattings.getMessageFormattingList()));
     assertThat(issue.getCodeVariants()).isEqualTo(issueDto.getCodeVariantsString());
index 0d1ac24b094bf1ec63f66409ca20e04f713bffbb..94e6af5c4cff78861e5738522c80246005956016 100644 (file)
@@ -207,7 +207,7 @@ public class ExportAdHocRulesStepIT {
       .setIsAdHoc(true)
       .setCleanCodeAttribute(CleanCodeAttribute.CONVENTIONAL)
       .addDefaultImpact(new ImpactDto().setSoftwareQuality(SoftwareQuality.MAINTAINABILITY).setSeverity(org.sonar.api.issue.impact.Severity.MEDIUM))
-      .addDefaultImpact(new ImpactDto().setSoftwareQuality(SoftwareQuality.RELIABILITY).setSeverity(org.sonar.api.issue.impact.Severity.HIGH))
+      .addDefaultImpact(new ImpactDto().setSoftwareQuality(SoftwareQuality.RELIABILITY).setSeverity(org.sonar.api.issue.impact.Severity.BLOCKER))
       .setRuleKey(ruleKey)
       .setScope(RuleDto.Scope.ALL)
       .setStatus(RuleStatus.READY);
index 32b15ebaacaafbd7c206a634a00606d269498624..bf7aedad8c2ce33fe2de1b7b0a10033b446deeac 100644 (file)
@@ -157,7 +157,7 @@ public class MaintainabilityMeasuresVisitor extends PathAwareVisitorAdapter<Main
   }
 
   private void addSoftwareQualityMaintainabilityRatingMeasure(Component component, double density) {
-    Rating rating = ratingSettings.getDebtRatingGrid().getAToDRatingForDensity(density);
+    Rating rating = ratingSettings.getDebtRatingGrid().getRatingForDensity(density);
     measureRepository.add(component, softwareQualityMaintainabilityRatingMetric, RatingMeasures.get(rating));
   }
 
index f39acc0c7d23828aec2249ba8d9b60a9ae3b4543..39ac9561d1c48c3b16276bc041ddd3222c6af6da 100644 (file)
@@ -131,7 +131,7 @@ public class NewMaintainabilityMeasuresVisitor extends PathAwareVisitorAdapter<N
 
     double densityBasedOnSoftwareQuality = computeDensity(path.current().getNewSoftwareQualityDebt(), path.current().getDevCost());
     double newSoftwareQualityDebtRatio = 100.0 * densityBasedOnSoftwareQuality;
-    int newSoftwareQualityMaintainabilityRating = ratingSettings.getDebtRatingGrid().getAToDRatingForDensity(densityBasedOnSoftwareQuality).getIndex();
+    int newSoftwareQualityMaintainabilityRating = ratingSettings.getDebtRatingGrid().getRatingForDensity(densityBasedOnSoftwareQuality).getIndex();
     measureRepository.add(component, this.newSoftwareQualityMaintainabilityDebtRatioMetric, newMeasureBuilder().create(newSoftwareQualityDebtRatio));
     measureRepository.add(component, this.newSoftwareQualityMaintainabilityRatingMetric, newMeasureBuilder().create(newSoftwareQualityMaintainabilityRating));
   }
index e1b92e9b8fbba300c5dca693234573f8a54283ef..a6c3a5b32bd98772e2f2fc2c517178a4faaefdc0 100644 (file)
@@ -36,8 +36,6 @@ import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_REVIEW_RATING_KEY;
 import static org.sonar.api.rules.RuleType.SECURITY_HOTSPOT;
 import static org.sonar.ce.task.projectanalysis.component.ComponentVisitor.Order.POST_ORDER;
 import static org.sonar.ce.task.projectanalysis.component.CrawlerDepthLimit.FILE;
-import static org.sonar.core.metric.SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_SECURITY_REVIEW_RATING_KEY;
-import static org.sonar.server.security.SecurityReviewRating.computeAToDRating;
 import static org.sonar.server.security.SecurityReviewRating.computePercent;
 import static org.sonar.server.security.SecurityReviewRating.computeRating;
 
@@ -46,7 +44,6 @@ public class NewSecurityReviewMeasuresVisitor extends PathAwareVisitorAdapter<Se
   private final ComponentIssuesRepository componentIssuesRepository;
   private final MeasureRepository measureRepository;
   private final Metric newSecurityReviewRatingMetric;
-  private final Metric newSoftwareQualitySecurityReviewRatingMetric;
   private final Metric newSecurityHotspotsReviewedMetric;
   private final Metric newSecurityHotspotsReviewedStatusMetric;
   private final Metric newSecurityHotspotsToReviewStatusMetric;
@@ -58,7 +55,6 @@ public class NewSecurityReviewMeasuresVisitor extends PathAwareVisitorAdapter<Se
     this.componentIssuesRepository = componentIssuesRepository;
     this.measureRepository = measureRepository;
     this.newSecurityReviewRatingMetric = metricRepository.getByKey(NEW_SECURITY_REVIEW_RATING_KEY);
-    this.newSoftwareQualitySecurityReviewRatingMetric = metricRepository.getByKey(NEW_SOFTWARE_QUALITY_SECURITY_REVIEW_RATING_KEY);
     this.newSecurityHotspotsReviewedMetric = metricRepository.getByKey(NEW_SECURITY_HOTSPOTS_REVIEWED_KEY);
     this.newSecurityHotspotsReviewedStatusMetric = metricRepository.getByKey(NEW_SECURITY_HOTSPOTS_REVIEWED_STATUS_KEY);
     this.newSecurityHotspotsToReviewStatusMetric = metricRepository.getByKey(NEW_SECURITY_HOTSPOTS_TO_REVIEW_STATUS_KEY);
@@ -96,8 +92,6 @@ public class NewSecurityReviewMeasuresVisitor extends PathAwareVisitorAdapter<Se
 
     Optional<Double> percent = computePercent(path.current().getHotspotsToReview(), path.current().getHotspotsReviewed());
     measureRepository.add(component, newSecurityReviewRatingMetric, Measure.newMeasureBuilder().create(computeRating(percent.orElse(null)).getIndex()));
-    measureRepository.add(component, newSoftwareQualitySecurityReviewRatingMetric,
-      Measure.newMeasureBuilder().create(computeAToDRating(percent.orElse(null)).getIndex()));
     percent.ifPresent(p -> measureRepository.add(component, newSecurityHotspotsReviewedMetric, Measure.newMeasureBuilder().create(p)));
 
     if (!path.isRoot()) {
index df9029b03288569f1371866f8d3cdef8b25a58a7..c8e8ec02fc0a40db5fceb5b58c3b94cf412aca46 100644 (file)
@@ -37,8 +37,6 @@ import static org.sonar.api.rules.RuleType.SECURITY_HOTSPOT;
 import static org.sonar.ce.task.projectanalysis.component.ComponentVisitor.Order.POST_ORDER;
 import static org.sonar.ce.task.projectanalysis.component.CrawlerDepthLimit.FILE;
 import static org.sonar.ce.task.projectanalysis.measure.Measure.newMeasureBuilder;
-import static org.sonar.core.metric.SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_REVIEW_RATING_KEY;
-import static org.sonar.server.security.SecurityReviewRating.computeAToDRating;
 import static org.sonar.server.security.SecurityReviewRating.computePercent;
 import static org.sonar.server.security.SecurityReviewRating.computeRating;
 
@@ -47,7 +45,6 @@ public class SecurityReviewMeasuresVisitor extends PathAwareVisitorAdapter<Secur
   private final ComponentIssuesRepository componentIssuesRepository;
   private final MeasureRepository measureRepository;
   private final Metric securityReviewRatingMetric;
-  private final Metric softwareQualitySecurityReviewRatingMetric;
   private final Metric securityHotspotsReviewedMetric;
   private final Metric securityHotspotsReviewedStatusMetric;
   private final Metric securityHotspotsToReviewStatusMetric;
@@ -57,7 +54,6 @@ public class SecurityReviewMeasuresVisitor extends PathAwareVisitorAdapter<Secur
     this.componentIssuesRepository = componentIssuesRepository;
     this.measureRepository = measureRepository;
     this.securityReviewRatingMetric = metricRepository.getByKey(SECURITY_REVIEW_RATING_KEY);
-    this.softwareQualitySecurityReviewRatingMetric = metricRepository.getByKey(SOFTWARE_QUALITY_SECURITY_REVIEW_RATING_KEY);
     this.securityHotspotsReviewedMetric = metricRepository.getByKey(SECURITY_HOTSPOTS_REVIEWED_KEY);
     this.securityHotspotsReviewedStatusMetric = metricRepository.getByKey(SECURITY_HOTSPOTS_REVIEWED_STATUS_KEY);
     this.securityHotspotsToReviewStatusMetric = metricRepository.getByKey(SECURITY_HOTSPOTS_TO_REVIEW_STATUS_KEY);
@@ -88,8 +84,6 @@ public class SecurityReviewMeasuresVisitor extends PathAwareVisitorAdapter<Secur
     measureRepository.add(component, securityHotspotsToReviewStatusMetric, newMeasureBuilder().create(path.current().getHotspotsToReview()));
     Optional<Double> percent = computePercent(path.current().getHotspotsToReview(), path.current().getHotspotsReviewed());
     measureRepository.add(component, securityReviewRatingMetric, RatingMeasures.get(computeRating(percent.orElse(null))));
-    measureRepository.add(component, softwareQualitySecurityReviewRatingMetric,
-      RatingMeasures.get(computeAToDRating(percent.orElse(null))));
     percent.ifPresent(p -> measureRepository.add(component, securityHotspotsReviewedMetric, newMeasureBuilder().create(p, securityHotspotsReviewedMetric.getDecimalScale())));
 
     if (!path.isRoot()) {
index 7ec45800832c979cf5832bbd88a3ae3dab9213e0..42497ea5066bc1c4f69562306cbfaf77f80ba276 100644 (file)
@@ -114,7 +114,6 @@ public class PersistLiveMeasuresStep implements ComputationStep {
     SoftwareQualitiesMetrics.SOFTWARE_QUALITY_MAINTAINABILITY_RATING_KEY,
     SoftwareQualitiesMetrics.SOFTWARE_QUALITY_RELIABILITY_RATING_KEY,
     SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_RATING_KEY,
-    SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_REVIEW_RATING_KEY,
     SoftwareQualitiesMetrics.EFFORT_TO_REACH_SOFTWARE_QUALITY_MAINTAINABILITY_RATING_A_KEY,
     SoftwareQualitiesMetrics.SOFTWARE_QUALITY_MAINTAINABILITY_REMEDIATION_EFFORT_KEY,
     SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_REMEDIATION_EFFORT_KEY,
index d1e3782b7f151de5694b90776b65a2d39688734b..114fdda60f423bc1acf44563e01a6fa0f75d65f4 100644 (file)
@@ -253,4 +253,6 @@ enum Severity {
   LOW = 0;
   MEDIUM = 1;
   HIGH = 2;
+  INFO = 3;
+  BLOCKER = 4;
 }
index 7ad66ed9b4089475415a74510ad14624bfaba938..f47a6da5495fb475bfe74d4cc01107506fa061a6 100644 (file)
@@ -126,6 +126,7 @@ import static org.sonar.ce.task.projectanalysis.issue.IssueCounter.IMPACT_TO_MET
 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;
+import static org.sonar.test.JsonAssert.assertJson;
 
 class IssueCounterTest {
 
@@ -316,9 +317,11 @@ class IssueCounterTest {
     underTest.beforeComponent(PROJECT);
     underTest.afterComponent(PROJECT);
 
-    assertIntValue(FILE1, entry(NEW_VIOLATIONS_KEY, 2), entry(NEW_CRITICAL_VIOLATIONS_KEY, 2), entry(NEW_BLOCKER_VIOLATIONS_KEY, 0), entry(NEW_MAJOR_VIOLATIONS_KEY, 0),
+    assertIntValue(FILE1, entry(NEW_VIOLATIONS_KEY, 2), entry(NEW_CRITICAL_VIOLATIONS_KEY, 2), entry(NEW_BLOCKER_VIOLATIONS_KEY, 0),
+      entry(NEW_MAJOR_VIOLATIONS_KEY, 0),
       entry(NEW_CODE_SMELLS_KEY, 1), entry(NEW_BUGS_KEY, 1), entry(NEW_VULNERABILITIES_KEY, 0), entry(NEW_SECURITY_HOTSPOTS_KEY, 1));
-    assertIntValue(PROJECT, entry(NEW_VIOLATIONS_KEY, 2), entry(NEW_CRITICAL_VIOLATIONS_KEY, 2), entry(NEW_BLOCKER_VIOLATIONS_KEY, 0), entry(NEW_MAJOR_VIOLATIONS_KEY, 0),
+    assertIntValue(PROJECT, entry(NEW_VIOLATIONS_KEY, 2), entry(NEW_CRITICAL_VIOLATIONS_KEY, 2), entry(NEW_BLOCKER_VIOLATIONS_KEY, 0),
+      entry(NEW_MAJOR_VIOLATIONS_KEY, 0),
       entry(NEW_CODE_SMELLS_KEY, 1), entry(NEW_BUGS_KEY, 1), entry(NEW_VULNERABILITIES_KEY, 0), entry(NEW_SECURITY_HOTSPOTS_KEY, 1));
   }
 
@@ -350,14 +353,14 @@ class IssueCounterTest {
     when(newIssueClassifier.isEnabled()).thenReturn(true);
 
     underTest.beforeComponent(FILE1);
-    underTest.onIssue(FILE1, createIssue(RESOLUTION_WONT_FIX, STATUS_OPEN, SoftwareQuality.MAINTAINABILITY,  HIGH));
+    underTest.onIssue(FILE1, createIssue(RESOLUTION_WONT_FIX, STATUS_OPEN, SoftwareQuality.MAINTAINABILITY, HIGH));
     underTest.onIssue(FILE1, createIssue(RESOLUTION_WONT_FIX, STATUS_OPEN, SoftwareQuality.MAINTAINABILITY, MEDIUM));
 
     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.SECURITY,  HIGH));
+    underTest.onIssue(FILE1, createIssue(RESOLUTION_WONT_FIX, STATUS_OPEN, SoftwareQuality.SECURITY, HIGH));
     underTest.onIssue(FILE1, createIssue(RESOLUTION_WONT_FIX, STATUS_OPEN, SoftwareQuality.SECURITY, MEDIUM));
 
     underTest.onIssue(FILE1, createNewSecurityHotspot());
@@ -368,9 +371,9 @@ class IssueCounterTest {
 
     Set<Map.Entry<String, Measure>> entries = measureRepository.getRawMeasures(FILE1).entrySet();
 
-    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);
+    assertOverallSoftwareQualityMeasures(SoftwareQuality.MAINTAINABILITY, getImpactMeasure(4, 2, 2, 0, 0, 0), entries);
+    assertOverallSoftwareQualityMeasures(SoftwareQuality.SECURITY, getImpactMeasure(2, 1, 1, 0, 0, 0), entries);
+    assertOverallSoftwareQualityMeasures(SoftwareQuality.RELIABILITY, getImpactMeasure(0, 0, 0, 0, 0, 0), entries);
   }
 
   @Test
@@ -378,17 +381,17 @@ class IssueCounterTest {
     when(newIssueClassifier.isEnabled()).thenReturn(true);
 
     underTest.beforeComponent(FILE1);
-    underTest.onIssue(FILE1, createIssue(RESOLUTION_WONT_FIX, STATUS_OPEN, SoftwareQuality.MAINTAINABILITY,  HIGH));
+    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, 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, 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));
@@ -402,9 +405,9 @@ class IssueCounterTest {
 
     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);
+    assertNewSoftwareQualityMeasures(SoftwareQuality.MAINTAINABILITY, getImpactMeasure(2, 1, 1, 0, 0, 0), entries);
+    assertNewSoftwareQualityMeasures(SoftwareQuality.RELIABILITY, getImpactMeasure(2, 0, 1, 1, 0, 0), entries);
+    assertNewSoftwareQualityMeasures(SoftwareQuality.SECURITY, getImpactMeasure(4, 2, 1, 1, 0, 0), entries);
   }
 
   private static Map<String, Long> getImpactMeasure(long total, long high, long medium, long low) {
@@ -416,6 +419,13 @@ class IssueCounterTest {
     return map;
   }
 
+  private static Map<String, Long> getImpactMeasure(long total, long high, long medium, long low, long info, long blocker) {
+    Map<String, Long> map = getImpactMeasure(total, high, medium, low);
+    map.put(Severity.INFO.name(), info);
+    map.put(Severity.BLOCKER.name(), blocker);
+    return map;
+  }
+
   private void assertOverallSoftwareQualityMeasures(SoftwareQuality softwareQuality, Map<? extends String, Long> expectedMap,
     Set<Map.Entry<String, Measure>> actualRaw) {
     assertSoftwareQualityMeasures(softwareQuality, expectedMap, actualRaw, IMPACT_TO_METRIC_KEY);
@@ -434,7 +444,7 @@ class IssueCounterTest {
       .findFirst()
       .get();
 
-    assertThat(softwareQualityMap.getValue().getData()).isEqualTo(new Gson().toJson(expectedMap));
+    assertJson(softwareQualityMap.getValue().getData()).isSimilarTo(new Gson().toJson(expectedMap));
   }
 
   @Test
@@ -513,9 +523,11 @@ class IssueCounterTest {
     underTest.beforeComponent(PROJECT);
     underTest.afterComponent(PROJECT);
 
-    assertIntValue(FILE1, entry(NEW_VIOLATIONS_KEY, 0), entry(NEW_CRITICAL_VIOLATIONS_KEY, 0), entry(NEW_BLOCKER_VIOLATIONS_KEY, 0), entry(NEW_MAJOR_VIOLATIONS_KEY, 0),
+    assertIntValue(FILE1, entry(NEW_VIOLATIONS_KEY, 0), entry(NEW_CRITICAL_VIOLATIONS_KEY, 0), entry(NEW_BLOCKER_VIOLATIONS_KEY, 0),
+      entry(NEW_MAJOR_VIOLATIONS_KEY, 0),
       entry(NEW_VULNERABILITIES_KEY, 0));
-    assertIntValue(PROJECT, entry(NEW_VIOLATIONS_KEY, 0), entry(NEW_CRITICAL_VIOLATIONS_KEY, 0), entry(NEW_BLOCKER_VIOLATIONS_KEY, 0), entry(NEW_MAJOR_VIOLATIONS_KEY, 0),
+    assertIntValue(PROJECT, entry(NEW_VIOLATIONS_KEY, 0), entry(NEW_CRITICAL_VIOLATIONS_KEY, 0), entry(NEW_BLOCKER_VIOLATIONS_KEY, 0),
+      entry(NEW_MAJOR_VIOLATIONS_KEY, 0),
       entry(NEW_VULNERABILITIES_KEY, 0));
   }
 
@@ -546,7 +558,8 @@ class IssueCounterTest {
     return createNewIssue(resolution, status, SoftwareQuality.MAINTAINABILITY, impactSeverity);
   }
 
-  private DefaultIssue createNewIssue(@Nullable String resolution, String status, SoftwareQuality softwareQuality, Severity impactSeverity) {
+  private DefaultIssue createNewIssue(@Nullable String resolution, String status, SoftwareQuality softwareQuality,
+    Severity impactSeverity) {
     DefaultIssue issue = createNewIssue(resolution, status, MAJOR, CODE_SMELL);
     issue.addImpact(softwareQuality, impactSeverity);
     return issue;
@@ -566,7 +579,8 @@ class IssueCounterTest {
     return createIssue(resolution, status, SoftwareQuality.MAINTAINABILITY, impactSeverity);
   }
 
-  private static DefaultIssue createIssue(@Nullable String resolution, String status, SoftwareQuality softwareQuality, Severity impactSeverity) {
+  private static DefaultIssue createIssue(@Nullable String resolution, String status, SoftwareQuality softwareQuality,
+    Severity impactSeverity) {
     DefaultIssue issue = createIssue(resolution, status, MAJOR, CODE_SMELL);
     issue.addImpact(softwareQuality, impactSeverity);
     return issue;
index e2741c6a5da10eebaa4ccf54e7e5fad36bb09044..25779ab5480ef3e275ca6dd047a45c5186703bf0 100644 (file)
@@ -279,7 +279,7 @@ class MaintainabilityMeasuresVisitorTest {
     verifyAddedRawMeasure(FILE_1_REF, SOFTWARE_QUALITY_MAINTAINABILITY_RATING_KEY, C);
     verifyAddedRawMeasure(FILE_2_REF, SOFTWARE_QUALITY_MAINTAINABILITY_RATING_KEY, A);
     verifyAddedRawMeasure(DIRECTORY_REF, SOFTWARE_QUALITY_MAINTAINABILITY_RATING_KEY, C);
-    verifyAddedRawMeasure(PROJECT_REF, SOFTWARE_QUALITY_MAINTAINABILITY_RATING_KEY, D);
+    verifyAddedRawMeasure(PROJECT_REF, SOFTWARE_QUALITY_MAINTAINABILITY_RATING_KEY, E);
   }
 
   @ParameterizedTest
index 19415757cec960cb278c10e39c07638dda6b2083..ad891b152595226769878526daae8832d9028d56 100644 (file)
@@ -358,7 +358,7 @@ public class NewMaintainabilityMeasuresVisitorTest {
 
   @ParameterizedTest
   @MethodSource("metrics")
-  void compute_new_maintainability_rating_map_to_D(String remediationEffortKey, String debtRatioKey, String ratingKey) {
+  void compute_new_maintainability_rating_map_to_E(String remediationEffortKey, String debtRatioKey, String ratingKey) {
     ReportComponent file = builder(FILE, LANGUAGE_1_FILE_REF).setFileAttributes(new FileAttributes(false, LANGUAGE_1_KEY, 1)).build();
     treeRootHolder.setRoot(
       builder(PROJECT, ROOT_REF)
@@ -375,12 +375,7 @@ public class NewMaintainabilityMeasuresVisitorTest {
     setNewLines(file, 3, 4);
 
     underTest.visit(treeRootHolder.getRoot());
-
-    if (ratingKey.equals(NEW_SOFTWARE_QUALITY_MAINTAINABILITY_RATING_KEY)) {
-      assertNewRating(ratingKey, LANGUAGE_1_FILE_REF, D);
-    } else if (ratingKey.equals(NEW_MAINTAINABILITY_RATING_KEY)) {
-      assertNewRating(ratingKey, LANGUAGE_1_FILE_REF, E);
-    }
+    assertNewRating(ratingKey, LANGUAGE_1_FILE_REF, E);
   }
 
   @Test
index f2d94abce7acc1153d747d74e06158a49f474402..48eba3a75412c43273b8311f086b38aba8a2eea7 100644 (file)
@@ -186,7 +186,7 @@ class NewReliabilityAndSecurityRatingMeasuresVisitorTest {
       oldImpactIssue(SoftwareQuality.SECURITY, Severity.HIGH));
     fillComponentIssuesVisitorRule.setIssues(FILE_2_REF,
       newImpactIssue(SoftwareQuality.SECURITY, Severity.LOW),
-      newImpactIssue(SoftwareQuality.SECURITY, Severity.HIGH),
+      newImpactIssue(SoftwareQuality.SECURITY, Severity.BLOCKER),
       // Should not be taken into account
       oldImpactIssue(SoftwareQuality.SECURITY, Severity.HIGH));
     fillComponentIssuesVisitorRule.setIssues(ROOT_DIR_REF, newImpactIssue(SoftwareQuality.SECURITY, Severity.HIGH));
@@ -194,10 +194,10 @@ class NewReliabilityAndSecurityRatingMeasuresVisitorTest {
     underTest.visit(ROOT_PROJECT);
 
     verifyAddedRawMeasureOnLeakPeriod(FILE_1_REF, NEW_SOFTWARE_QUALITY_SECURITY_RATING_KEY, C);
-    verifyAddedRawMeasureOnLeakPeriod(FILE_2_REF, NEW_SOFTWARE_QUALITY_SECURITY_RATING_KEY, D);
-    verifyAddedRawMeasureOnLeakPeriod(DIRECTORY_REF, NEW_SOFTWARE_QUALITY_SECURITY_RATING_KEY, D);
-    verifyAddedRawMeasureOnLeakPeriod(ROOT_DIR_REF, NEW_SOFTWARE_QUALITY_SECURITY_RATING_KEY, D);
-    verifyAddedRawMeasureOnLeakPeriod(PROJECT_REF, NEW_SOFTWARE_QUALITY_SECURITY_RATING_KEY, D);
+    verifyAddedRawMeasureOnLeakPeriod(FILE_2_REF, NEW_SOFTWARE_QUALITY_SECURITY_RATING_KEY, E);
+    verifyAddedRawMeasureOnLeakPeriod(DIRECTORY_REF, NEW_SOFTWARE_QUALITY_SECURITY_RATING_KEY, E);
+    verifyAddedRawMeasureOnLeakPeriod(ROOT_DIR_REF, NEW_SOFTWARE_QUALITY_SECURITY_RATING_KEY, E);
+    verifyAddedRawMeasureOnLeakPeriod(PROJECT_REF, NEW_SOFTWARE_QUALITY_SECURITY_RATING_KEY, E);
   }
 
   @Test
@@ -289,7 +289,7 @@ class NewReliabilityAndSecurityRatingMeasuresVisitorTest {
       // Should not be taken into account
       oldImpactIssue(SoftwareQuality.RELIABILITY, Severity.HIGH));
     fillComponentIssuesVisitorRule.setIssues(FILE_2_REF,
-      newImpactIssue(SoftwareQuality.RELIABILITY, Severity.LOW),
+      newImpactIssue(SoftwareQuality.RELIABILITY, Severity.INFO),
       newImpactIssue(SoftwareQuality.RELIABILITY, Severity.HIGH),
       // Should not be taken into account
       oldImpactIssue(SoftwareQuality.RELIABILITY, Severity.HIGH));
@@ -375,6 +375,21 @@ class NewReliabilityAndSecurityRatingMeasuresVisitorTest {
     verifyAddedRawMeasureOnLeakPeriod(PROJECT_REF, NEW_SECURITY_RATING_KEY, E);
   }
 
+  @Test
+  void compute_E_software_quality_reliability_and_security_rating_on_blocker_severity_issue() {
+    treeRootHolder.setRoot(ROOT_PROJECT);
+    fillComponentIssuesVisitorRule.setIssues(FILE_1_REF,
+      newImpactIssue(SoftwareQuality.RELIABILITY, Severity.BLOCKER),
+      newImpactIssue(SoftwareQuality.SECURITY, Severity.BLOCKER),
+      // Should not be taken into account
+      newCodeSmellIssue(1L, MAJOR));
+
+    underTest.visit(ROOT_PROJECT);
+
+    verifyAddedRawMeasureOnLeakPeriod(PROJECT_REF, NEW_SOFTWARE_QUALITY_RELIABILITY_RATING_KEY, E);
+    verifyAddedRawMeasureOnLeakPeriod(PROJECT_REF, NEW_SOFTWARE_QUALITY_SECURITY_RATING_KEY, E);
+  }
+
   @Test
   void compute_D_reliability_and_security_rating_on_critical_issue() {
     treeRootHolder.setRoot(ROOT_PROJECT);
@@ -480,6 +495,21 @@ class NewReliabilityAndSecurityRatingMeasuresVisitorTest {
     verifyAddedRawMeasureOnLeakPeriod(PROJECT_REF, NEW_SECURITY_RATING_KEY, A);
   }
 
+  @Test
+  void compute_A_software_quality_reliability_and_security_rating_on_info_severity_issue() {
+    treeRootHolder.setRoot(ROOT_PROJECT);
+    fillComponentIssuesVisitorRule.setIssues(FILE_1_REF,
+      newImpactIssue(SoftwareQuality.RELIABILITY, Severity.INFO),
+      newImpactIssue(SoftwareQuality.SECURITY, Severity.INFO),
+      // Should not be taken into account
+      newCodeSmellIssue(1L, MAJOR));
+
+    underTest.visit(ROOT_PROJECT);
+
+    verifyAddedRawMeasureOnLeakPeriod(PROJECT_REF, NEW_SOFTWARE_QUALITY_RELIABILITY_RATING_KEY, A);
+    verifyAddedRawMeasureOnLeakPeriod(PROJECT_REF, NEW_SOFTWARE_QUALITY_SECURITY_RATING_KEY, A);
+  }
+
   @Test
   void compute_A_software_quality_reliability_and_security_rating_when_no_issue() {
     treeRootHolder.setRoot(ROOT_PROJECT);
index 2fe66648466787b7fb43aee3c09731118d28b517..83d8db3d25cde73b5476fd089551e82e5eb20f9f 100644 (file)
@@ -68,8 +68,6 @@ import static org.sonar.server.measure.Rating.B;
 import static org.sonar.server.measure.Rating.C;
 import static org.sonar.server.measure.Rating.D;
 import static org.sonar.server.measure.Rating.E;
-import static org.sonar.core.metric.SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_SECURITY_REVIEW_RATING;
-import static org.sonar.core.metric.SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_SECURITY_REVIEW_RATING_KEY;
 
 class NewSecurityReviewMeasuresVisitorTest {
   private static final Offset<Double> VALUE_COMPARISON_OFFSET = Offset.offset(0.01);
@@ -98,7 +96,6 @@ class NewSecurityReviewMeasuresVisitorTest {
   @RegisterExtension
   private final MetricRepositoryRule metricRepository = new MetricRepositoryRule()
     .add(NEW_SECURITY_REVIEW_RATING)
-    .add(NEW_SOFTWARE_QUALITY_SECURITY_REVIEW_RATING)
     .add(NEW_SECURITY_HOTSPOTS_REVIEWED)
     .add(NEW_SECURITY_HOTSPOTS_REVIEWED_STATUS)
     .add(NEW_SECURITY_HOTSPOTS_TO_REVIEW_STATUS);
@@ -134,11 +131,11 @@ class NewSecurityReviewMeasuresVisitorTest {
 
     underTest.visit(ROOT_PROJECT);
 
-    verifyRatingAndReviewedMeasures(FILE_1_REF, A, A, 100.0);
-    verifyRatingAndReviewedMeasures(FILE_2_REF, A, A, 100.0);
-    verifyRatingAndReviewedMeasures(DIRECTORY_REF, A, A, 100.0);
-    verifyRatingAndReviewedMeasures(ROOT_DIR_REF, A, A, 100.0);
-    verifyRatingAndReviewedMeasures(PROJECT_REF, A, A, 100.0);
+    verifyRatingAndReviewedMeasures(FILE_1_REF, A, 100.0);
+    verifyRatingAndReviewedMeasures(FILE_2_REF, A, 100.0);
+    verifyRatingAndReviewedMeasures(DIRECTORY_REF, A, 100.0);
+    verifyRatingAndReviewedMeasures(ROOT_DIR_REF, A, 100.0);
+    verifyRatingAndReviewedMeasures(PROJECT_REF, A, 100.0);
   }
 
   @Test
@@ -163,11 +160,11 @@ class NewSecurityReviewMeasuresVisitorTest {
 
     underTest.visit(ROOT_PROJECT);
 
-    verifyRatingAndReviewedMeasures(FILE_1_REF, A, A, 100.0);
-    verifyRatingAndReviewedMeasures(FILE_2_REF, A, B, 80.0);
-    verifyRatingAndReviewedMeasures(DIRECTORY_REF, A, B, 87.5);
-    verifyRatingAndReviewedMeasures(ROOT_DIR_REF, A, B, 87.5);
-    verifyRatingAndReviewedMeasures(PROJECT_REF, A, B, 87.5);
+    verifyRatingAndReviewedMeasures(FILE_1_REF, A, 100.0);
+    verifyRatingAndReviewedMeasures(FILE_2_REF, A, 80.0);
+    verifyRatingAndReviewedMeasures(DIRECTORY_REF, A, 87.5);
+    verifyRatingAndReviewedMeasures(ROOT_DIR_REF, A, 87.5);
+    verifyRatingAndReviewedMeasures(PROJECT_REF, A, 87.5);
   }
 
   @Test
@@ -192,11 +189,11 @@ class NewSecurityReviewMeasuresVisitorTest {
 
     underTest.visit(ROOT_PROJECT);
 
-    verifyRatingAndReviewedMeasures(FILE_1_REF, A, A, 100.0);
-    verifyRatingAndReviewedMeasures(FILE_2_REF, B, B, 71.42);
-    verifyRatingAndReviewedMeasures(DIRECTORY_REF, B, B, 75.0);
-    verifyRatingAndReviewedMeasures(ROOT_DIR_REF, B, B, 75.0);
-    verifyRatingAndReviewedMeasures(PROJECT_REF, B, B, 75.0);
+    verifyRatingAndReviewedMeasures(FILE_1_REF, A, 100.0);
+    verifyRatingAndReviewedMeasures(FILE_2_REF, B, 71.42);
+    verifyRatingAndReviewedMeasures(DIRECTORY_REF, B, 75.0);
+    verifyRatingAndReviewedMeasures(ROOT_DIR_REF, B, 75.0);
+    verifyRatingAndReviewedMeasures(PROJECT_REF, B, 75.0);
   }
 
   @Test
@@ -220,11 +217,11 @@ class NewSecurityReviewMeasuresVisitorTest {
 
     underTest.visit(ROOT_PROJECT);
 
-    verifyRatingAndReviewedMeasures(FILE_1_REF, C, C, 50.0);
-    verifyRatingAndReviewedMeasures(FILE_2_REF, C, C, 60.0);
-    verifyRatingAndReviewedMeasures(DIRECTORY_REF, C, C, 57.14);
-    verifyRatingAndReviewedMeasures(ROOT_DIR_REF, C, C, 57.14);
-    verifyRatingAndReviewedMeasures(PROJECT_REF, C, C, 57.14);
+    verifyRatingAndReviewedMeasures(FILE_1_REF, C, 50.0);
+    verifyRatingAndReviewedMeasures(FILE_2_REF, C, 60.0);
+    verifyRatingAndReviewedMeasures(DIRECTORY_REF, C, 57.14);
+    verifyRatingAndReviewedMeasures(ROOT_DIR_REF, C, 57.14);
+    verifyRatingAndReviewedMeasures(PROJECT_REF, C, 57.14);
   }
 
   @Test
@@ -249,11 +246,11 @@ class NewSecurityReviewMeasuresVisitorTest {
 
     underTest.visit(ROOT_PROJECT);
 
-    verifyRatingAndReviewedMeasures(FILE_1_REF, D, D, 33.33);
-    verifyRatingAndReviewedMeasures(FILE_2_REF, D, D, 40.0);
-    verifyRatingAndReviewedMeasures(DIRECTORY_REF, D, D, 37.5);
-    verifyRatingAndReviewedMeasures(ROOT_DIR_REF, D, D, 37.5);
-    verifyRatingAndReviewedMeasures(PROJECT_REF, D, D, 37.5);
+    verifyRatingAndReviewedMeasures(FILE_1_REF, D, 33.33);
+    verifyRatingAndReviewedMeasures(FILE_2_REF, D, 40.0);
+    verifyRatingAndReviewedMeasures(DIRECTORY_REF, D, 37.5);
+    verifyRatingAndReviewedMeasures(ROOT_DIR_REF, D, 37.5);
+    verifyRatingAndReviewedMeasures(PROJECT_REF, D, 37.5);
   }
 
   @Test
@@ -276,11 +273,11 @@ class NewSecurityReviewMeasuresVisitorTest {
 
     underTest.visit(ROOT_PROJECT);
 
-    verifyRatingAndReviewedMeasures(FILE_1_REF, D, D, 33.33);
-    verifyRatingAndReviewedMeasures(FILE_2_REF, E, D, 0.0);
-    verifyRatingAndReviewedMeasures(DIRECTORY_REF, E, D, 16.66);
-    verifyRatingAndReviewedMeasures(ROOT_DIR_REF, E, D, 16.66);
-    verifyRatingAndReviewedMeasures(PROJECT_REF, E, D, 16.66);
+    verifyRatingAndReviewedMeasures(FILE_1_REF, D, 33.33);
+    verifyRatingAndReviewedMeasures(FILE_2_REF, E, 0.0);
+    verifyRatingAndReviewedMeasures(DIRECTORY_REF, E, 16.66);
+    verifyRatingAndReviewedMeasures(ROOT_DIR_REF, E, 16.66);
+    verifyRatingAndReviewedMeasures(PROJECT_REF, E, 16.66);
   }
 
   @Test
@@ -293,7 +290,7 @@ class NewSecurityReviewMeasuresVisitorTest {
 
     underTest.visit(ROOT_PROJECT);
 
-    verifyRatingAndReviewedMeasures(PROJECT_REF, A, A, null);
+    verifyRatingAndReviewedMeasures(PROJECT_REF, A, null);
   }
 
   @Test
@@ -343,10 +340,8 @@ class NewSecurityReviewMeasuresVisitorTest {
     assertThat(measureRepository.getAddedRawMeasures(PROJECT_REF).values()).isEmpty();
   }
 
-  private void verifyRatingAndReviewedMeasures(int componentRef, Rating expectedReviewRating,
-    Rating expectedSoftwareQualitySecurityReviewRating, @Nullable Double expectedHotspotsReviewed) {
+  private void verifyRatingAndReviewedMeasures(int componentRef, Rating expectedReviewRating, @Nullable Double expectedHotspotsReviewed) {
     assertThat(measureRepository.getAddedRawMeasure(componentRef, NEW_SECURITY_REVIEW_RATING_KEY)).hasValue(expectedReviewRating.getIndex());
-    assertThat(measureRepository.getAddedRawMeasure(componentRef, NEW_SOFTWARE_QUALITY_SECURITY_REVIEW_RATING_KEY)).hasValue(expectedSoftwareQualitySecurityReviewRating.getIndex());
     if (expectedHotspotsReviewed != null) {
       assertThat(measureRepository.getAddedRawMeasure(componentRef, NEW_SECURITY_HOTSPOTS_REVIEWED_KEY)).hasValue(expectedHotspotsReviewed,
         VALUE_COMPARISON_OFFSET);
index fae829957bb1f9acc1ab4f7b28b2b8d1bbb7e8e2..9ce99a9556a3c5bb7641c9091e3ff3b18e3aded0 100644 (file)
@@ -163,7 +163,7 @@ class ReliabilityAndSecurityRatingMeasuresVisitorTest {
       // Should not be taken into account
       newImpactIssue(SoftwareQuality.SECURITY, Severity.HIGH));
 
-    fillComponentIssuesVisitorRule.setIssues(PROJECT_REF, newImpactIssue(SoftwareQuality.RELIABILITY, Severity.HIGH));
+    fillComponentIssuesVisitorRule.setIssues(PROJECT_REF, newImpactIssue(SoftwareQuality.RELIABILITY, Severity.BLOCKER));
 
     underTest.visit(ROOT_PROJECT);
 
@@ -171,7 +171,7 @@ class ReliabilityAndSecurityRatingMeasuresVisitorTest {
     verifyAddedRawMeasure(FILE_2_REF, SOFTWARE_QUALITY_RELIABILITY_RATING_KEY, C);
     verifyAddedRawMeasure(FILE_3_REF, SOFTWARE_QUALITY_RELIABILITY_RATING_KEY, A);
     verifyAddedRawMeasure(DIRECTORY_REF, SOFTWARE_QUALITY_RELIABILITY_RATING_KEY, C);
-    verifyAddedRawMeasure(PROJECT_REF, SOFTWARE_QUALITY_RELIABILITY_RATING_KEY, D);
+    verifyAddedRawMeasure(PROJECT_REF, SOFTWARE_QUALITY_RELIABILITY_RATING_KEY, E);
   }
 
   @Test
@@ -197,13 +197,13 @@ class ReliabilityAndSecurityRatingMeasuresVisitorTest {
   void compute_software_quality_security_rating() {
     treeRootHolder.setRoot(ROOT_PROJECT);
     fillComponentIssuesVisitorRule.setIssues(FILE_1_REF,
-      newImpactIssue(SoftwareQuality.SECURITY, Severity.LOW),
+      newImpactIssue(SoftwareQuality.SECURITY, Severity.INFO),
       // Should not be taken into account
       newImpactIssue(SoftwareQuality.RELIABILITY, Severity.HIGH));
     fillComponentIssuesVisitorRule.setIssues(FILE_2_REF,
       newImpactIssue(SoftwareQuality.SECURITY, Severity.MEDIUM),
       // Should not be taken into account
-      newImpactIssue(SoftwareQuality.RELIABILITY, Severity.HIGH));
+      newImpactIssue(SoftwareQuality.RELIABILITY, Severity.BLOCKER));
     fillComponentIssuesVisitorRule.setIssues(FILE_3_REF,
       // Should not be taken into account
       newImpactIssue(SoftwareQuality.RELIABILITY, Severity.HIGH));
@@ -212,7 +212,7 @@ class ReliabilityAndSecurityRatingMeasuresVisitorTest {
 
     underTest.visit(ROOT_PROJECT);
 
-    verifyAddedRawMeasure(FILE_1_REF, SOFTWARE_QUALITY_SECURITY_RATING_KEY, B);
+    verifyAddedRawMeasure(FILE_1_REF, SOFTWARE_QUALITY_SECURITY_RATING_KEY, A);
     verifyAddedRawMeasure(FILE_2_REF, SOFTWARE_QUALITY_SECURITY_RATING_KEY, C);
     verifyAddedRawMeasure(FILE_3_REF, SOFTWARE_QUALITY_SECURITY_RATING_KEY, A);
     verifyAddedRawMeasure(DIRECTORY_REF, SOFTWARE_QUALITY_SECURITY_RATING_KEY, C);
@@ -232,6 +232,19 @@ class ReliabilityAndSecurityRatingMeasuresVisitorTest {
     verifyAddedRawMeasure(PROJECT_REF, SECURITY_RATING_KEY, E);
   }
 
+  @Test
+  void compute_E_software_quality_reliability_and_security_rating_on_blocker_issue() {
+    treeRootHolder.setRoot(ROOT_PROJECT);
+    fillComponentIssuesVisitorRule.setIssues(FILE_1_REF, newImpactIssue(SoftwareQuality.RELIABILITY, Severity.BLOCKER), newImpactIssue(SoftwareQuality.SECURITY, Severity.BLOCKER),
+      // Should not be taken into account
+      newImpactIssue(SoftwareQuality.MAINTAINABILITY, Severity.HIGH));
+
+    underTest.visit(ROOT_PROJECT);
+
+    verifyAddedRawMeasure(PROJECT_REF, SOFTWARE_QUALITY_RELIABILITY_RATING_KEY, E);
+    verifyAddedRawMeasure(PROJECT_REF, SOFTWARE_QUALITY_SECURITY_RATING_KEY, E);
+  }
+
   @Test
   void compute_D_reliability_and_security_rating_on_critical_issue() {
     treeRootHolder.setRoot(ROOT_PROJECT);
@@ -323,6 +336,19 @@ class ReliabilityAndSecurityRatingMeasuresVisitorTest {
     verifyAddedRawMeasure(PROJECT_REF, SECURITY_RATING_KEY, A);
   }
 
+  @Test
+  void compute_A_software_quality_reliability_and_security_rating_on_info_issue() {
+    treeRootHolder.setRoot(ROOT_PROJECT);
+    fillComponentIssuesVisitorRule.setIssues(FILE_1_REF, newImpactIssue(SoftwareQuality.RELIABILITY, Severity.INFO), newImpactIssue(SoftwareQuality.SECURITY, Severity.INFO),
+      // Should not be taken into account
+      newImpactIssue(SoftwareQuality.MAINTAINABILITY, Severity.HIGH));
+
+    underTest.visit(ROOT_PROJECT);
+
+    verifyAddedRawMeasure(PROJECT_REF, SOFTWARE_QUALITY_RELIABILITY_RATING_KEY, A);
+    verifyAddedRawMeasure(PROJECT_REF, SOFTWARE_QUALITY_SECURITY_RATING_KEY, A);
+  }
+
   @Test
   void compute_A_software_quality_reliability_and_security_rating_when_no_issue() {
     treeRootHolder.setRoot(ROOT_PROJECT);
index 8d33cc5ddf5e56400ff29b726ed965202030bd75..ef5b6b59f8a7f2cab384b9f723c2b15054870394 100644 (file)
@@ -59,8 +59,6 @@ import static org.sonar.server.measure.Rating.B;
 import static org.sonar.server.measure.Rating.C;
 import static org.sonar.server.measure.Rating.D;
 import static org.sonar.server.measure.Rating.E;
-import static org.sonar.core.metric.SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_REVIEW_RATING;
-import static org.sonar.core.metric.SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_REVIEW_RATING_KEY;
 
 class SecurityReviewMeasuresVisitorTest {
 
@@ -87,7 +85,6 @@ class SecurityReviewMeasuresVisitorTest {
   @RegisterExtension
   private final MetricRepositoryRule metricRepository = new MetricRepositoryRule()
     .add(SECURITY_REVIEW_RATING)
-    .add(SOFTWARE_QUALITY_SECURITY_REVIEW_RATING)
     .add(SECURITY_HOTSPOTS_REVIEWED)
     .add(SECURITY_HOTSPOTS_REVIEWED_STATUS)
     .add(SECURITY_HOTSPOTS_TO_REVIEW_STATUS);
@@ -115,11 +112,11 @@ class SecurityReviewMeasuresVisitorTest {
 
     underTest.visit(ROOT_PROJECT);
 
-    verifyRatingAndReviewedMeasures(FILE_1_REF, A, A, 100.0);
-    verifyRatingAndReviewedMeasures(FILE_2_REF, A, A, 100.0);
-    verifyRatingAndReviewedMeasures(DIRECTORY_REF, A, A, 100.0);
-    verifyRatingAndReviewedMeasures(ROOT_DIR_REF, A, A, 100.0);
-    verifyRatingAndReviewedMeasures(PROJECT_REF, A, A, 100.0);
+    verifyRatingAndReviewedMeasures(FILE_1_REF, A, 100.0);
+    verifyRatingAndReviewedMeasures(FILE_2_REF, A, 100.0);
+    verifyRatingAndReviewedMeasures(DIRECTORY_REF, A, 100.0);
+    verifyRatingAndReviewedMeasures(ROOT_DIR_REF, A, 100.0);
+    verifyRatingAndReviewedMeasures(PROJECT_REF, A, 100.0);
   }
 
   @Test
@@ -141,11 +138,11 @@ class SecurityReviewMeasuresVisitorTest {
 
     underTest.visit(ROOT_PROJECT);
 
-    verifyRatingAndReviewedMeasures(FILE_1_REF, A, A, 100.0);
-    verifyRatingAndReviewedMeasures(FILE_2_REF, A, B, 80.0);
-    verifyRatingAndReviewedMeasures(DIRECTORY_REF, A, B, 87.5);
-    verifyRatingAndReviewedMeasures(ROOT_DIR_REF, A, B, 87.5);
-    verifyRatingAndReviewedMeasures(PROJECT_REF, A, B, 87.5);
+    verifyRatingAndReviewedMeasures(FILE_1_REF, A, 100.0);
+    verifyRatingAndReviewedMeasures(FILE_2_REF, A, 80.0);
+    verifyRatingAndReviewedMeasures(DIRECTORY_REF, A, 87.5);
+    verifyRatingAndReviewedMeasures(ROOT_DIR_REF, A, 87.5);
+    verifyRatingAndReviewedMeasures(PROJECT_REF, A, 87.5);
   }
 
   @Test
@@ -167,11 +164,11 @@ class SecurityReviewMeasuresVisitorTest {
 
     underTest.visit(ROOT_PROJECT);
 
-    verifyRatingAndReviewedMeasures(FILE_1_REF, A, A, 100.0);
-    verifyRatingAndReviewedMeasures(FILE_2_REF, B, B, 71.4);
-    verifyRatingAndReviewedMeasures(DIRECTORY_REF, B, B, 75.0);
-    verifyRatingAndReviewedMeasures(ROOT_DIR_REF, B, B, 75.0);
-    verifyRatingAndReviewedMeasures(PROJECT_REF, B, B, 75.0);
+    verifyRatingAndReviewedMeasures(FILE_1_REF, A, 100.0);
+    verifyRatingAndReviewedMeasures(FILE_2_REF, B, 71.4);
+    verifyRatingAndReviewedMeasures(DIRECTORY_REF, B, 75.0);
+    verifyRatingAndReviewedMeasures(ROOT_DIR_REF, B, 75.0);
+    verifyRatingAndReviewedMeasures(PROJECT_REF, B, 75.0);
   }
 
   @Test
@@ -192,11 +189,11 @@ class SecurityReviewMeasuresVisitorTest {
 
     underTest.visit(ROOT_PROJECT);
 
-    verifyRatingAndReviewedMeasures(FILE_1_REF, C, C,50.0);
-    verifyRatingAndReviewedMeasures(FILE_2_REF, C, C,60.0);
-    verifyRatingAndReviewedMeasures(DIRECTORY_REF, C,C, 57.1);
-    verifyRatingAndReviewedMeasures(ROOT_DIR_REF, C, C,57.1);
-    verifyRatingAndReviewedMeasures(PROJECT_REF, C, C,57.1);
+    verifyRatingAndReviewedMeasures(FILE_1_REF, C, 50.0);
+    verifyRatingAndReviewedMeasures(FILE_2_REF, C, 60.0);
+    verifyRatingAndReviewedMeasures(DIRECTORY_REF, C, 57.1);
+    verifyRatingAndReviewedMeasures(ROOT_DIR_REF, C, 57.1);
+    verifyRatingAndReviewedMeasures(PROJECT_REF, C, 57.1);
   }
 
   @Test
@@ -218,11 +215,11 @@ class SecurityReviewMeasuresVisitorTest {
 
     underTest.visit(ROOT_PROJECT);
 
-    verifyRatingAndReviewedMeasures(FILE_1_REF, D, D,33.3);
-    verifyRatingAndReviewedMeasures(FILE_2_REF, D, D,40.0);
-    verifyRatingAndReviewedMeasures(DIRECTORY_REF, D,D, 37.5);
-    verifyRatingAndReviewedMeasures(ROOT_DIR_REF, D, D,37.5);
-    verifyRatingAndReviewedMeasures(PROJECT_REF, D, D,37.5);
+    verifyRatingAndReviewedMeasures(FILE_1_REF, D, 33.3);
+    verifyRatingAndReviewedMeasures(FILE_2_REF, D, 40.0);
+    verifyRatingAndReviewedMeasures(DIRECTORY_REF, D, 37.5);
+    verifyRatingAndReviewedMeasures(ROOT_DIR_REF, D, 37.5);
+    verifyRatingAndReviewedMeasures(PROJECT_REF, D, 37.5);
   }
 
   @Test
@@ -242,11 +239,11 @@ class SecurityReviewMeasuresVisitorTest {
 
     underTest.visit(ROOT_PROJECT);
 
-    verifyRatingAndReviewedMeasures(FILE_1_REF, D, D,33.3);
-    verifyRatingAndReviewedMeasures(FILE_2_REF, E, D,0.0);
-    verifyRatingAndReviewedMeasures(DIRECTORY_REF, E,D, 16.7);
-    verifyRatingAndReviewedMeasures(ROOT_DIR_REF, E, D,16.7);
-    verifyRatingAndReviewedMeasures(PROJECT_REF, E, D,16.7);
+    verifyRatingAndReviewedMeasures(FILE_1_REF, D, 33.3);
+    verifyRatingAndReviewedMeasures(FILE_2_REF, E, 0.0);
+    verifyRatingAndReviewedMeasures(DIRECTORY_REF, E, 16.7);
+    verifyRatingAndReviewedMeasures(ROOT_DIR_REF, E, 16.7);
+    verifyRatingAndReviewedMeasures(PROJECT_REF, E, 16.7);
   }
 
   @Test
@@ -255,7 +252,7 @@ class SecurityReviewMeasuresVisitorTest {
 
     underTest.visit(ROOT_PROJECT);
 
-    verifyRatingAndReviewedMeasures(PROJECT_REF, A, A,null);
+    verifyRatingAndReviewedMeasures(PROJECT_REF, A, null);
   }
 
   @Test
@@ -292,9 +289,8 @@ class SecurityReviewMeasuresVisitorTest {
     verifyHotspotStatusMeasures(PROJECT_REF, 0, 0);
   }
 
-  private void verifyRatingAndReviewedMeasures(int componentRef, Rating expectedReviewRating, Rating expectedSoftwareQualityReviewRating,
-    @Nullable Double expectedHotspotsReviewed) {
-    verifySecurityReviewRating(componentRef, expectedReviewRating, expectedSoftwareQualityReviewRating);
+  private void verifyRatingAndReviewedMeasures(int componentRef, Rating expectedReviewRating, @Nullable Double expectedHotspotsReviewed) {
+    verifySecurityReviewRating(componentRef, expectedReviewRating);
     if (expectedHotspotsReviewed != null) {
       verifySecurityHotspotsReviewed(componentRef, expectedHotspotsReviewed);
     } else {
@@ -302,13 +298,10 @@ class SecurityReviewMeasuresVisitorTest {
     }
   }
 
-  private void verifySecurityReviewRating(int componentRef, Rating rating, Rating softwareQualityRating) {
+  private void verifySecurityReviewRating(int componentRef, Rating rating) {
     Measure measure = measureRepository.getAddedRawMeasure(componentRef, SECURITY_REVIEW_RATING_KEY).get();
-    Measure softwareQualityMeasure = measureRepository.getAddedRawMeasure(componentRef, SOFTWARE_QUALITY_SECURITY_REVIEW_RATING_KEY).get();
     assertThat(measure.getIntValue()).isEqualTo(rating.getIndex());
     assertThat(measure.getData()).isEqualTo(rating.name());
-    assertThat(softwareQualityMeasure.getIntValue()).isEqualTo(softwareQualityRating.getIndex());
-    assertThat(softwareQualityMeasure.getData()).isEqualTo(softwareQualityRating.name());
   }
 
   private void verifySecurityHotspotsReviewed(int componentRef, double percent) {
index f7d898745b019538b84c012a2544f5e39dfd9a23..eb256bd630951cd8eaec56138da9b8437f308126 100644 (file)
@@ -77,9 +77,7 @@ public class ProjectMeasuresIndexerIterator extends CloseableIterator<ProjectMea
     SoftwareQualitiesMetrics.SOFTWARE_QUALITY_MAINTAINABILITY_RATING_KEY,
     SoftwareQualitiesMetrics.SOFTWARE_QUALITY_RELIABILITY_RATING_KEY,
     SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_RATING_KEY,
-    SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_REVIEW_RATING_KEY,
     SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_SECURITY_RATING_KEY,
-    SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_SECURITY_REVIEW_RATING_KEY,
     SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_MAINTAINABILITY_RATING_KEY,
     SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_RELIABILITY_RATING_KEY
     );
index 39b9e1cd6b02c851c7d24b9eec9a037a99952f66..76897cb5743eb5a9eba0d60786b0136cbab1b53b 100644 (file)
@@ -48,6 +48,7 @@ import org.sonar.db.component.ComponentDto;
 import org.sonar.db.component.ComponentTesting;
 import org.sonar.db.component.ProjectData;
 import org.sonar.db.es.EsQueueDto;
+import org.sonar.db.issue.ImpactDto;
 import org.sonar.db.issue.IssueDto;
 import org.sonar.db.issue.IssueTesting;
 import org.sonar.db.project.ProjectDto;
@@ -174,7 +175,9 @@ public class IssueIndexerIT {
     ComponentDto project = projectData.getMainBranchComponent();
     ComponentDto dir = db.components().insertComponent(ComponentTesting.newDirectory(project, "src/main/java/foo"));
     ComponentDto file = db.components().insertComponent(newFileDto(project, dir, "F1"));
-    IssueDto issue = db.issues().insert(rule, project, file);
+    IssueDto issue = db.issues().insert(rule, project, file,
+      i -> i.replaceAllImpacts(List.of(new ImpactDto().setSoftwareQuality(SoftwareQuality.MAINTAINABILITY).setSeverity(Severity.HIGH),
+        new ImpactDto().setSoftwareQuality(SoftwareQuality.SECURITY).setSeverity(Severity.INFO))));
 
     underTest.indexAllIssues();
 
@@ -205,7 +208,10 @@ public class IssueIndexerIT {
     assertThat(doc.impacts())
       .containsExactlyInAnyOrder(Map.of(
         SUB_FIELD_SOFTWARE_QUALITY, SoftwareQuality.MAINTAINABILITY.name(),
-        SUB_FIELD_SEVERITY, Severity.HIGH.name()));
+        SUB_FIELD_SEVERITY, Severity.HIGH.name()),
+        Map.of(
+          SUB_FIELD_SOFTWARE_QUALITY, SoftwareQuality.SECURITY.name(),
+          SUB_FIELD_SEVERITY, Severity.INFO.name()));
     assertThat(doc.issueStatus()).isEqualTo(issue.getIssueStatus().name());
   }
 
index a7be47d330e97c60e31eb9c6ec3f3a2b23031473..87c8474ffe6786cb387362f18660a8fc16bacd81 100644 (file)
@@ -50,11 +50,11 @@ import org.sonar.server.es.SearchOptions;
 import org.sonar.server.qualityprofile.index.ActiveRuleIndexer;
 import org.sonar.server.security.SecurityStandards;
 
-import static com.google.common.collect.ImmutableSet.of;
 import static java.util.Arrays.asList;
 import static java.util.Collections.emptyList;
 import static java.util.Collections.emptySet;
 import static java.util.Collections.singletonList;
+import static java.util.Set.of;
 import static java.util.stream.IntStream.rangeClosed;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
@@ -225,7 +225,8 @@ public class RuleIndexIT {
     RuleDto rule1 = insertJavaRule("My great rule CWE-123 which makes your code 1000 times better!", "123", "rule 123");
     RuleDto rule2 = insertJavaRule("Another great and shiny rule CWE-124", "124", "rule 124");
     RuleDto rule3 = insertJavaRule("Another great rule CWE-1000", "1000", "rule 1000");
-    RuleDto rule4 = insertJavaRule("<h1>HTML-Geeks</h1><p style=\"color:blue\">special formatting!</p><table><tr><td>inside</td><td>tables</td></tr></table>", "404", "rule 404");
+    RuleDto rule4 = insertJavaRule("<h1>HTML-Geeks</h1><p style=\"color:blue\">special " +
+      "formatting!</p><table><tr><td>inside</td><td>tables</td></tr></table>", "404", "rule 404");
     RuleDto rule5 = insertJavaRule("internationalization missunderstandings alsdkjfnadklsjfnadkdfnsksdjfn", "405", "rule 405");
     index();
 
@@ -323,7 +324,7 @@ public class RuleIndexIT {
 
   @Test
   public void tags_facet_supports_selected_value_with_regexp_special_characters() {
-    createRule(r -> r.setTags(Set.of("misra++")));
+    createRule(r -> r.setTags(of("misra++")));
     index();
 
     RuleQuery query = new RuleQuery()
@@ -480,7 +481,8 @@ public class RuleIndexIT {
 
   @Test
   public void search_by_security_owaspTop10_2021_return_vulnerabilities_and_hotspots_only() {
-    RuleDto rule1 = createRule(setSecurityStandards(of("owaspTop10-2021:a1", "owaspTop10-2021:a10", "cwe:543")), r -> r.setType(VULNERABILITY));
+    RuleDto rule1 = createRule(setSecurityStandards(of("owaspTop10-2021:a1", "owaspTop10-2021:a10", "cwe:543")),
+      r -> r.setType(VULNERABILITY));
     RuleDto rule2 = createRule(setSecurityStandards(of("owaspTop10-2021:a10", "cwe:543")), r -> r.setType(SECURITY_HOTSPOT));
     createRule(setSecurityStandards(of("cwe:543")), r -> r.setType(CODE_SMELL));
     index();
@@ -521,7 +523,8 @@ public class RuleIndexIT {
 
     // Creation of one rule for each standard security category defined (except other)
     for (Map.Entry<SecurityStandards.SQCategory, Set<String>> sqCategorySetEntry : SecurityStandards.CWES_BY_SQ_CATEGORY.entrySet()) {
-      rules.add(createRule(setSecurityStandards(of("cwe:" + sqCategorySetEntry.getValue().iterator().next())), r -> r.setType(SECURITY_HOTSPOT)));
+      rules.add(createRule(setSecurityStandards(of("cwe:" + sqCategorySetEntry.getValue().iterator().next())),
+        r -> r.setType(SECURITY_HOTSPOT)));
     }
     index();
 
@@ -553,9 +556,11 @@ public class RuleIndexIT {
     db.qualityProfiles().activateRule(anotherProfile, anotherProfileRule2);
     index();
 
-    verifySearch(newRuleQuery().setActivation(false).setQProfile(profile).setCompareToQProfile(anotherProfile), anotherProfileRule1, anotherProfileRule2);
+    verifySearch(newRuleQuery().setActivation(false).setQProfile(profile).setCompareToQProfile(anotherProfile), anotherProfileRule1,
+      anotherProfileRule2);
     verifySearch(newRuleQuery().setActivation(true).setQProfile(profile).setCompareToQProfile(anotherProfile), commonRule);
-    verifySearch(newRuleQuery().setActivation(true).setQProfile(profile).setCompareToQProfile(profile), commonRule, profileRule1, profileRule2, profileRule3);
+    verifySearch(newRuleQuery().setActivation(true).setQProfile(profile).setCompareToQProfile(profile), commonRule, profileRule1,
+      profileRule2, profileRule3);
     verifySearch(newRuleQuery().setActivation(false).setQProfile(profile).setCompareToQProfile(profile));
   }
 
@@ -704,7 +709,8 @@ public class RuleIndexIT {
     verifyEmptySearch(newRuleQuery().setActivation(true).setQProfile(parent).setInheritance(of(INHERITED.name(), OVERRIDES.name())));
 
     // inherited AND overridden on child
-    verifySearch(newRuleQuery().setActivation(true).setQProfile(child).setInheritance(of(INHERITED.name(), OVERRIDES.name())), rule1, rule2, rule3);
+    verifySearch(newRuleQuery().setActivation(true).setQProfile(child).setInheritance(of(INHERITED.name(), OVERRIDES.name())), rule1,
+      rule2, rule3);
   }
 
   @Test
@@ -812,17 +818,19 @@ public class RuleIndexIT {
     index();
 
     RuleQuery query = new RuleQuery();
-    SearchIdResult result1 = underTest.search(query.setImpactSoftwareQualities(List.of(SoftwareQuality.MAINTAINABILITY.name())), new SearchOptions());
+    SearchIdResult result1 = underTest.search(query.setImpactSoftwareQualities(List.of(SoftwareQuality.MAINTAINABILITY.name())),
+      new SearchOptions());
     assertThat(result1.getUuids()).isEmpty();
 
     query = new RuleQuery();
-    SearchIdResult result2 = underTest.search(query.setImpactSoftwareQualities(List.of(SoftwareQuality.SECURITY.name())), new SearchOptions());
+    SearchIdResult result2 = underTest.search(query.setImpactSoftwareQualities(List.of(SoftwareQuality.SECURITY.name())),
+      new SearchOptions());
     assertThat(result2.getUuids()).containsOnly(phpRule.getUuid());
   }
 
   @Test
   public void search_by_severity() {
-    ImpactDto impactDto = new ImpactDto().setSoftwareQuality(SoftwareQuality.SECURITY).setSeverity(Severity.HIGH);
+    ImpactDto impactDto = new ImpactDto().setSoftwareQuality(SoftwareQuality.SECURITY).setSeverity(Severity.BLOCKER);
     RuleDto phpRule = createRule(setRepositoryKey("php"), setImpacts(List.of(impactDto)));
     index();
 
@@ -831,7 +839,7 @@ public class RuleIndexIT {
     assertThat(result1.getUuids()).isEmpty();
 
     query = new RuleQuery();
-    SearchIdResult result2 = underTest.search(query.setImpactSeverities(List.of(Severity.HIGH.name())), new SearchOptions());
+    SearchIdResult result2 = underTest.search(query.setImpactSeverities(List.of(Severity.BLOCKER.name())), new SearchOptions());
     assertThat(result2.getUuids()).containsOnly(phpRule.getUuid());
   }
 
@@ -846,7 +854,8 @@ public class RuleIndexIT {
     SearchIdResult result2 = underTest.search(query, new SearchOptions().addFacets(singletonList("cleanCodeAttributeCategories")));
 
     assertThat(result2.getFacets().getAll()).hasSize(1);
-    assertThat(result2.getFacets().getAll().get("cleanCodeAttributeCategories")).containsOnly(entry("ADAPTABLE", 1L), entry("INTENTIONAL", 1L));
+    assertThat(result2.getFacets().getAll().get("cleanCodeAttributeCategories")).containsOnly(entry("ADAPTABLE", 1L), entry("INTENTIONAL"
+      , 1L));
   }
 
   @Test
@@ -859,7 +868,8 @@ public class RuleIndexIT {
     RuleQuery query = new RuleQuery();
 
     SearchIdResult result = underTest.search(
-      query.setCleanCodeAttributesCategories(List.of(CleanCodeAttributeCategory.CONSISTENT.name(), CleanCodeAttributeCategory.ADAPTABLE.name())),
+      query.setCleanCodeAttributesCategories(List.of(CleanCodeAttributeCategory.CONSISTENT.name(),
+        CleanCodeAttributeCategory.ADAPTABLE.name())),
       new SearchOptions().addFacets(singletonList("cleanCodeAttributeCategories")));
 
     assertThat(result.getUuids()).containsExactlyInAnyOrder(php.getUuid(), java.getUuid());
@@ -895,7 +905,8 @@ public class RuleIndexIT {
 
     RuleQuery query = new RuleQuery();
 
-    SearchIdResult result2 = underTest.search(query.setImpactSeverities(Set.of(Severity.HIGH.name())), new SearchOptions().addFacets(singletonList("impactSoftwareQualities")));
+    SearchIdResult result2 = underTest.search(query.setImpactSeverities(of(Severity.HIGH.name())),
+      new SearchOptions().addFacets(singletonList("impactSoftwareQualities")));
 
     assertThat(result2.getFacets().getAll()).hasSize(1);
     assertThat(result2.getFacets().getAll().get("impactSoftwareQualities"))
@@ -910,14 +921,21 @@ public class RuleIndexIT {
     ImpactDto impactDto = new ImpactDto().setSoftwareQuality(SoftwareQuality.MAINTAINABILITY).setSeverity(Severity.HIGH);
     ImpactDto impactDto2 = new ImpactDto().setSoftwareQuality(SoftwareQuality.MAINTAINABILITY).setSeverity(Severity.LOW);
     ImpactDto impactDto3 = new ImpactDto().setSoftwareQuality(SoftwareQuality.RELIABILITY).setSeverity(Severity.LOW);
+    ImpactDto impactDto4 = new ImpactDto().setSoftwareQuality(SoftwareQuality.RELIABILITY).setSeverity(Severity.BLOCKER);
+    ImpactDto impactDto5 = new ImpactDto().setSoftwareQuality(SoftwareQuality.MAINTAINABILITY).setSeverity(Severity.BLOCKER);
+    ImpactDto impactDto6 = new ImpactDto().setSoftwareQuality(SoftwareQuality.RELIABILITY).setSeverity(Severity.INFO);
     createRule(setRepositoryKey("php"), setImpacts(List.of(impactDto)));
-    createRule(setRepositoryKey("php"), setImpacts(List.of(impactDto2, impactDto3)));
+    createRule(setRepositoryKey("php"), setImpacts(List.of(impactDto2)));
+    createRule(setRepositoryKey("php"), setImpacts(List.of(impactDto3)));
+    createRule(setRepositoryKey("php"), setImpacts(List.of(impactDto4)));
+    createRule(setRepositoryKey("php"), setImpacts(List.of(impactDto5)));
+    createRule(setRepositoryKey("php"), setImpacts(List.of(impactDto6)));
     index();
 
     RuleQuery query = new RuleQuery();
 
     SearchIdResult result = underTest.search(
-      query.setImpactSeverities(Set.of(Severity.LOW.name())).setImpactSoftwareQualities(List.of(SoftwareQuality.MAINTAINABILITY.name())),
+      query.setImpactSeverities(of(Severity.LOW.name())).setImpactSoftwareQualities(List.of(SoftwareQuality.MAINTAINABILITY.name())),
       new SearchOptions().addFacets(List.of("impactSoftwareQualities", "impactSeverities")));
 
     assertThat(result.getFacets().getAll()).hasSize(2);
@@ -931,15 +949,18 @@ public class RuleIndexIT {
       .containsOnly(
         entry("HIGH", 1L),
         entry("MEDIUM", 0L),
-        entry("LOW", 1L));
+        entry("LOW", 1L),
+        entry("INFO", 0L),
+        entry("BLOCKER", 1L));
   }
 
   @Test
   public void search_should_support_severity_facet() {
     ImpactDto impactDto = new ImpactDto().setSoftwareQuality(SoftwareQuality.SECURITY).setSeverity(Severity.HIGH);
     ImpactDto impactDto2 = new ImpactDto().setSoftwareQuality(SoftwareQuality.MAINTAINABILITY).setSeverity(Severity.LOW);
+    ImpactDto impactDto3 = new ImpactDto().setSoftwareQuality(SoftwareQuality.RELIABILITY).setSeverity(Severity.BLOCKER);
     createRule(setRepositoryKey("php"), setImpacts(List.of(impactDto)));
-    createRule(setRepositoryKey("php"), setImpacts(List.of(impactDto2)));
+    createRule(setRepositoryKey("php"), setImpacts(List.of(impactDto2, impactDto3)));
     index();
 
     RuleQuery query = new RuleQuery();
@@ -947,40 +968,65 @@ public class RuleIndexIT {
     SearchIdResult result2 = underTest.search(query, new SearchOptions().addFacets(singletonList("impactSeverities")));
 
     assertThat(result2.getFacets().getAll()).hasSize(1);
-    assertThat(result2.getFacets().getAll().get("impactSeverities")).containsOnly(entry("LOW", 1L), entry("MEDIUM", 0L), entry("HIGH", 1L));
+    assertThat(result2.getFacets().getAll().get("impactSeverities"))
+      .containsOnly(
+        entry("LOW", 1L),
+        entry("MEDIUM", 0L),
+        entry("HIGH", 1L),
+        entry("INFO", 0L),
+        entry("BLOCKER", 1L));
   }
 
   @Test
   public void search_should_support_severity_facet_with_filters() {
     ImpactDto impactDto = new ImpactDto().setSoftwareQuality(SoftwareQuality.SECURITY).setSeverity(Severity.HIGH);
     ImpactDto impactDto2 = new ImpactDto().setSoftwareQuality(SoftwareQuality.MAINTAINABILITY).setSeverity(Severity.LOW);
+    ImpactDto impactDto3 = new ImpactDto().setSoftwareQuality(SoftwareQuality.SECURITY).setSeverity(Severity.INFO);
+    ImpactDto impactDto4 = new ImpactDto().setSoftwareQuality(SoftwareQuality.MAINTAINABILITY).setSeverity(Severity.INFO);
+    ImpactDto impactDto5 = new ImpactDto().setSoftwareQuality(SoftwareQuality.SECURITY).setSeverity(Severity.BLOCKER);
     createRule(setRepositoryKey("php"), setImpacts(List.of(impactDto)));
-    createRule(setRepositoryKey("php"), setImpacts(List.of(impactDto2)));
+    createRule(setRepositoryKey("php"), setImpacts(List.of(impactDto2, impactDto3)));
+    createRule(setRepositoryKey("php"), setImpacts(List.of(impactDto4, impactDto5)));
     index();
 
     RuleQuery query = new RuleQuery();
 
-    SearchIdResult result2 = underTest.search(query.setImpactSeverities(Set.of("LOW")), new SearchOptions().addFacets(singletonList("impactSeverities")));
+    SearchIdResult result2 = underTest.search(query.setImpactSeverities(of("LOW")), new SearchOptions().addFacets(singletonList(
+      "impactSeverities")));
 
     assertThat(result2.getFacets().getAll()).hasSize(1);
-    assertThat(result2.getFacets().getAll().get("impactSeverities")).containsOnly(entry("LOW", 1L), entry("MEDIUM", 0L), entry("HIGH", 1L));
+    assertThat(result2.getFacets().getAll().get("impactSeverities"))
+      .containsOnly(
+        entry("LOW", 1L),
+        entry("MEDIUM", 0L),
+        entry("HIGH", 1L),
+        entry("INFO", 2L),
+        entry("BLOCKER", 1L));
   }
 
   @Test
   public void search_should_support_software_quality_and_severity_facets_with_filtering() {
     ImpactDto impactDto = new ImpactDto().setSoftwareQuality(SoftwareQuality.SECURITY).setSeverity(Severity.HIGH);
     ImpactDto impactDto2 = new ImpactDto().setSoftwareQuality(SoftwareQuality.MAINTAINABILITY).setSeverity(Severity.LOW);
+    ImpactDto impactDto3 = new ImpactDto().setSoftwareQuality(SoftwareQuality.MAINTAINABILITY).setSeverity(Severity.BLOCKER);
     createRule(setRepositoryKey("php"), setImpacts(List.of(impactDto)));
     createRule(setRepositoryKey("php"), setImpacts(List.of(impactDto2)));
+    createRule(setRepositoryKey("php"), setImpacts(List.of(impactDto3)));
     index();
 
-    RuleQuery query = new RuleQuery().setImpactSeverities(Set.of("LOW"))
-      .setImpactSoftwareQualities(Set.of(SoftwareQuality.MAINTAINABILITY.name()));
+    RuleQuery query = new RuleQuery().setImpactSeverities(of("LOW"))
+      .setImpactSoftwareQualities(of(SoftwareQuality.MAINTAINABILITY.name()));
     SearchOptions searchOptions = new SearchOptions().addFacets(List.of("impactSeverities", "impactSoftwareQualities"));
     SearchIdResult result2 = underTest.search(query, searchOptions);
 
     assertThat(result2.getFacets().getAll()).hasSize(2);
-    assertThat(result2.getFacets().getAll().get("impactSeverities")).containsOnly(entry("LOW", 1L), entry("MEDIUM", 0L), entry("HIGH", 0L));
+    assertThat(result2.getFacets().getAll().get("impactSeverities"))
+      .containsOnly(
+        entry("LOW", 1L),
+        entry("MEDIUM", 0L),
+        entry("HIGH", 0L),
+        entry("INFO", 0L),
+        entry("BLOCKER", 1L));
     assertThat(result2.getFacets().getAll().get("impactSoftwareQualities")).containsOnly(
       entry(SoftwareQuality.SECURITY.name(), 0L),
       entry(SoftwareQuality.MAINTAINABILITY.name(), 1L),
@@ -1000,7 +1046,8 @@ public class RuleIndexIT {
     assertThat(result1.getFacets().getAll()).isEmpty();
 
     // should not have any facet on non matching query!
-    SearchIdResult result2 = underTest.search(new RuleQuery().setQueryText("aeiou"), new SearchOptions().addFacets(singletonList("repositories")));
+    SearchIdResult result2 = underTest.search(new RuleQuery().setQueryText("aeiou"), new SearchOptions().addFacets(singletonList(
+      "repositories")));
     assertThat(result2.getFacets().getAll()).hasSize(1);
     assertThat(result2.getFacets().getAll().get("repositories")).isEmpty();
 
@@ -1070,7 +1117,8 @@ public class RuleIndexIT {
     setupStickyFacets();
     RuleQuery query = new RuleQuery().setLanguages(ImmutableList.of("cpp"));
 
-    SearchIdResult<String> result = underTest.search(query, new SearchOptions().addFacets(asList(FACET_LANGUAGES, FACET_REPOSITORIES, FACET_TAGS)));
+    SearchIdResult<String> result = underTest.search(query, new SearchOptions().addFacets(asList(FACET_LANGUAGES, FACET_REPOSITORIES,
+      FACET_TAGS)));
     assertThat(result.getUuids()).hasSize(3);
     assertThat(result.getFacets().getAll()).hasSize(3);
     assertThat(result.getFacets().get(FACET_LANGUAGES)).containsOnlyKeys("cpp", "java", "cobol");
@@ -1194,7 +1242,8 @@ public class RuleIndexIT {
       .setTags(ImmutableList.of("T2"))
       .setTypes(asList(BUG, CODE_SMELL));
 
-    SearchIdResult<String> result = underTest.search(query, new SearchOptions().addFacets(asList(FACET_LANGUAGES, FACET_REPOSITORIES, FACET_TAGS,
+    SearchIdResult<String> result = underTest.search(query, new SearchOptions().addFacets(asList(FACET_LANGUAGES, FACET_REPOSITORIES,
+      FACET_TAGS,
       FACET_TYPES)));
     assertThat(result.getUuids()).hasSize(2);
     assertThat(result.getFacets().getAll()).hasSize(4);
index 4bbc2b54ac7a542fa7f72f90ec584f8cef7004d3..247a1829f98fa27f6e5d326956c1f962057529f7 100644 (file)
@@ -78,17 +78,6 @@ public class DebtRatingGrid {
       .orElseThrow(() -> new IllegalArgumentException(format("Invalid value '%s'", value)));
   }
 
-  /**
-   * Computes a rating from A to D, where E is converted to D.
-   */
-  public Rating getAToDRatingForDensity(double value) {
-    return ratingBounds.entrySet().stream()
-      .filter(e -> e.getValue().match(value))
-      .map(e -> e.getKey() == E ? D : e.getKey())
-      .findFirst()
-      .orElseThrow(() -> new IllegalArgumentException(format("Invalid value '%s'", value)));
-  }
-
   public double getGradeLowerBound(Rating rating) {
     if (rating.getIndex() > 1) {
       return gridValues[rating.getIndex() - 2];
index 3167b5d03eec999cb2e2fe8a45a099ef72b346e6..eae57b9d4829fa19ddcd5696a3a9934be1fa20af 100644 (file)
@@ -23,7 +23,6 @@ import com.google.gson.Gson;
 import com.google.gson.GsonBuilder;
 import com.google.gson.reflect.TypeToken;
 import java.lang.reflect.Type;
-import java.util.Arrays;
 import java.util.LinkedHashMap;
 import java.util.Map;
 import javax.annotation.CheckForNull;
@@ -32,7 +31,8 @@ import org.sonar.api.issue.impact.Severity;
 import static org.sonar.api.utils.Preconditions.checkArgument;
 
 /**
- * Builder class to help build measures based on impacts with payload such as @{link {@link org.sonar.api.measures.CoreMetrics#RELIABILITY_ISSUES}}.
+ * Builder class to help build measures based on impacts with payload such as @{link
+ * {@link org.sonar.api.measures.CoreMetrics#RELIABILITY_ISSUES}}.
  */
 public class ImpactMeasureBuilder {
 
@@ -65,9 +65,16 @@ public class ImpactMeasureBuilder {
     return new ImpactMeasureBuilder(map);
   }
 
-  private static void checkImpactMap(Map<String, Long> map) {
-    checkArgument(map.containsKey(TOTAL_KEY), "Map must contain a total key");
-    Arrays.stream(Severity.values()).forEach(severity -> checkArgument(map.containsKey(severity.name()), "Map must contain a key for severity " + severity.name()));
+  /**
+   * As we moved from 3 to 5 severities, we need to be able to handle measures saved with missing severities.
+   */
+  private static void checkImpactMap(Map<String, Long> impactMap) {
+    checkArgument(impactMap.containsKey(TOTAL_KEY), "Map must contain a total key");
+    for (Severity severity : Severity.values()) {
+      if (!impactMap.containsKey(severity.name())) {
+        impactMap.put(severity.name(), 0L);
+      }
+    }
   }
 
   public static ImpactMeasureBuilder fromString(String value) {
index f00b8796dd8c075fa8f9049470bcda4a65c79ddd..3ba14d771a380487990257f5d66a46f3f6b0751a 100644 (file)
@@ -45,9 +45,11 @@ public enum Rating {
     INFO, A);
 
   public static final Map<Severity, Rating> RATING_BY_SOFTWARE_QUALITY_SEVERITY = Map.of(
+    Severity.BLOCKER, Rating.E,
     Severity.HIGH, Rating.D,
     Severity.MEDIUM, Rating.C,
-    Severity.LOW, Rating.B);
+    Severity.LOW, Rating.B,
+    Severity.INFO, Rating.A);
 
   private final int index;
 
index fecce77fcd49521cf5f53bdb6a2be49fa5d902a7..2c2a1d017b17b10c6d3ce3b389f1495a009ec9fd 100644 (file)
@@ -26,7 +26,7 @@ import org.sonar.server.measure.Rating;
 
 /**
  * This class defines "default" measures values for the "Software Quality Rating Metrics" when they do not exist.
- * The "default" value is the same as the equivalent "Rule Type Rating Metric", except for E Rating that is converted to D Rating
+ * The "default" value is the same as the equivalent "Rule Type Rating Metric"
   * If the "Software Quality Rating Metrics" exists, then no changes are made
  */
 public class ProjectMeasuresSoftwareQualityRatingsInitializer {
@@ -35,9 +35,7 @@ public class ProjectMeasuresSoftwareQualityRatingsInitializer {
     CoreMetrics.SQALE_RATING_KEY, SoftwareQualitiesMetrics.SOFTWARE_QUALITY_MAINTAINABILITY_RATING_KEY,
     CoreMetrics.RELIABILITY_RATING_KEY, SoftwareQualitiesMetrics.SOFTWARE_QUALITY_RELIABILITY_RATING_KEY,
     CoreMetrics.SECURITY_RATING_KEY, SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_RATING_KEY,
-    CoreMetrics.SECURITY_REVIEW_RATING_KEY, SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_REVIEW_RATING_KEY,
     CoreMetrics.NEW_SECURITY_RATING_KEY, SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_SECURITY_RATING_KEY,
-    CoreMetrics.NEW_SECURITY_REVIEW_RATING_KEY, SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_SECURITY_REVIEW_RATING_KEY,
     CoreMetrics.NEW_MAINTAINABILITY_RATING_KEY, SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_MAINTAINABILITY_RATING_KEY,
     CoreMetrics.NEW_RELIABILITY_RATING_KEY, SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_RELIABILITY_RATING_KEY
   );
@@ -59,7 +57,7 @@ public class ProjectMeasuresSoftwareQualityRatingsInitializer {
 
     Double value = measures.get(ruleTypeMetric);
     if (value != null) {
-      measures.put(softwareQualityMetric, value > Rating.D.getIndex() ? Double.valueOf(Rating.D.getIndex()) : value);
+      measures.put(softwareQualityMetric, value);
     }
   }
 }
index 69791800a7b9e2d5a6423b60f5c4b0c8182acb4a..1af9a5db8c8428e363b39ac0ab94501a961239ff 100644 (file)
@@ -56,14 +56,4 @@ public class SecurityReviewRating {
     return E;
   }
 
-  public static Rating computeAToDRating(@Nullable Double percent) {
-    if (percent == null || Math.abs(percent - 100.0D) < 10e-6) {
-      return A;
-    } else if (percent >= 70.0D) {
-      return B;
-    } else if (percent >= 50.0D) {
-      return C;
-    }
-    return D;
-  }
 }
index 6557e1088150a202c916efed74ff26d2cfd2808f..8a312cba5993ec82885e9367a624c2135fb22d28 100644 (file)
@@ -54,21 +54,6 @@ public class DebtRatingGridTest {
     assertThat(ratingGrid.getRatingForDensity(1.01)).isEqualTo(E);
   }
 
-  @Test
-  public void getAToDRatingForDensity_returnsValueBetweenAAndD() {
-    assertThat(ratingGrid.getAToDRatingForDensity(0)).isEqualTo(A);
-    assertThat(ratingGrid.getAToDRatingForDensity(0.05)).isEqualTo(A);
-    assertThat(ratingGrid.getAToDRatingForDensity(0.09999999)).isEqualTo(A);
-    assertThat(ratingGrid.getAToDRatingForDensity(0.1)).isEqualTo(A);
-    assertThat(ratingGrid.getAToDRatingForDensity(0.15)).isEqualTo(B);
-    assertThat(ratingGrid.getAToDRatingForDensity(0.2)).isEqualTo(B);
-    assertThat(ratingGrid.getAToDRatingForDensity(0.25)).isEqualTo(C);
-    assertThat(ratingGrid.getAToDRatingForDensity(0.5)).isEqualTo(C);
-    assertThat(ratingGrid.getAToDRatingForDensity(0.65)).isEqualTo(D);
-    assertThat(ratingGrid.getAToDRatingForDensity(1)).isEqualTo(D);
-    assertThat(ratingGrid.getAToDRatingForDensity(1.01)).isEqualTo(D);
-  }
-
   @Test
   public void density_matching_exact_grid_values() {
     assertThat(ratingGrid.getRatingForDensity(0.1)).isEqualTo(A);
index 82f26c979c79f748d2ab4745d9ec2de3bcdd57f3..a7a393a6c62329929399d15c35c8a57a3a8006e0 100644 (file)
  */
 package org.sonar.server.measure;
 
+import java.util.HashMap;
 import java.util.Map;
 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;
+import static org.sonar.api.issue.impact.Severity.BLOCKER;
+import static org.sonar.api.issue.impact.Severity.HIGH;
+import static org.sonar.api.issue.impact.Severity.INFO;
+import static org.sonar.api.issue.impact.Severity.LOW;
+import static org.sonar.api.issue.impact.Severity.MEDIUM;
 
 class ImpactMeasureBuilderTest {
 
@@ -32,17 +37,17 @@ class ImpactMeasureBuilderTest {
   void createEmptyMeasure_shouldReturnMeasureWithAllFields() {
     ImpactMeasureBuilder builder = ImpactMeasureBuilder.createEmpty();
     assertThat(builder.buildAsMap())
-      .containsAllEntriesOf(getImpactMap(0L, 0L, 0L, 0L));
-  }
-
-  private static Map<String, Long> getImpactMap(Long total, Long high, Long medium, Long low) {
-    return Map.of("total", total, Severity.HIGH.name(), high, Severity.MEDIUM.name(), medium, Severity.LOW.name(), low);
+      .containsAllEntriesOf(getImpactMap(0L, 0L, 0L, 0L, 0L, 0L));
   }
 
   @Test
   void fromMap_shouldInitializeCorrectlyTheBuilder() {
     Map<String, Long> map = getImpactMap(6L, 3L, 2L, 1L);
     ImpactMeasureBuilder builder = ImpactMeasureBuilder.fromMap(map);
+
+    map.put(INFO.name(), 0L);
+    map.put(BLOCKER.name(), 0L);
+
     assertThat(builder.buildAsMap())
       .isEqualTo(map);
   }
@@ -65,8 +70,9 @@ class ImpactMeasureBuilderTest {
         LOW: 1
       }
       """);
+    Map<String, Long> expectedMap = getImpactMap(6L, 3L, 2L, 1L, 0L, 0L);
     assertThat(builder.buildAsMap())
-      .isEqualTo(getImpactMap(6L, 3L, 2L, 1L));
+      .isEqualTo(expectedMap);
   }
 
   @Test
@@ -77,45 +83,31 @@ class ImpactMeasureBuilderTest {
       .hasMessage("Map must contain a total key");
   }
 
-  @Test
-  void buildAsMap_whenMissingSeverity_shouldThrowException() {
-    ImpactMeasureBuilder impactMeasureBuilder = ImpactMeasureBuilder.newInstance()
-      .setTotal(1L)
-      .setSeverity(Severity.HIGH, 1L)
-      .setSeverity(Severity.MEDIUM, 1L);
-    assertThatThrownBy(impactMeasureBuilder::buildAsMap)
-      .isInstanceOf(IllegalArgumentException.class)
-      .hasMessage("Map must contain a key for severity LOW");
-  }
-
-  @Test
-  void buildAsString_whenMissingSeverity_shouldThrowException() {
-    ImpactMeasureBuilder impactMeasureBuilder = ImpactMeasureBuilder.newInstance()
-      .setTotal(1L)
-      .setSeverity(Severity.HIGH, 1L)
-      .setSeverity(Severity.MEDIUM, 1L);
-    assertThatThrownBy(impactMeasureBuilder::buildAsString)
-      .isInstanceOf(IllegalArgumentException.class)
-      .hasMessage("Map must contain a key for severity LOW");
-  }
-
   @Test
   void setSeverity_shouldInitializeSeverityValues() {
     ImpactMeasureBuilder builder = ImpactMeasureBuilder.newInstance()
-      .setSeverity(Severity.HIGH, 3L)
-      .setSeverity(Severity.MEDIUM, 2L)
-      .setSeverity(Severity.LOW, 1L)
-      .setTotal(6L);
+      .setSeverity(HIGH, 3L)
+      .setSeverity(MEDIUM, 2L)
+      .setSeverity(LOW, 1L)
+      .setSeverity(INFO, 4L)
+      .setSeverity(BLOCKER, 5L)
+      .setTotal(15L);
     assertThat(builder.buildAsMap())
-      .isEqualTo(getImpactMap(6L, 3L, 2L, 1L));
+      .isEqualTo(getImpactMap(15L, 3L, 2L, 1L, 4L, 5L));
   }
 
   @Test
   void add_shouldSumImpactsAndTotal() {
-    ImpactMeasureBuilder builder = ImpactMeasureBuilder.fromMap(getImpactMap(6L, 3L, 2L, 1L))
-      .add(ImpactMeasureBuilder.newInstance().setTotal(6L).setSeverity(Severity.HIGH, 3L).setSeverity(Severity.MEDIUM, 2L).setSeverity(Severity.LOW, 1L));
+    ImpactMeasureBuilder builder = ImpactMeasureBuilder.fromMap(getImpactMap(11L, 3L, 2L, 1L, 1L, 4L))
+      .add(ImpactMeasureBuilder.newInstance()
+        .setTotal(14L)
+        .setSeverity(HIGH, 3L)
+        .setSeverity(MEDIUM, 2L)
+        .setSeverity(LOW, 1L)
+        .setSeverity(INFO, 5L)
+        .setSeverity(BLOCKER, 3L));
     assertThat(builder.buildAsMap())
-      .isEqualTo(getImpactMap(12L, 6L, 4L, 2L));
+      .isEqualTo(getImpactMap(25L, 6L, 4L, 2L, 6L, 7L));
   }
 
   @Test
@@ -128,10 +120,28 @@ class ImpactMeasureBuilderTest {
   }
 
   @Test
-  void getTotal_shoudReturnExpectedTotal() {
+  void getTotal_shouldReturnExpectedTotal() {
     ImpactMeasureBuilder builder = ImpactMeasureBuilder.fromMap(getImpactMap(6L, 3L, 2L, 1L));
 
     assertThat(builder.getTotal()).isEqualTo(6L);
   }
 
+  private static Map<String, Long> getImpactMap(Long total, Long high, Long medium, Long low) {
+    return new HashMap<>() {
+      {
+        put("total", total);
+        put(HIGH.name(), high);
+        put(MEDIUM.name(), medium);
+        put(LOW.name(), low);
+      }
+    };
+  }
+
+  private static Map<String, Long> getImpactMap(Long total, Long high, Long medium, Long low, Long info, Long blocker) {
+    Map<String, Long> impactMap = getImpactMap(total, high, medium, low);
+    impactMap.put(INFO.name(), info);
+    impactMap.put(BLOCKER.name(), blocker);
+    return impactMap;
+  }
+
 }
index ed9119174ecabaae7236583dc5e1788df25aa1d2..e1dddaaafc00703753ff060b0d0b0e056c6fdd0e 100644 (file)
@@ -49,10 +49,8 @@ class ProjectMeasuresSoftwareQualityRatingsInitializerTest {
     initialMeasures.put(CoreMetrics.SQALE_RATING_KEY, 1.0);
     initialMeasures.put(CoreMetrics.RELIABILITY_RATING_KEY, 2.0);
     initialMeasures.put(CoreMetrics.SECURITY_RATING_KEY, 3.0);
-    initialMeasures.put(CoreMetrics.SECURITY_REVIEW_RATING_KEY, 4.0);
     initialMeasures.put(CoreMetrics.NEW_SECURITY_RATING_KEY, 4.0);
-    initialMeasures.put(CoreMetrics.NEW_SECURITY_REVIEW_RATING_KEY, 3.0);
-    initialMeasures.put(CoreMetrics.NEW_MAINTAINABILITY_RATING_KEY, 2.0);
+    initialMeasures.put(CoreMetrics.NEW_MAINTAINABILITY_RATING_KEY, 5.0);
     initialMeasures.put(CoreMetrics.NEW_RELIABILITY_RATING_KEY, 1.0);
 
     Map<String, Double> measures = new HashMap<>(initialMeasures);
@@ -64,38 +62,8 @@ class ProjectMeasuresSoftwareQualityRatingsInitializerTest {
       .containsEntry(SoftwareQualitiesMetrics.SOFTWARE_QUALITY_MAINTAINABILITY_RATING_KEY, 1.0)
       .containsEntry(SoftwareQualitiesMetrics.SOFTWARE_QUALITY_RELIABILITY_RATING_KEY, 2.0)
       .containsEntry(SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_RATING_KEY, 3.0)
-      .containsEntry(SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_REVIEW_RATING_KEY, 4.0)
       .containsEntry(SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_SECURITY_RATING_KEY, 4.0)
-      .containsEntry(SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_SECURITY_REVIEW_RATING_KEY, 3.0)
-      .containsEntry(SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_MAINTAINABILITY_RATING_KEY, 2.0)
+      .containsEntry(SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_MAINTAINABILITY_RATING_KEY, 5.0)
       .containsEntry(SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_RELIABILITY_RATING_KEY, 1.0);
   }
-
-  @Test
-  void initializeSoftwareQualityRatings_whenERating_thenSoftwareQualityRatingCreatedWithD() {
-    Map<String, Double> initialMeasures = new HashMap<>();
-    initialMeasures.put(CoreMetrics.SQALE_RATING_KEY, (double) Rating.E.getIndex());
-    initialMeasures.put(CoreMetrics.RELIABILITY_RATING_KEY, (double) Rating.E.getIndex());
-    initialMeasures.put(CoreMetrics.SECURITY_RATING_KEY, (double) Rating.E.getIndex());
-    initialMeasures.put(CoreMetrics.SECURITY_REVIEW_RATING_KEY, (double) Rating.E.getIndex());
-    initialMeasures.put(CoreMetrics.NEW_SECURITY_RATING_KEY, (double) Rating.E.getIndex());
-    initialMeasures.put(CoreMetrics.NEW_SECURITY_REVIEW_RATING_KEY, (double) Rating.E.getIndex());
-    initialMeasures.put(CoreMetrics.NEW_MAINTAINABILITY_RATING_KEY, (double) Rating.E.getIndex());
-    initialMeasures.put(CoreMetrics.NEW_RELIABILITY_RATING_KEY, (double) Rating.E.getIndex());
-
-    Map<String, Double> measures = new HashMap<>(initialMeasures);
-
-    ProjectMeasuresSoftwareQualityRatingsInitializer.initializeSoftwareQualityRatings(measures);
-
-    assertThat(measures).hasSize(initialMeasures.size() * 2)
-      .containsAllEntriesOf(initialMeasures)
-      .containsEntry(SoftwareQualitiesMetrics.SOFTWARE_QUALITY_MAINTAINABILITY_RATING_KEY, (double) Rating.D.getIndex())
-      .containsEntry(SoftwareQualitiesMetrics.SOFTWARE_QUALITY_RELIABILITY_RATING_KEY, (double) Rating.D.getIndex())
-      .containsEntry(SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_RATING_KEY, (double) Rating.D.getIndex())
-      .containsEntry(SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_REVIEW_RATING_KEY, (double) Rating.D.getIndex())
-      .containsEntry(SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_SECURITY_RATING_KEY, (double) Rating.D.getIndex())
-      .containsEntry(SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_SECURITY_REVIEW_RATING_KEY, (double) Rating.D.getIndex())
-      .containsEntry(SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_MAINTAINABILITY_RATING_KEY, (double) Rating.D.getIndex())
-      .containsEntry(SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_RELIABILITY_RATING_KEY, (double) Rating.D.getIndex());
-  }
 }
index 1d586900a1b81abf357c525345c52c81e65fad9f..ec7c91b03b204b6ee197b19c948cbfd6f331c50b 100644 (file)
@@ -33,7 +33,6 @@ import static org.sonar.server.measure.Rating.B;
 import static org.sonar.server.measure.Rating.C;
 import static org.sonar.server.measure.Rating.D;
 import static org.sonar.server.measure.Rating.E;
-import static org.sonar.server.security.SecurityReviewRating.computeAToDRating;
 import static org.sonar.server.security.SecurityReviewRating.computePercent;
 import static org.sonar.server.security.SecurityReviewRating.computeRating;
 
@@ -56,36 +55,12 @@ class SecurityReviewRatingTest {
     return res.toArray(new Object[res.size()][2]);
   }
 
-  private static Object[][] valuesForSoftwareQualityRatings() {
-    List<Object[]> res = new ArrayList<>();
-    res.add(new Object[] {100.0, A});
-    res.add(new Object[] {99.999999, A});
-    res.add(new Object[] {99.99999, B});
-    res.add(new Object[] {99.9, B});
-    res.add(new Object[] {90.0, B});
-    res.add(new Object[] {80.0, B});
-    res.add(new Object[] {75.0, B});
-    res.add(new Object[] {70.0, B});
-    res.add(new Object[] {60, C});
-    res.add(new Object[] {50.0, C});
-    res.add(new Object[] {40.0, D});
-    res.add(new Object[] {30.0, D});
-    res.add(new Object[] {29.9, D});
-    return res.toArray(new Object[res.size()][2]);
-  }
-
   @ParameterizedTest
   @MethodSource("values")
   void compute_rating(double percent, Rating expectedRating) {
     assertThat(computeRating(percent)).isEqualTo(expectedRating);
   }
 
-  @ParameterizedTest
-  @MethodSource("valuesForSoftwareQualityRatings")
-  void compute_ratingForSoftwareQuality(double percent, Rating expectedRating) {
-    assertThat(computeAToDRating(percent)).isEqualTo(expectedRating);
-  }
-
   @Test
   void compute_percent() {
     assertThat(computePercent(0, 0)).isEmpty();
index 6755fa753b919703dd800ebb6bc99a8e9df055dd..d847ef36ad7ec37ffde74de42d1eb05a1d1bd5ff 100644 (file)
@@ -57,6 +57,39 @@ expect.extend({
       expect(screen.queryByRole('tooltip')).not.toBeInTheDocument();
     });
 
+    return result;
+  },
+  async toHaveAPopoverWithContent(received: any, content: string) {
+    const user = userEvent.setup({ pointerEventsCheck: PointerEventsCheckLevel.Never });
+
+    if (!(received instanceof Element)) {
+      return {
+        pass: false,
+        message: () => `Received object is not an HTMLElement, and cannot have a tooltip`,
+      };
+    }
+
+    await user.click(received);
+
+    const popover = await screen.findByRole('dialog');
+
+    const result = popover.textContent?.includes(content)
+      ? {
+          pass: true,
+          message: () => `Tooltip content "${popover.textContent}" contains expected "${content}"`,
+        }
+      : {
+          pass: false,
+          message: () =>
+            `Tooltip content "${popover.textContent}" does not contain expected "${content}"`,
+        };
+
+    await user.keyboard('{Escape}');
+
+    await waitFor(() => {
+      expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
+    });
+
     return result;
   },
 });
index 5974a136379e8e6f65aa588971bf32f5dd0a133b..b1b111d0d47b27fc7f8111ac646c2f42cd21cb62 100644 (file)
@@ -64,12 +64,12 @@ export function ColorsLegend(props: ColorLegendProps) {
                       ? {
                           backgroundColor:
                             color.backgroundColor ??
-                            themeColor(`bubble.legacy.${(idx + 1) as BubbleColorVal}`)({
+                            themeColor(`bubble.${(idx + 1) as BubbleColorVal}`)({
                               theme,
                             }),
                           borderColor:
                             color.borderColor ??
-                            themeContrast(`bubble.legacy.${(idx + 1) as BubbleColorVal}`)({
+                            themeContrast(`bubble.${(idx + 1) as BubbleColorVal}`)({
                               theme,
                             }),
                         }
index 792eb2731232f77d77bceedf98723464d4c69277..f92d58c30a0c94c3d5f8f179e5286d1600ef3e2b 100644 (file)
@@ -89,37 +89,38 @@ export function FacetBox(props: FacetBoxProps) {
       inner={inner}
     >
       <Header>
-        <ChevronAndTitle
-          aria-controls={`${id}-panel`}
-          aria-disabled={!expandable}
-          aria-expanded={open}
-          aria-label={ariaLabel ?? name}
-          expandable={expandable}
-          id={`${id}-header`}
-          onClick={() => {
-            if (!disabled) {
-              onClick?.(!open);
-            }
-          }}
-        >
-          {expandable && <OpenCloseIndicator aria-hidden open={open} />}
-
-          {disabled ? (
-            <Tooltip content={disabledHelper}>
-              <HeaderTitle
-                aria-disabled
-                aria-label={`${name}, ${disabledHelper ?? ''}`}
-                disabled={disabled}
-              >
-                {name}
-              </HeaderTitle>
-            </Tooltip>
-          ) : (
-            <HeaderTitle>{name}</HeaderTitle>
-          )}
-
+        <TitleWithHelp>
+          <ChevronAndTitle
+            aria-controls={`${id}-panel`}
+            aria-disabled={!expandable}
+            aria-expanded={open}
+            aria-label={ariaLabel ?? name}
+            expandable={expandable}
+            id={`${id}-header`}
+            onClick={() => {
+              if (!disabled) {
+                onClick?.(!open);
+              }
+            }}
+          >
+            {expandable && <OpenCloseIndicator aria-hidden open={open} />}
+
+            {disabled ? (
+              <Tooltip content={disabledHelper}>
+                <HeaderTitle
+                  aria-disabled
+                  aria-label={`${name}, ${disabledHelper ?? ''}`}
+                  disabled={disabled}
+                >
+                  {name}
+                </HeaderTitle>
+              </Tooltip>
+            ) : (
+              <HeaderTitle>{name}</HeaderTitle>
+            )}
+          </ChevronAndTitle>
           {help && <span className="sw-ml-1">{help}</span>}
-        </ChevronAndTitle>
+        </TitleWithHelp>
 
         {<Spinner loading={loading} />}
 
@@ -173,6 +174,11 @@ const BadgeAndIcons = styled.div`
   ${tw`sw-gap-2`};
 `;
 
+const TitleWithHelp = styled.div`
+  ${tw`sw-flex`};
+  ${tw`sw-items-center`};
+`;
+
 const ChevronAndTitle = styled(BareButton)<{
   expandable?: boolean;
 }>`
index acd24669bda7b64c103706d5fede8380a26f9ede..c2eb3fb56c599f22d70ed6d891b233b84ef538f8 100644 (file)
@@ -24,20 +24,31 @@ import tw from 'twin.macro';
 import { themeColor, themeContrast } from '../helpers/theme';
 import { ThemeColors } from '../types/theme';
 
-type PillVariant = 'danger' | 'warning' | 'info' | 'accent';
+export enum PillVariant {
+  Critical = 'critical',
+  Danger = 'danger',
+  Warning = 'warning',
+  Caution = 'caution',
+  Info = 'info',
+  Accent = 'accent',
+}
 
 const variantThemeColors: Record<PillVariant, ThemeColors> = {
-  danger: 'pillDanger',
-  warning: 'pillWarning',
-  info: 'pillInfo',
-  accent: 'pillAccent',
+  [PillVariant.Critical]: 'pillCritical',
+  [PillVariant.Danger]: 'pillDanger',
+  [PillVariant.Warning]: 'pillWarning',
+  [PillVariant.Caution]: 'pillCaution',
+  [PillVariant.Info]: 'pillInfo',
+  [PillVariant.Accent]: 'pillAccent',
 };
 
 const variantThemeBorderColors: Record<PillVariant, ThemeColors> = {
-  danger: 'pillDangerBorder',
-  warning: 'pillWarningBorder',
-  info: 'pillInfoBorder',
-  accent: 'pillAccentBorder',
+  [PillVariant.Critical]: 'pillCriticalBorder',
+  [PillVariant.Danger]: 'pillDangerBorder',
+  [PillVariant.Warning]: 'pillWarningBorder',
+  [PillVariant.Caution]: 'pillCautionBorder',
+  [PillVariant.Info]: 'pillInfoBorder',
+  [PillVariant.Accent]: 'pillAccentBorder',
 };
 
 interface PillProps {
@@ -84,7 +95,7 @@ const StyledPill = styled.span<{
 
   background-color: ${({ variant }) => themeColor(variantThemeColors[variant])};
   color: ${({ variant }) => themeContrast(variantThemeColors[variant])};
-  border-style: ${({ variant }) => (variant === 'accent' ? 'hidden' : 'solid')};
+  border-style: ${({ variant }) => (variant === PillVariant.Accent ? 'hidden' : 'solid')};
   border-color: ${({ variant }) => themeColor(variantThemeBorderColors[variant])};
 `;
 
@@ -95,7 +106,7 @@ const StyledPillButton = styled.button<{
 
   background-color: ${({ variant }) => themeColor(variantThemeColors[variant])};
   color: ${({ variant }) => themeContrast(variantThemeColors[variant])};
-  border-style: ${({ variant }) => (variant === 'accent' ? 'hidden' : 'solid')};
+  border-style: ${({ variant }) => (variant === PillVariant.Accent ? 'hidden' : 'solid')};
   border-color: ${({ variant }) => themeColor(variantThemeBorderColors[variant])};
 
   cursor: pointer;
index 5c385ce0cec107d7fa66746002ef854192a6f994..014f020809eb291ac98db8b0e5b921ef82482a9c 100644 (file)
@@ -19,9 +19,9 @@
  */
 import { screen } from '@testing-library/react';
 import { render } from '../../helpers/testUtils';
-import { Pill } from '../Pill';
+import { Pill, PillVariant } from '../Pill';
 
 it('should render correctly', () => {
-  render(<Pill variant="accent">23</Pill>);
+  render(<Pill variant={PillVariant.Accent}>23</Pill>);
   expect(screen.getByText('23')).toBeInTheDocument();
 });
index 900f1f226d9777325fde376c00a975addf0b5587..741beaababd34821f5df22d5598b9ea106b68686 100644 (file)
@@ -74,7 +74,7 @@ exports[`should render HotspotRating with MEDIUM rating 1`] = `
   <circle
     cx="8"
     cy="8"
-    fill="rgb(254,205,202)"
+    fill="rgb(255,214,175)"
     r="7"
   />
   <path
diff --git a/server/sonar-web/design-system/src/components/icons/SoftwareImpactSeverityBlockerIcon.tsx b/server/sonar-web/design-system/src/components/icons/SoftwareImpactSeverityBlockerIcon.tsx
new file mode 100644 (file)
index 0000000..bf8c4b0
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+import { useTheme } from '@emotion/react';
+import { themeColor } from '../../helpers';
+import { CustomIcon, IconProps } from './Icon';
+
+export function SoftwareImpactSeverityBlockerIcon({
+  disabled,
+  ...iconProps
+}: IconProps & { disabled?: boolean }) {
+  const theme = useTheme();
+  const color = disabled
+    ? 'iconSoftwareImpactSeverityDisabled'
+    : 'iconSoftwareImpactSeverityBlocker';
+
+  return (
+    <CustomIcon viewBox="0 0 14 14" {...iconProps}>
+      <path
+        clipRule="evenodd"
+        d="M7 13.375C10.5208 13.375 13.375 10.5208 13.375 7C13.375 3.47918 10.5208 0.625 7 0.625C3.47918 0.625 0.625 3.47918 0.625 7C0.625 10.5208 3.47918 13.375 7 13.375ZM4 6C3.44772 6 3 6.44772 3 7C3 7.55228 3.44772 8 4 8H10C10.5523 8 11 7.55228 11 7C11 6.44772 10.5523 6 10 6H4Z"
+        fill={themeColor(color)({ theme })}
+        fillRule="evenodd"
+      />
+    </CustomIcon>
+  );
+}
index 52b7aa4e977d0547e030a6edc4c26eb599e29615..dd5cd03ed2e3603bd2c189a9e349dee33cb48799 100644 (file)
@@ -29,10 +29,10 @@ export function SoftwareImpactSeverityHighIcon({
   const color = disabled ? 'iconSoftwareImpactSeverityDisabled' : 'iconSoftwareImpactSeverityHigh';
 
   return (
-    <CustomIcon {...iconProps}>
+    <CustomIcon viewBox="0 0 14 14" {...iconProps}>
       <path
         clipRule="evenodd"
-        d="M7 14C10.866 14 14 10.866 14 7C14 3.13401 10.866 0 7 0C3.13401 0 0 3.13401 0 7C0 10.866 3.13401 14 7 14ZM4.14421 5.43198C4.05583 5.47727 4 5.56986 4 5.67113V9.73238C4 9.91906 4.18192 10.0483 4.35247 9.98273L6.9084 9.00033C6.96746 8.97763 7.03254 8.97763 7.0916 9.00033L9.64753 9.98273C9.81808 10.0483 10 9.91906 10 9.73238V5.67113C10 5.56986 9.94417 5.47727 9.85579 5.43198L7.11666 4.02823C7.04322 3.99059 6.95678 3.99059 6.88334 4.02823L4.14421 5.43198Z"
+        d="M7 13.375C10.5208 13.375 13.375 10.5208 13.375 7C13.375 3.47918 10.5208 0.625 7 0.625C3.47918 0.625 0.625 3.47918 0.625 7C0.625 10.5208 3.47918 13.375 7 13.375ZM4.3983 5.57213C4.31781 5.61338 4.26697 5.6977 4.26697 5.78993V9.48856C4.26697 9.65858 4.43265 9.77626 4.58796 9.71657L6.91569 8.82188C6.96948 8.80121 7.02875 8.80121 7.08253 8.82188L9.41026 9.71657C9.56557 9.77626 9.73125 9.65858 9.73125 9.48856V5.78993C9.73125 5.6977 9.68041 5.61338 9.59992 5.57213L7.10536 4.29371C7.03847 4.25944 6.95975 4.25944 6.89286 4.29371L4.3983 5.57213Z"
         fill={themeColor(color)({ theme })}
         fillRule="evenodd"
       />
diff --git a/server/sonar-web/design-system/src/components/icons/SoftwareImpactSeverityInfoIcon.tsx b/server/sonar-web/design-system/src/components/icons/SoftwareImpactSeverityInfoIcon.tsx
new file mode 100644 (file)
index 0000000..7939c63
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+import styled from '@emotion/styled';
+import { IconInfo } from '@sonarsource/echoes-react';
+import { IconProps } from './Icon';
+
+const defaultIconSize = 15;
+
+export function SoftwareImpactSeverityInfoIcon({
+  disabled,
+  ...iconProps
+}: IconProps & { disabled?: boolean }) {
+  const color = disabled ? 'echoes-color-icon-disabled' : 'echoes-color-icon-info';
+
+  return <StyledIconInfo color={color} {...iconProps} />;
+}
+
+// Info icon is the only one that is imported from echoes, so we need to adjust its size
+const StyledIconInfo = styled(IconInfo)`
+  ${(props: IconProps & { disabled?: boolean }) => {
+    let size = props.width ?? props.height;
+    size = size ? size + 1 : defaultIconSize;
+
+    return `
+    font-size: ${size}px;
+    margin-left: -0.5px;
+    `;
+  }};
+`;
index 305cee4beda0a634a500b6bba7321e4252bb2dd4..ef943283feda2a0a0896804c20c1f88fe560ed83 100644 (file)
@@ -29,10 +29,10 @@ export function SoftwareImpactSeverityLowIcon({
   const color = disabled ? 'iconSoftwareImpactSeverityDisabled' : 'iconSoftwareImpactSeverityLow';
 
   return (
-    <CustomIcon {...iconProps}>
+    <CustomIcon viewBox="0 0 14 14" {...iconProps}>
       <path
         clipRule="evenodd"
-        d="M7 14C10.866 14 14 10.866 14 7C14 3.13401 10.866 0 7 0C3.13401 0 0 3.13401 0 7C0 10.866 3.13401 14 7 14ZM3.7019 6.46256L6.46967 9.23033C6.76256 9.52322 7.23744 9.52322 7.53033 9.23033L10.2981 6.46256C10.591 6.16967 10.591 5.6948 10.2981 5.4019C10.0052 5.10901 9.53033 5.10901 9.23744 5.4019L7 7.63934L4.76256 5.4019C4.46967 5.10901 3.9948 5.10901 3.7019 5.4019C3.40901 5.6948 3.40901 6.16967 3.7019 6.46256Z"
+        d="M7 13.375C10.5208 13.375 13.375 10.5208 13.375 7C13.375 3.47918 10.5208 0.625 7 0.625C3.47918 0.625 0.625 3.47918 0.625 7C0.625 10.5208 3.47918 13.375 7 13.375ZM3.94899 6.55761L6.46964 9.07825C6.76253 9.37115 7.2374 9.37115 7.5303 9.07825L10.0509 6.55761C10.3438 6.26472 10.3438 5.78984 10.0509 5.49695C9.75805 5.20406 9.28317 5.20406 8.99028 5.49695L6.99997 7.48727L5.00965 5.49695C4.71676 5.20406 4.24188 5.20406 3.94899 5.49695C3.6561 5.78984 3.6561 6.26472 3.94899 6.55761Z"
         fill={themeColor(color)({ theme })}
         fillRule="evenodd"
       />
index 5d54e0f6e91e179aa770a60a0801dd705620849f..417ff73c76c1e37fbbe20aea34a9102fa023c19b 100644 (file)
@@ -31,10 +31,10 @@ export function SoftwareImpactSeverityMediumIcon({
     : 'iconSoftwareImpactSeverityMedium';
 
   return (
-    <CustomIcon {...iconProps}>
+    <CustomIcon viewBox="0 0 14 14" {...iconProps}>
       <path
         clipRule="evenodd"
-        d="M7 14C10.866 14 14 10.866 14 7C14 3.13401 10.866 0 7 0C3.13401 0 0 3.13401 0 7C0 10.866 3.13401 14 7 14ZM10.2981 7.96967L7.53033 5.2019C7.23744 4.90901 6.76256 4.90901 6.46967 5.2019L3.7019 7.96967C3.40901 8.26256 3.40901 8.73744 3.7019 9.03033C3.9948 9.32322 4.46967 9.32322 4.76256 9.03033L7 6.79289L9.23744 9.03033C9.53033 9.32322 10.0052 9.32322 10.2981 9.03033C10.591 8.73744 10.591 8.26256 10.2981 7.96967Z"
+        d="M7 13.375C10.5208 13.375 13.375 10.5208 13.375 7C13.375 3.47918 10.5208 0.625 7 0.625C3.47918 0.625 0.625 3.47918 0.625 7C0.625 10.5208 3.47918 13.375 7 13.375ZM10.051 7.83547L7.53033 5.31482C7.23744 5.02193 6.76256 5.02193 6.46967 5.31482L3.94903 7.83547C3.65613 8.12836 3.65613 8.60324 3.94903 8.89613C4.24192 9.18902 4.71679 9.18902 5.00969 8.89613L7 6.90581L8.99031 8.89613C9.28321 9.18902 9.75808 9.18902 10.051 8.89613C10.3439 8.60324 10.3439 8.12836 10.051 7.83547Z"
         fill={themeColor(color)({ theme })}
         fillRule="evenodd"
       />
index 1cccc42ba0c68414d6d274f90bf35648ea83ba9a..45d0a0c4ff97f0fdb8d541c19ae1a56f1875e5f0 100644 (file)
@@ -78,7 +78,9 @@ export { SeverityInfoIcon } from './SeverityInfoIcon';
 export { SeverityMajorIcon } from './SeverityMajorIcon';
 export { SeverityMinorIcon } from './SeverityMinorIcon';
 export { SnoozeCircleIcon } from './SnoozeCircleIcon';
+export { SoftwareImpactSeverityBlockerIcon } from './SoftwareImpactSeverityBlockerIcon';
 export { SoftwareImpactSeverityHighIcon } from './SoftwareImpactSeverityHighIcon';
+export { SoftwareImpactSeverityInfoIcon } from './SoftwareImpactSeverityInfoIcon';
 export { SoftwareImpactSeverityLowIcon } from './SoftwareImpactSeverityLowIcon';
 export { SoftwareImpactSeverityMediumIcon } from './SoftwareImpactSeverityMediumIcon';
 export { SortAscendIcon } from './SortAscendIcon';
index 46771d97fd7031ecbc1e9f61d15d35ffd2fe320b..da0bc81f8efdedcf84a5a74617763f66c5337bde 100644 (file)
@@ -76,7 +76,7 @@ export * from './MultiSelector';
 export * from './NavBarTabs';
 export * from './NewCodeLegend';
 export * from './OutsideClickHandler';
-export { Pill } from './Pill';
+export * from './Pill';
 export * from './popups';
 export { QualityGateIndicator } from './QualityGateIndicator';
 export * from './SearchHighlighter';
index 9276c9b286aaa2ac03a3973fa92c3d839dfee55b..2ca4a739a7ffdbc8576165e502a75c43d4ddf247 100644 (file)
@@ -26,7 +26,6 @@ import { RatingLabel } from '../types/measures';
 type sizeType = keyof typeof SIZE_MAPPING;
 interface Props extends React.AriaAttributes {
   className?: string;
-  isLegacy?: boolean;
   label?: string;
   rating?: RatingLabel;
   size?: sizeType;
@@ -41,10 +40,7 @@ const SIZE_MAPPING = {
 };
 
 export const MetricsRatingBadge = forwardRef<HTMLDivElement, Props>(
-  (
-    { className, size = 'sm', isLegacy = true, label, rating, ...ariaAttrs }: Readonly<Props>,
-    ref,
-  ) => {
+  ({ className, size = 'sm', label, rating, ...ariaAttrs }: Readonly<Props>, ref) => {
     if (!rating) {
       return (
         <StyledNoRatingBadge
@@ -62,7 +58,6 @@ export const MetricsRatingBadge = forwardRef<HTMLDivElement, Props>(
       <MetricsRatingBadgeStyled
         aria-label={label}
         className={className}
-        isLegacy={isLegacy}
         rating={rating}
         ref={ref}
         size={SIZE_MAPPING[size]}
@@ -97,7 +92,6 @@ const getFontSize = (size: string) => {
 };
 
 const MetricsRatingBadgeStyled = styled.div<{
-  isLegacy: boolean;
   rating: RatingLabel;
   size: string;
 }>`
@@ -105,8 +99,7 @@ const MetricsRatingBadgeStyled = styled.div<{
   height: ${getProp('size')};
   color: ${({ rating }) => themeContrast(`rating.${rating}`)};
   font-size: ${({ size }) => getFontSize(size)};
-  background-color: ${({ rating, isLegacy }) =>
-    themeColor(`rating.${isLegacy ? 'legacy.' : ''}${rating}`)};
+  background-color: ${({ rating }) => themeColor(`rating.${rating}`)};
   user-select: none;
 
   display: inline-flex;
index ef98fce9ac929883c97b0c0dd681613b94b8dbf5..77301732b6475840967cf16771e10ca2b6f9a9cc 100644 (file)
@@ -341,10 +341,14 @@ export const lightTheme = {
     badgeCounterFailedBorder: COLORS.red[200],
 
     // pills
+    pillCritical: COLORS.red[100],
+    pillCriticalBorder: COLORS.red[800],
     pillDanger: COLORS.red[50],
-    pillDangerBorder: COLORS.red[300],
-    pillWarning: COLORS.yellow[50],
-    pillWarningBorder: COLORS.yellow[300],
+    pillDangerBorder: COLORS.red[600],
+    pillWarning: COLORS.orange[50],
+    pillWarningBorder: COLORS.orange[300],
+    pillCaution: COLORS.yellow[50],
+    pillCautionBorder: COLORS.yellow[300],
     pillInfo: COLORS.blue[50],
     pillInfoBorder: COLORS.blue[300],
     pillAccent: COLORS.indigo[50],
@@ -401,9 +405,11 @@ export const lightTheme = {
     destructiveIconFocus: danger.default,
 
     // icons
-    iconSoftwareImpactSeverityHigh: COLORS.red[500],
-    iconSoftwareImpactSeverityMedium: COLORS.yellow[700],
-    iconSoftwareImpactSeverityLow: COLORS.blue[700],
+    iconSoftwareImpactSeverityBlocker: COLORS.red[800],
+    iconSoftwareImpactSeverityHigh: COLORS.red[600],
+    iconSoftwareImpactSeverityMedium: COLORS.orange[400],
+    iconSoftwareImpactSeverityLow: COLORS.yellow[500],
+    iconSoftwareImpactSeverityInfo: COLORS.blue[600],
     iconSoftwareImpactSeverityDisabled: COLORS.blueGrey[300],
     iconSeverityMajor: danger.light,
     iconSeverityMinor: COLORS.yellowGreen[400],
@@ -466,18 +472,11 @@ export const lightTheme = {
     // size indicators
     sizeIndicator: COLORS.blue[500],
 
-    // rating colors
-    'rating.legacy.A': COLORS.green[200],
-    'rating.legacy.B': COLORS.yellowGreen[200],
-    'rating.legacy.C': COLORS.yellow[200],
-    'rating.legacy.D': COLORS.orange[200],
-    'rating.legacy.E': COLORS.red[200],
-
     // rating colors
     'rating.A': COLORS.green[200],
     'rating.B': COLORS.yellowGreen[200],
     'rating.C': COLORS.yellow[200],
-    'rating.D': COLORS.red[200],
+    'rating.D': COLORS.orange[200],
     'rating.E': COLORS.red[200],
 
     'portfolio.rating.A.text': COLORS.green[900],
@@ -489,9 +488,9 @@ export const lightTheme = {
     'portfolio.rating.C.text': COLORS.yellow[900],
     'portfolio.rating.C.background': COLORS.yellow[100],
     'portfolio.rating.C.border': COLORS.yellow[500],
-    'portfolio.rating.D.text': COLORS.red[900],
-    'portfolio.rating.D.background': COLORS.red[100],
-    'portfolio.rating.D.border': COLORS.red[400],
+    'portfolio.rating.D.text': COLORS.orange[900],
+    'portfolio.rating.D.background': COLORS.orange[100],
+    'portfolio.rating.D.border': COLORS.orange[300],
     'portfolio.rating.E.text': COLORS.red[900],
     'portfolio.rating.E.background': COLORS.red[100],
     'portfolio.rating.E.border': COLORS.red[400],
@@ -499,25 +498,6 @@ export const lightTheme = {
     'portfolio.rating.NONE.background': COLORS.blueGrey[50],
     'portfolio.rating.NONE.border': COLORS.blueGrey[200],
 
-    'portfolio.rating.legacy.A.text': COLORS.green[900],
-    'portfolio.rating.legacy.A.background': COLORS.green[100],
-    'portfolio.rating.legacy.A.border': COLORS.green[400],
-    'portfolio.rating.legacy.B.text': COLORS.yellowGreen[900],
-    'portfolio.rating.legacy.B.background': COLORS.yellowGreen[100],
-    'portfolio.rating.legacy.B.border': COLORS.yellowGreen[400],
-    'portfolio.rating.legacy.C.text': COLORS.yellow[900],
-    'portfolio.rating.legacy.C.background': COLORS.yellow[100],
-    'portfolio.rating.legacy.C.border': COLORS.yellow[500],
-    'portfolio.rating.legacy.D.text': COLORS.orange[900],
-    'portfolio.rating.legacy.D.background': COLORS.orange[100],
-    'portfolio.rating.legacy.D.border': COLORS.orange[300],
-    'portfolio.rating.legacy.E.text': COLORS.red[900],
-    'portfolio.rating.legacy.E.background': COLORS.red[100],
-    'portfolio.rating.legacy.E.border': COLORS.red[400],
-    'portfolio.rating.legacy.NONE.text': COLORS.blueGrey[300],
-    'portfolio.rating.legacy.NONE.background': COLORS.blueGrey[50],
-    'portfolio.rating.legacy.NONE.border': COLORS.blueGrey[200],
-
     // rating donut outside circle indicators
     'ratingDonut.A': COLORS.green[400],
     'ratingDonut.B': COLORS.yellowGreen[400],
@@ -676,29 +656,18 @@ export const lightTheme = {
     // bubble charts
     bubbleChartLine: COLORS.grey[50],
     bubbleDefault: [...COLORS.blue[500], 0.3],
-    'bubble.legacy.1': [...COLORS.green[500], 0.3],
-    'bubble.legacy.2': [...COLORS.yellowGreen[500], 0.3],
-    'bubble.legacy.3': [...COLORS.yellow[500], 0.3],
-    'bubble.legacy.4': [...COLORS.orange[500], 0.3],
-    'bubble.legacy.5': [...COLORS.red[500], 0.3],
 
     'bubble.1': [...COLORS.green[500], 0.3],
     'bubble.2': [...COLORS.yellowGreen[500], 0.3],
     'bubble.3': [...COLORS.yellow[500], 0.3],
-    'bubble.4': [...COLORS.red[500], 0.3],
+    'bubble.4': [...COLORS.orange[500], 0.3],
     'bubble.5': [...COLORS.red[500], 0.3],
 
     // TreeMap Colors
-    'treeMap.legacy.A': COLORS.green[500],
-    'treeMap.legacy.B': COLORS.yellowGreen[500],
-    'treeMap.legacy.C': COLORS.yellow[500],
-    'treeMap.legacy.D': COLORS.orange[500],
-    'treeMap.legacy.E': COLORS.red[500],
-
     'treeMap.A': COLORS.green[500],
     'treeMap.B': COLORS.yellowGreen[500],
     'treeMap.C': COLORS.yellow[500],
-    'treeMap.D': COLORS.red[500],
+    'treeMap.D': COLORS.orange[500],
     'treeMap.E': COLORS.red[500],
 
     'treeMap.NA1': COLORS.blueGrey[300],
@@ -843,12 +812,11 @@ export const lightTheme = {
     badgeCounterFailed: danger.dark,
 
     // pills
-    pillDanger: COLORS.red[800],
-    pillDangerIcon: COLORS.red[700],
-    pillWarning: COLORS.yellow[800],
-    pillWarningIcon: COLORS.yellow[700],
+    pillCritical: COLORS.red[800],
+    pillDanger: COLORS.red[700],
+    pillWarning: COLORS.orange[800],
+    pillCaution: COLORS.yellow[800],
     pillInfo: COLORS.blue[800],
-    pillInfoIcon: COLORS.blue[700],
     pillAccent: COLORS.indigo[500],
 
     // project cards
@@ -945,12 +913,6 @@ export const lightTheme = {
     // page
     pageBlock: secondary.darker,
 
-    // overview software impact breakdown
-    overviewSoftwareImpactSeverityNeutral: COLORS.blueGrey[500],
-    overviewSoftwareImpactSeverityHigh: COLORS.red[700],
-    overviewSoftwareImpactSeverityMedium: COLORS.yellow[800],
-    overviewSoftwareImpactSeverityLow: COLORS.blue[800],
-
     // graph - chart
     graphZoomHandleColor: COLORS.white,
 
@@ -967,16 +929,11 @@ export const lightTheme = {
 
     // bubble charts
     bubbleDefault: COLORS.blue[500],
-    'bubble.legacy.1': COLORS.green[500],
-    'bubble.legacy.2': COLORS.yellowGreen[500],
-    'bubble.legacy.3': COLORS.yellow[500],
-    'bubble.legacy.4': COLORS.orange[500],
-    'bubble.legacy.5': COLORS.red[500],
 
     'bubble.1': COLORS.green[500],
     'bubble.2': COLORS.yellowGreen[500],
     'bubble.3': COLORS.yellow[500],
-    'bubble.4': COLORS.red[500],
+    'bubble.4': COLORS.orange[500],
     'bubble.5': COLORS.red[500],
 
     // news bar
index bdf31ea2a25aecb365f7ff7366a568c2e09d1789..af82e478ded3e2f29331655c51cd85f6a55edd54 100644 (file)
@@ -18,7 +18,7 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import { Popover } from '@sonarsource/echoes-react';
-import { Pill } from 'design-system';
+import { Pill, PillVariant } from 'design-system';
 import * as React from 'react';
 import DocumentationLink from '../../components/common/DocumentationLink';
 import { DocLink } from '../../helpers/doc-links';
@@ -49,7 +49,11 @@ export default function ChangeInCalculation({ qualifier }: Readonly<Props>) {
         </DocumentationLink>
       }
     >
-      <Pill variant="info" className="sw-ml-2" onClick={() => setIsPopoverOpen(!isPopoverOpen)}>
+      <Pill
+        variant={PillVariant.Accent}
+        className="sw-ml-2"
+        onClick={() => setIsPopoverOpen(!isPopoverOpen)}
+      >
         {translate('projects.awaiting_scan')}
       </Pill>
     </Popover>
index ce59c6e03ce3f3212a9f081cb6439844a44f39d6..f63301ef1f387d16b9b06e2bc66119ef82745ffa 100644 (file)
@@ -61,6 +61,8 @@ function isNewRatingMetric(metricKey: MetricKey) {
 const useGetMetricKeyForRating = (ratingMetric: RatingMetricKeys): MetricKey | null => {
   const { data: isLegacy, isLoading } = useIsLegacyCCTMode();
 
+  const hasSoftwareQualityRating = !!SOFTWARE_QUALITY_RATING_METRICS_MAP[ratingMetric];
+
   if (isNewRatingMetric(ratingMetric)) {
     return ratingMetric;
   }
@@ -68,7 +70,9 @@ const useGetMetricKeyForRating = (ratingMetric: RatingMetricKeys): MetricKey | n
   if (isLoading) {
     return null;
   }
-  return isLegacy ? ratingMetric : SOFTWARE_QUALITY_RATING_METRICS_MAP[ratingMetric];
+  return isLegacy || !hasSoftwareQualityRating
+    ? ratingMetric
+    : SOFTWARE_QUALITY_RATING_METRICS_MAP[ratingMetric];
 };
 
 export default function RatingComponent(props: Readonly<Props>) {
@@ -108,7 +112,6 @@ export default function RatingComponent(props: Readonly<Props>) {
   const badge = (
     <MetricsRatingBadge
       label={getLabel ? getLabel(rating) : (value ?? '—')}
-      isLegacy={measure?.metric ? !isNewRatingMetric(measure.metric as MetricKey) : false}
       rating={rating}
       size={size}
       className={className}
index bd05e186171ccc7ceb7e1aa95af02bcab27c257e..2ba88be1e1d65659c6efb61f63e815bce2652613 100644 (file)
@@ -19,7 +19,6 @@ exports[`getCodeMetrics should return the right metrics for apps 1`] = `
 exports[`getCodeMetrics should return the right metrics for portfolios 1`] = `
 [
   "releasability_rating",
-  "software_quality_releasability_rating",
   "new_security_rating",
   "new_software_quality_security_rating",
   "new_reliability_rating",
@@ -27,10 +26,8 @@ exports[`getCodeMetrics should return the right metrics for portfolios 1`] = `
   "new_maintainability_rating",
   "new_software_quality_maintainability_rating",
   "new_security_review_rating",
-  "new_software_quality_security_review_rating",
   "new_lines",
   "releasability_rating",
-  "software_quality_releasability_rating",
   "security_rating",
   "software_quality_security_rating",
   "reliability_rating",
@@ -38,7 +35,6 @@ exports[`getCodeMetrics should return the right metrics for portfolios 1`] = `
   "sqale_rating",
   "software_quality_maintainability_rating",
   "security_review_rating",
-  "software_quality_security_review_rating",
   "ncloc",
 ]
 `;
@@ -46,7 +42,6 @@ exports[`getCodeMetrics should return the right metrics for portfolios 1`] = `
 exports[`getCodeMetrics should return the right metrics for portfolios 2`] = `
 [
   "releasability_rating",
-  "software_quality_releasability_rating",
   "new_security_rating",
   "new_software_quality_security_rating",
   "new_reliability_rating",
@@ -54,10 +49,8 @@ exports[`getCodeMetrics should return the right metrics for portfolios 2`] = `
   "new_maintainability_rating",
   "new_software_quality_maintainability_rating",
   "new_security_review_rating",
-  "new_software_quality_security_review_rating",
   "new_lines",
   "releasability_rating",
-  "software_quality_releasability_rating",
   "security_rating",
   "software_quality_security_rating",
   "reliability_rating",
@@ -65,7 +58,6 @@ exports[`getCodeMetrics should return the right metrics for portfolios 2`] = `
   "sqale_rating",
   "software_quality_maintainability_rating",
   "security_review_rating",
-  "software_quality_security_review_rating",
   "ncloc",
   "alert_status",
 ]
@@ -74,7 +66,6 @@ exports[`getCodeMetrics should return the right metrics for portfolios 2`] = `
 exports[`getCodeMetrics should return the right metrics for portfolios 3`] = `
 [
   "releasability_rating",
-  "software_quality_releasability_rating",
   "new_security_rating",
   "new_software_quality_security_rating",
   "new_reliability_rating",
@@ -82,7 +73,6 @@ exports[`getCodeMetrics should return the right metrics for portfolios 3`] = `
   "new_maintainability_rating",
   "new_software_quality_maintainability_rating",
   "new_security_review_rating",
-  "new_software_quality_security_review_rating",
   "new_lines",
   "alert_status",
 ]
@@ -91,7 +81,6 @@ exports[`getCodeMetrics should return the right metrics for portfolios 3`] = `
 exports[`getCodeMetrics should return the right metrics for portfolios 4`] = `
 [
   "releasability_rating",
-  "software_quality_releasability_rating",
   "security_rating",
   "software_quality_security_rating",
   "reliability_rating",
@@ -99,7 +88,6 @@ exports[`getCodeMetrics should return the right metrics for portfolios 4`] = `
   "sqale_rating",
   "software_quality_maintainability_rating",
   "security_review_rating",
-  "software_quality_security_review_rating",
   "ncloc",
   "alert_status",
 ]
index 021f688d4011b2f7c40442d3affd21ea1f4244f5..173311aee9adf910c714ee6ba401d6445b173c8a 100644 (file)
@@ -49,6 +49,7 @@ import {
   areCCTMeasuresComputed,
   areSoftwareQualityRatingsComputed,
 } from '../../../helpers/measures';
+import { useIsLegacyCCTMode } from '../../../queries/settings';
 import { BranchLike } from '../../../types/branch-like';
 import { isApplication } from '../../../types/component';
 import { Component, ComponentMeasure, Dict, Metric } from '../../../types/types';
@@ -108,6 +109,8 @@ export default function CodeAppRenderer(props: Readonly<Props>) {
 
   const showComponentList = sourceViewer === undefined && components.length > 0 && !showSearch;
 
+  const { data: isLegacy, isLoading: isLoadingLegacy } = useIsLegacyCCTMode();
+
   const metricKeys = intersection(
     getCodeMetrics(component.qualifier, branchLike, { newCode: newCodeSelected }),
     Object.keys(metrics),
@@ -121,10 +124,10 @@ export default function CodeAppRenderer(props: Readonly<Props>) {
   );
 
   const filteredMetrics = difference(metricKeys, [
-    ...(allComponentsHaveSoftwareQualityMeasures
+    ...(allComponentsHaveSoftwareQualityMeasures && !isLegacy
       ? OLD_TAXONOMY_METRICS
       : CCT_SOFTWARE_QUALITY_METRICS),
-    ...(allComponentsHaveRatings
+    ...(allComponentsHaveRatings && !isLegacy
       ? [...OLD_TAXONOMY_RATINGS, ...LEAK_OLD_TAXONOMY_RATINGS]
       : SOFTWARE_QUALITY_RATING_METRICS),
   ]).map((key) => metrics[key]);
@@ -156,7 +159,7 @@ export default function CodeAppRenderer(props: Readonly<Props>) {
         </FlagMessage>
       )}
 
-      <Spinner isLoading={loading}>
+      <Spinner isLoading={loading || isLoadingLegacy}>
         {!allComponentsHaveSoftwareQualityMeasures && (
           <AnalysisMissingInfoMessage
             qualifier={component.qualifier}
index a7056a18b264cb99b5750e8a2832731827d68e63..caddcc41f85d5b1dce45623f78c1d833b070a9ec 100644 (file)
@@ -37,7 +37,6 @@ const APPLICATION_METRICS = [MetricKey.alert_status, ...METRICS];
 
 const PORTFOLIO_METRICS = [
   MetricKey.releasability_rating,
-  MetricKey.software_quality_releasability_rating,
   MetricKey.security_rating,
   MetricKey.software_quality_security_rating,
   MetricKey.reliability_rating,
@@ -45,13 +44,11 @@ const PORTFOLIO_METRICS = [
   MetricKey.sqale_rating,
   MetricKey.software_quality_maintainability_rating,
   MetricKey.security_review_rating,
-  MetricKey.software_quality_security_review_rating,
   MetricKey.ncloc,
 ];
 
 const NEW_PORTFOLIO_METRICS = [
   MetricKey.releasability_rating,
-  MetricKey.software_quality_releasability_rating,
   MetricKey.new_security_rating,
   MetricKey.new_software_quality_security_rating,
   MetricKey.new_reliability_rating,
@@ -59,7 +56,6 @@ const NEW_PORTFOLIO_METRICS = [
   MetricKey.new_maintainability_rating,
   MetricKey.new_software_quality_maintainability_rating,
   MetricKey.new_security_review_rating,
-  MetricKey.new_software_quality_security_review_rating,
   MetricKey.new_lines,
 ];
 
index 7e57a1be36dd76f31e8a25869110e878c1320a08..fb96c4192910768ad726846bcdd2d0adfd4a5496 100644 (file)
@@ -188,7 +188,7 @@ describe('Rules app list', () => {
 
       // Filter by severity
       await user.click(ui.severetiesFacet.get());
-      await user.click(ui.facetItem(/severity.HIGH/).get());
+      await user.click(ui.facetItem(/severity_impact.HIGH/).get());
       expect(ui.getAllRuleListItems()).toHaveLength(8);
     });
 
index 208cc1a0b984fe4cf69d4439fa55c721112e065e..cc8644c433ad922eca476a226639b120d651a9d1 100644 (file)
@@ -86,9 +86,11 @@ describe('custom rule', () => {
     await user.click(ui.cleanCodeQualityCheckbox(SoftwareQuality.Reliability).get());
 
     await user.click(ui.cleanCodeSeveritySelect(SoftwareQuality.Reliability).get());
-    await user.click(byRole('option', { name: 'severity.MEDIUM severity.MEDIUM' }).get());
+    await user.click(
+      byRole('option', { name: 'severity_impact.MEDIUM severity_impact.MEDIUM' }).get(),
+    );
 
-    expect(ui.createCustomRuleDialog.byText('severity.MEDIUM').get()).toBeInTheDocument();
+    expect(ui.createCustomRuleDialog.byText('severity_impact.MEDIUM').get()).toBeInTheDocument();
 
     await user.click(ui.statusSelect.get());
     await user.click(byRole('option', { name: 'rules.status.BETA' }).get());
index c314a1150cfdc13d454469d109bd7f764b7c422f..76f99ceeb88da4596c6a8236abbe2ed13374e80c 100644 (file)
@@ -18,9 +18,9 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import * as React from 'react';
+import Facet, { BasicProps } from '../../../components/facets/Facet';
 import { CLEAN_CODE_CATEGORIES } from '../../../helpers/constants';
 import { translate } from '../../../helpers/l10n';
-import Facet, { BasicProps } from './Facet';
 
 export default function AttributeCategoryFacet(props: BasicProps) {
   const renderName = React.useCallback(
index 94e0d1f44475a99a6c784004118a87cfbaafc4b1..5482b9a4147d7093610d92a693ab2c1a0abacec4 100644 (file)
@@ -133,7 +133,7 @@ export function SoftwareQualitiesFields(
     () =>
       IMPACT_SEVERITIES.map((severity) => ({
         value: severity,
-        label: intl.formatMessage({ id: `severity.${severity}` }),
+        label: intl.formatMessage({ id: `severity_impact.${severity}` }),
         Icon: <SoftwareImpactSeverityIcon severity={severity} />,
       })),
     [intl],
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/Facet.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/Facet.tsx
deleted file mode 100644 (file)
index 2f594df..0000000
+++ /dev/null
@@ -1,155 +0,0 @@
-/*
- * 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.
- */
-import classNames from 'classnames';
-import { FacetBox, FacetItem } from 'design-system';
-import { orderBy, sortBy, without } from 'lodash';
-import * as React from 'react';
-import { formatMeasure } from '~sonar-aligned/helpers/measures';
-import { MetricType } from '~sonar-aligned/types/metrics';
-import Tooltip from '../../../components/controls/Tooltip';
-import { translate } from '../../../helpers/l10n';
-import { Dict } from '../../../types/types';
-import { FacetItemsList } from '../../issues/sidebar/FacetItemsList';
-import { MultipleSelectionHint } from '../../issues/sidebar/MultipleSelectionHint';
-import { FacetKey } from '../query';
-
-export interface BasicProps {
-  help?: React.ReactNode;
-  onChange: (changes: Dict<string | string[] | undefined>) => void;
-  onToggle: (facet: FacetKey) => void;
-  open: boolean;
-  stats?: Dict<number>;
-  values: string[];
-}
-
-interface Props extends BasicProps {
-  disabled?: boolean;
-  disabledHelper?: string;
-  options?: string[];
-  property: FacetKey;
-  renderFooter?: () => React.ReactNode;
-  renderName?: (value: string, disabled: boolean) => React.ReactNode;
-  renderTextName?: (value: string) => string;
-  singleSelection?: boolean;
-}
-
-export default class Facet extends React.PureComponent<Props> {
-  handleItemClick = (itemValue: string, multiple: boolean) => {
-    const { values } = this.props;
-    let newValue;
-    if (this.props.singleSelection) {
-      const value = values.length ? values[0] : undefined;
-      newValue = itemValue === value ? undefined : itemValue;
-    } else if (multiple) {
-      newValue = orderBy(
-        values.includes(itemValue) ? without(values, itemValue) : [...values, itemValue],
-      );
-    } else {
-      newValue = values.includes(itemValue) && values.length < 2 ? [] : [itemValue];
-    }
-    this.props.onChange({ [this.props.property]: newValue });
-  };
-
-  handleHeaderClick = () => this.props.onToggle(this.props.property);
-
-  handleClear = () => this.props.onChange({ [this.props.property]: [] });
-
-  getStat = (value: string) => this.props.stats && this.props.stats[value];
-
-  renderItem = (value: string) => {
-    const active = this.props.values.includes(value);
-    const stat = this.getStat(value);
-    const disabled = stat === 0 || typeof stat === 'undefined';
-    const { renderName = defaultRenderName, renderTextName = defaultRenderName } = this.props;
-
-    return (
-      <FacetItem
-        className="it__search-navigator-facet"
-        active={active}
-        key={value}
-        name={renderName(value, disabled)}
-        onClick={this.handleItemClick}
-        stat={stat && formatMeasure(stat, MetricType.ShortInteger)}
-        value={value}
-        tooltip={renderTextName(value)}
-      />
-    );
-  };
-
-  render() {
-    const {
-      disabled,
-      disabledHelper,
-      open,
-      property,
-      renderTextName = defaultRenderName,
-      stats,
-      help,
-      values,
-    } = this.props;
-    const items =
-      this.props.options ||
-      (stats &&
-        sortBy(
-          Object.keys(stats),
-          (key) => -stats[key],
-          (key) => renderTextName(key).toLowerCase(),
-        ));
-    const headerId = `facet_${property}`;
-    const nbSelectableItems =
-      items?.filter((item) => (stats ? stats[item] : undefined)).length ?? 0;
-    const nbSelectedItems = values.length;
-
-    return (
-      <FacetBox
-        className={classNames('it__search-navigator-facet-box', {
-          'it__search-navigator-facet-box-forbidden': disabled,
-        })}
-        data-property={property}
-        clearIconLabel={translate('clear')}
-        count={values.length}
-        id={headerId}
-        name={translate('coding_rules.facet', property)}
-        onClear={this.handleClear}
-        onClick={disabled ? undefined : this.handleHeaderClick}
-        open={open && !disabled}
-        disabled={disabled}
-        disabledHelper={disabledHelper}
-        tooltipComponent={Tooltip}
-        help={help}
-      >
-        {open && items !== undefined && (
-          <FacetItemsList labelledby={headerId}>{items.map(this.renderItem)}</FacetItemsList>
-        )}
-
-        {open && this.props.renderFooter !== undefined && this.props.renderFooter()}
-
-        <MultipleSelectionHint
-          nbSelectableItems={nbSelectableItems}
-          nbSelectedItems={nbSelectedItems}
-        />
-      </FacetBox>
-    );
-  }
-}
-
-function defaultRenderName(value: string) {
-  return value;
-}
index e937feb48064dddf22b244b993c046d901bfe827..a1dbda02d91f3a45f38656087a8dcd72774d3189 100644 (file)
@@ -21,6 +21,7 @@ import { BasicSeparator } from 'design-system';
 import * as React from 'react';
 import { Profile } from '../../../api/quality-profiles';
 import { useAvailableFeatures } from '../../../app/components/available-features/withAvailableFeatures';
+import SeverityFacet from '../../../components/facets/SeverityFacet';
 import { translate } from '../../../helpers/l10n';
 import { Feature } from '../../../types/features';
 import { Dict } from '../../../types/types';
@@ -33,7 +34,6 @@ import InheritanceFacet from './InheritanceFacet';
 import PrioritizedRulesFacet from './PrioritizedRulesFacet';
 import ProfileFacet from './ProfileFacet';
 import RepositoryFacet from './RepositoryFacet';
-import SeverityFacet from './SeverityFacet';
 import SoftwareQualityFacet from './SoftwareQualityFacet';
 import StatusFacet from './StatusFacet';
 import TagFacet from './TagFacet';
index 49a5fb2eae4bbdf42490bffa57a9d54a55e21a5e..92080f9afefb27c098997d349d4145532bf69939 100644 (file)
@@ -18,9 +18,9 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import * as React from 'react';
+import Facet, { BasicProps } from '../../../components/facets/Facet';
 import { translate } from '../../../helpers/l10n';
 import { RuleInheritance } from '../../../types/types';
-import Facet, { BasicProps } from './Facet';
 
 interface Props extends Omit<BasicProps, 'values'> {
   disabled: boolean;
index 66f1cc3b697cecf76281b4eaad28413e8469fc4a..f739939fd8993c517dae5883db7e3bb54e8dd39a 100644 (file)
@@ -18,8 +18,8 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import * as React from 'react';
+import Facet, { BasicProps } from '../../../components/facets/Facet';
 import { translate } from '../../../helpers/l10n';
-import Facet, { BasicProps } from './Facet';
 
 interface Props extends Omit<BasicProps, 'onChange' | 'values'> {
   disabled: boolean;
index f53ba7c4ae4f6e5db899105b27d69a257757bcd2..997f2f4bed153cd68ffd6cda495bfad3a655ce83 100644 (file)
@@ -21,12 +21,12 @@ import { Note } from 'design-system';
 import * as React from 'react';
 import { getRuleRepositories } from '../../../api/rules';
 import withLanguagesContext from '../../../app/components/languages/withLanguagesContext';
+import { BasicProps } from '../../../components/facets/Facet';
 import { translate } from '../../../helpers/l10n';
 import { highlightTerm } from '../../../helpers/search';
 import { Languages } from '../../../types/languages';
 import { Dict } from '../../../types/types';
 import { ListStyleFacet } from '../../issues/sidebar/ListStyleFacet';
-import { BasicProps } from './Facet';
 
 interface StateProps {
   languages: Languages;
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/SeverityFacet.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/SeverityFacet.tsx
deleted file mode 100644 (file)
index 6acfbcc..0000000
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * 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.
- */
-
-import { HelperHintIcon } from 'design-system';
-import * as React from 'react';
-import DocHelpTooltip from '~sonar-aligned/components/controls/DocHelpTooltip';
-import SoftwareImpactSeverityIcon from '../../../components/icon-mappers/SoftwareImpactSeverityIcon';
-import { IMPACT_SEVERITIES } from '../../../helpers/constants';
-import { DocLink } from '../../../helpers/doc-links';
-import { translate } from '../../../helpers/l10n';
-import Facet, { BasicProps } from './Facet';
-
-export default function SeverityFacet(props: BasicProps) {
-  const renderName = React.useCallback(
-    (severity: string, disabled: boolean) => (
-      <div className="sw-flex sw-items-center">
-        <SoftwareImpactSeverityIcon severity={severity} disabled={disabled} />
-        <span className="sw-ml-1">{translate('severity', severity)}</span>
-      </div>
-    ),
-    [],
-  );
-
-  const renderTextName = React.useCallback(
-    (severity: string) => translate('severity', severity),
-    [],
-  );
-
-  return (
-    <Facet
-      {...props}
-      options={IMPACT_SEVERITIES}
-      property="impactSeverities"
-      renderName={renderName}
-      renderTextName={renderTextName}
-      help={
-        <DocHelpTooltip
-          placement="right"
-          content={
-            <>
-              <p>{translate('issues.facet.impactSeverities.help.line1')}</p>
-              <p className="sw-mt-2">{translate('issues.facet.impactSeverities.help.line2')}</p>
-            </>
-          }
-          links={[
-            {
-              href: DocLink.CleanCodeIntroduction,
-              label: translate('learn_more'),
-            },
-          ]}
-        >
-          <HelperHintIcon />
-        </DocHelpTooltip>
-      }
-    />
-  );
-}
index 1097682a5aa82b466eecf169cacfb3482c8b61f0..26151da5286c5401d31a1bc3e32e18ab77bed185 100644 (file)
@@ -18,9 +18,9 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import * as React from 'react';
+import Facet, { BasicProps } from '../../../components/facets/Facet';
 import { SOFTWARE_QUALITIES } from '../../../helpers/constants';
 import { translate } from '../../../helpers/l10n';
-import Facet, { BasicProps } from './Facet';
 
 export default function SoftwareQualityFacet(props: BasicProps) {
   const renderName = React.useCallback(
index 8fcd7092240626f30e132a53d29af1b68b99bf2c..e7d03dc66d26411bbec7532514315da22925f388 100644 (file)
@@ -18,9 +18,9 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import * as React from 'react';
+import Facet, { BasicProps } from '../../../components/facets/Facet';
 import { RULE_STATUSES } from '../../../helpers/constants';
 import { translate } from '../../../helpers/l10n';
-import Facet, { BasicProps } from './Facet';
 
 export default class StatusFacet extends React.PureComponent<BasicProps> {
   renderName = (status: string) => translate('rules.status', status.toLowerCase());
index 78c3fd4a660be65a46ff5919dc1f7c83aa29c2b3..ae3b0b11a131196c2da1d346c56c60aa0441f562 100644 (file)
 import { uniq } from 'lodash';
 import * as React from 'react';
 import { getRuleTags } from '../../../api/rules';
+import { BasicProps } from '../../../components/facets/Facet';
 import { translate } from '../../../helpers/l10n';
 import { highlightTerm } from '../../../helpers/search';
 import { ListStyleFacet } from '../../issues/sidebar/ListStyleFacet';
-import { BasicProps } from './Facet';
 
 export default class TagFacet extends React.PureComponent<BasicProps> {
   handleSearch = (query: string) => {
index 87fdc3d87cf31f9362ec841003d9d3dfcccfb1ef..3d49c68bcb9de548410c2faecd0ebf92e2b3fa5a 100644 (file)
@@ -20,8 +20,8 @@
 import { HelperHintIcon } from 'design-system';
 import * as React from 'react';
 import HelpTooltip from '~sonar-aligned/components/controls/HelpTooltip';
+import Facet, { BasicProps } from '../../../components/facets/Facet';
 import { translate } from '../../../helpers/l10n';
-import Facet, { BasicProps } from './Facet';
 
 interface Props extends Omit<BasicProps, 'onChange' | 'values'> {
   onChange: (changes: { template: boolean | undefined }) => void;
index c92a3c11697557c959802a90618e9824a0122a6f..09ba4b0635dffe4c75c19ccab69bf06fe702de50 100644 (file)
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import * as React from 'react';
+import Facet, { BasicProps } from '../../../components/facets/Facet';
 import IssueTypeIcon from '../../../components/icon-mappers/IssueTypeIcon';
 import { RULE_TYPES } from '../../../helpers/constants';
 import { translate } from '../../../helpers/l10n';
-import Facet, { BasicProps } from './Facet';
 
 export default class TypeFacet extends React.PureComponent<BasicProps> {
   renderName = (type: string) => (
index 95165e1448a7c6f44e31a07f08ab17df437a746b..332801e1d3288ef40d352e2e77c8e5f7c53708be 100644 (file)
@@ -36,7 +36,6 @@ import { isDefined } from '../../../helpers/types';
 import { getProjectUrl } from '../../../helpers/urls';
 import { useCurrentBranchQuery } from '../../../queries/branch';
 import { useComponentTreeQuery, useMeasuresComponentQuery } from '../../../queries/measures';
-import { useIsLegacyCCTMode } from '../../../queries/settings';
 import { useLocation, useRouter } from '../../../sonar-aligned/components/hoc/withRouter';
 import { BranchLike } from '../../../types/branch-like';
 import { isApplication, isFile, isView } from '../../../types/component';
@@ -71,7 +70,6 @@ export default function MeasureContent(props: Readonly<Props>) {
   const { data: branchLike } = useCurrentBranchQuery(rootComponent);
   const router = useRouter();
   const query = parseQuery(rawQuery);
-  const { data: isLegacy } = useIsLegacyCCTMode();
   const { selected, asc, view } = query;
 
   const containerRef = React.useRef<HTMLDivElement>(null);
@@ -209,14 +207,7 @@ export default function MeasureContent(props: Readonly<Props>) {
       );
     }
 
-    return (
-      <TreeMapView
-        isLegacyMode={Boolean(isLegacy)}
-        components={components}
-        handleSelect={onOpenComponent}
-        metric={metric}
-      />
-    );
+    return <TreeMapView components={components} handleSelect={onOpenComponent} metric={metric} />;
   };
 
   return (
index 86cdf3e2be9886185e4cdeaf0bcafd19480c0094..67f507873ab0c4ee9af005f382acff60d2d4395d 100644 (file)
@@ -32,7 +32,6 @@ import * as React from 'react';
 import HelpTooltip from '~sonar-aligned/components/controls/HelpTooltip';
 import { formatMeasure } from '~sonar-aligned/helpers/measures';
 import { MetricKey } from '~sonar-aligned/types/metrics';
-import { SOFTWARE_QUALITY_RATING_METRICS } from '../../../helpers/constants';
 import {
   getLocalizedMetricDomain,
   getLocalizedMetricName,
@@ -42,7 +41,6 @@ import {
 import { getCCTMeasureValue, isDiffMetric } from '../../../helpers/measures';
 import { isDefined } from '../../../helpers/types';
 import { getComponentDrilldownUrl } from '../../../helpers/urls';
-import { useIsLegacyCCTMode } from '../../../queries/settings';
 import { BranchLike } from '../../../types/branch-like';
 import { isProject, isView } from '../../../types/component';
 import {
@@ -88,7 +86,6 @@ export default function BubbleChartView(props: Readonly<Props>) {
     bubblesByDomain,
   } = props;
   const theme = useTheme();
-  const { data: isLegacy } = useIsLegacyCCTMode();
   const bubbleMetrics = getBubbleMetrics(bubblesByDomain, domain, metrics);
   const [ratingFilters, setRatingFilters] = React.useState<{ [rating: number]: boolean }>({});
 
@@ -116,8 +113,7 @@ export default function BubbleChartView(props: Readonly<Props>) {
           return undefined;
         }
 
-        const bubbleColor =
-          `bubble.${isLegacy ? 'legacy.' : ''}${(colorRating ?? 1) as BubbleColorVal}` as const;
+        const bubbleColor = `bubble.${(colorRating ?? 1) as BubbleColorVal}` as const;
 
         return {
           x,
@@ -220,12 +216,6 @@ export default function BubbleChartView(props: Readonly<Props>) {
           </div>
           {bubbleMetrics.colors && (
             <ColorRatingsLegend
-              isLegacy={
-                isLegacy ||
-                bubbleMetrics.colors.every(
-                  (m) => !SOFTWARE_QUALITY_RATING_METRICS.includes(m.key as MetricKey),
-                )
-              }
               className="sw-mt-2"
               filters={ratingFilters}
               onRatingClick={handleRatingFilterClick}
index 80acadf8b66ccc22da43266b102d78a4c409c8b6..b765f2e4a250ce63e990134fb64ebb85ea764497 100644 (file)
@@ -33,14 +33,13 @@ import { translateWithParameters } from '../../../helpers/l10n';
 export interface ColorRatingsLegendProps {
   className?: string;
   filters: { [rating: number]: boolean };
-  isLegacy?: boolean;
   onRatingClick: (selection: number) => void;
 }
 
 export default function ColorRatingsLegend(props: ColorRatingsLegendProps) {
-  const { className, filters, isLegacy } = props;
+  const { className, filters } = props;
   const theme = useTheme();
-  const RATINGS = isLegacy ? [1, 2, 3, 4, 5] : [1, 2, 3, 4];
+  const RATINGS = [1, 2, 3, 4, 5];
 
   const ratingsColors = RATINGS.map((rating: BubbleColorVal) => {
     const formattedMeasure = formatMeasure(rating, MetricType.Rating);
@@ -50,10 +49,10 @@ export default function ColorRatingsLegend(props: ColorRatingsLegendProps) {
       label: formattedMeasure,
       value: rating,
       selected: !filters[rating],
-      backgroundColor: themeColor(isLegacy ? `bubble.legacy.${rating}` : `bubble.${rating}`)({
+      backgroundColor: themeColor(`bubble.${rating}`)({
         theme,
       }),
-      borderColor: themeContrast(isLegacy ? `bubble.legacy.${rating}` : `bubble.${rating}`)({
+      borderColor: themeContrast(`bubble.${rating}`)({
         theme,
       }),
     };
index 49cf36bb1f1a32d5d5fc1eeeeeea77e9afad0382..27b3dd2832e2b13322692a8ce16e6ed371e7173e 100644 (file)
@@ -46,7 +46,6 @@ import EmptyResult from './EmptyResult';
 interface TreeMapViewProps {
   components: ComponentMeasureEnhanced[];
   handleSelect: (component: ComponentMeasureIntern) => void;
-  isLegacyMode: boolean;
   metric: Metric;
 }
 
@@ -57,8 +56,7 @@ interface State {
 }
 
 const PERCENT_SCALE_DOMAIN = [0, 25, 50, 75, 100];
-const RATING_SCALE_DOMAIN = [1, 2, 3, 4];
-const LEGACY_RATING_SCALE_DOMAIN = [1, 2, 3, 4, 5];
+const RATING_SCALE_DOMAIN = [1, 2, 3, 4, 5];
 
 const HEIGHT = 500;
 const NA_COLORS: [ThemeColors, ThemeColors] = ['treeMap.NA1', 'treeMap.NA2'];
@@ -69,13 +67,6 @@ const TREEMAP_COLORS: ThemeColors[] = [
   'treeMap.D',
   'treeMap.E',
 ];
-const TREEMAP_LEGACY_COLORS: ThemeColors[] = [
-  'treeMap.legacy.A',
-  'treeMap.legacy.B',
-  'treeMap.legacy.C',
-  'treeMap.legacy.D',
-  'treeMap.legacy.E',
-];
 
 export class TreeMapView extends React.PureComponent<Props, State> {
   state: State;
@@ -149,10 +140,8 @@ export class TreeMapView extends React.PureComponent<Props, State> {
   };
 
   getMappedThemeColors = (): string[] => {
-    const { theme, isLegacyMode } = this.props;
-    return (isLegacyMode ? TREEMAP_LEGACY_COLORS : TREEMAP_COLORS).map((c) =>
-      themeColor(c)({ theme }),
-    );
+    const { theme } = this.props;
+    return TREEMAP_COLORS.map((c) => themeColor(c)({ theme }));
   };
 
   getLevelColorScale = () =>
@@ -171,9 +160,8 @@ export class TreeMapView extends React.PureComponent<Props, State> {
   };
 
   getRatingColorScale = () => {
-    const { isLegacyMode } = this.props;
     return scaleLinear<string, string>()
-      .domain(isLegacyMode ? LEGACY_RATING_SCALE_DOMAIN : RATING_SCALE_DOMAIN)
+      .domain(RATING_SCALE_DOMAIN)
       .range(this.getMappedThemeColors());
   };
 
index 5424ba9d4819464e8f65e6cdd5edeb9632169831..e47f720209e2e2b767eb59174dfb11e98189b344 100644 (file)
@@ -240,11 +240,7 @@ export function banQualityGateMeasure({ measures = [], qualifier }: ComponentMea
     bannedMetrics.push(MetricKey.alert_status);
   }
   if (qualifier === ComponentQualifier.Application) {
-    bannedMetrics.push(
-      MetricKey.releasability_rating,
-      MetricKey.releasability_effort,
-      MetricKey.software_quality_releasability_rating,
-    );
+    bannedMetrics.push(MetricKey.releasability_rating, MetricKey.releasability_effort);
   }
   return measures.filter((measure) => !bannedMetrics.includes(measure.metric));
 }
index b1edb27376bd58942035566d368e70e4ecfd501f..f249c8f48e47f6915bb2c429a3906cff6a600f40 100644 (file)
@@ -52,14 +52,14 @@ it('renders correctly', async () => {
     `issue.clean_code_attribute_category.${issue.cleanCodeAttributeCategory}`,
   ).get();
   expect(cctBadge).toBeInTheDocument();
-  await expect(cctBadge).toHaveATooltipWithContent(
+  await expect(cctBadge).toHaveAPopoverWithContent(
     `issue.clean_code_attribute.${issue.cleanCodeAttribute}`,
   );
 
   // Software Qualities
   const qualityBadge = byText(`software_quality.${issue.impacts[0].softwareQuality}`).get();
   expect(qualityBadge).toBeInTheDocument();
-  await expect(qualityBadge).toHaveATooltipWithContent('software_quality');
+  await expect(qualityBadge).toHaveAPopoverWithContent('software_quality');
 
   // Deprecated type
   const type = byText(`issue.type.${issue.type}`).get();
diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/SeverityFacet.tsx b/server/sonar-web/src/main/js/apps/issues/sidebar/SeverityFacet.tsx
deleted file mode 100644 (file)
index d4be7b7..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * 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.
- */
-
-import { HelperHintIcon } from 'design-system';
-import * as React from 'react';
-import DocHelpTooltip from '~sonar-aligned/components/controls/DocHelpTooltip';
-import SoftwareImpactSeverityIcon from '../../../components/icon-mappers/SoftwareImpactSeverityIcon';
-import { IMPACT_SEVERITIES } from '../../../helpers/constants';
-import { DocLink } from '../../../helpers/doc-links';
-import { translate } from '../../../helpers/l10n';
-import { SoftwareImpactSeverity } from '../../../types/clean-code-taxonomy';
-import { CommonProps, SimpleListStyleFacet } from './SimpleListStyleFacet';
-
-interface Props extends CommonProps {
-  severities: SoftwareImpactSeverity[];
-}
-
-export function SeverityFacet(props: Props) {
-  const { severities = [], ...rest } = props;
-
-  return (
-    <SimpleListStyleFacet
-      property="impactSeverities"
-      itemNamePrefix="severity"
-      listItems={IMPACT_SEVERITIES}
-      selectedItems={severities}
-      renderIcon={(severity: string, disabled: boolean) => (
-        <SoftwareImpactSeverityIcon severity={severity} disabled={disabled} />
-      )}
-      help={
-        <DocHelpTooltip
-          placement="right"
-          content={
-            <>
-              <p>{translate('issues.facet.impactSeverities.help.line1')}</p>
-              <p className="sw-mt-2">{translate('issues.facet.impactSeverities.help.line2')}</p>
-            </>
-          }
-          links={[
-            {
-              href: DocLink.CleanCodeIntroduction,
-              label: translate('learn_more'),
-            },
-          ]}
-        >
-          <HelperHintIcon />
-        </DocHelpTooltip>
-      }
-      {...rest}
-    />
-  );
-}
index 749603a72d74858548a77b9b91d9a50b1f45a717..1f643e26f92da908615561efe92ffc27a1c4f096 100644 (file)
@@ -25,6 +25,7 @@ import { isPortfolioLike } from '~sonar-aligned/helpers/component';
 import { ComponentQualifier } from '~sonar-aligned/types/component';
 import { useAppState } from '../../../app/components/app-state/withAppStateContext';
 import { useAvailableFeatures } from '../../../app/components/available-features/withAvailableFeatures';
+import SeverityFacet from '../../../components/facets/SeverityFacet';
 import { translate } from '../../../helpers/l10n';
 import { BranchLike } from '../../../types/branch-like';
 import { isApplication, isProject, isView } from '../../../types/component';
@@ -52,7 +53,6 @@ import { PrioritizedRuleFacet } from './PrioritizedRuleFacet';
 import { ProjectFacet } from './ProjectFacet';
 import { RuleFacet } from './RuleFacet';
 import { ScopeFacet } from './ScopeFacet';
-import { SeverityFacet } from './SeverityFacet';
 import { SoftwareQualityFacet } from './SoftwareQualityFacet';
 import { StandardFacet } from './StandardFacet';
 import { TagFacet } from './TagFacet';
@@ -204,8 +204,8 @@ export function Sidebar(props: Readonly<Props>) {
             onChange={props.onFilterChange}
             onToggle={props.onFacetToggle}
             open={!!openFacets.impactSeverities}
-            severities={query.impactSeverities}
             stats={facets.impactSeverities}
+            values={query.impactSeverities}
           />
 
           <BasicSeparator className="sw-my-4" />
index abd6403a955471aea716cfc6cdf220bfa484de21..e878ce08907917f70ad98a537311d979a1f4de7b 100644 (file)
@@ -48,7 +48,9 @@ it('should render correct facets for Projects with PrioritizedRules feature', ()
   expect(screen.getAllByRole('button').map((button) => button.textContent)).toStrictEqual([
     'issues.facet.cleanCodeAttributeCategories',
     'issues.facet.impactSoftwareQualities',
-    'issues.facet.impactSeverities',
+    'coding_rules.facet.impactSeverities',
+    // help icon
+    '',
     'issues.facet.types',
     'issues.facet.scopes',
     'issues.facet.issueStatuses',
@@ -72,7 +74,9 @@ it('should render correct facets for Application', () => {
   expect(screen.getAllByRole('button').map((button) => button.textContent)).toStrictEqual([
     'issues.facet.cleanCodeAttributeCategories',
     'issues.facet.impactSoftwareQualities',
-    'issues.facet.impactSeverities',
+    'coding_rules.facet.impactSeverities',
+    // help icon
+    '',
     'issues.facet.types',
     'issues.facet.scopes',
     'issues.facet.issueStatuses',
@@ -94,7 +98,9 @@ it('should render correct facets for Portfolio', () => {
   expect(screen.getAllByRole('button').map((button) => button.textContent)).toStrictEqual([
     'issues.facet.cleanCodeAttributeCategories',
     'issues.facet.impactSoftwareQualities',
-    'issues.facet.impactSeverities',
+    'coding_rules.facet.impactSeverities',
+    // help icon
+    '',
     'issues.facet.types',
     'issues.facet.scopes',
     'issues.facet.issueStatuses',
@@ -116,7 +122,9 @@ it('should render correct facets for SubPortfolio', () => {
   expect(screen.getAllByRole('button').map((button) => button.textContent)).toStrictEqual([
     'issues.facet.cleanCodeAttributeCategories',
     'issues.facet.impactSoftwareQualities',
-    'issues.facet.impactSeverities',
+    'coding_rules.facet.impactSeverities',
+    // help icon
+    '',
     'issues.facet.types',
     'issues.facet.scopes',
     'issues.facet.issueStatuses',
index 3918108f48b342d2844c147d34475208781f0009..aca69041755a2ab3f6a7a59fea569322161ac4e5 100644 (file)
@@ -107,7 +107,7 @@ export const ui = {
   softwareQualityFacet: byRole('button', {
     name: 'issues.facet.impactSoftwareQualities',
   }),
-  severityFacet: byRole('button', { name: 'issues.facet.impactSeverities' }),
+  severityFacet: byRole('button', { name: 'coding_rules.facet.impactSeverities' }),
   prioritizedRuleFacet: byRole('button', { name: 'issues.facet.prioritized_rule.category' }),
 
   clearCodeCategoryFacet: byTestId('clear-issues.facet.cleanCodeAttributeCategories'),
@@ -121,7 +121,7 @@ export const ui = {
   clearResolutionFacet: byTestId('clear-issues.facet.resolutions'),
   clearRuleFacet: byTestId('clear-issues.facet.rules'),
   clearScopeFacet: byTestId('clear-issues.facet.scopes'),
-  clearSeverityFacet: byTestId('clear-issues.facet.impactSeverities'),
+  clearSeverityFacet: byTestId('clear-coding_rules.facet.impactSeverities'),
   clearIssueStatusFacet: byTestId('clear-issues.facet.issueStatuses'),
   clearTagFacet: byTestId('clear-issues.facet.tags'),
   clearPrioritizedRuleFacet: byTestId('clear-issues.facet.prioritized_rule.category'),
@@ -139,7 +139,9 @@ export const ui = {
   confirmedStatusFilter: byRole('checkbox', { name: 'issue.issue_status.CONFIRMED' }),
   fixedResolutionFilter: byRole('checkbox', { name: 'issue.resolution.FIXED' }),
   mainScopeFilter: byRole('checkbox', { name: 'issue.scope.MAIN' }),
-  mediumSeverityFilter: byRole('checkbox', { name: `severity.${SoftwareImpactSeverity.Medium}` }),
+  mediumSeverityFilter: byRole('checkbox', {
+    name: `severity_impact.${SoftwareImpactSeverity.Medium}`,
+  }),
   openStatusFilter: byRole('checkbox', { name: 'issue.issue_status.OPEN' }),
   vulnerabilityIssueTypeFilter: byRole('checkbox', { name: 'issue.type.VULNERABILITY' }),
   prioritizedRuleFilter: byRole('checkbox', { name: 'issues.facet.prioritized_rule' }),
index dcf7161a7004a1ae718d2076424901c6a46169a4..12ba47636295ebf4dca7e45920fa34881adc516d 100644 (file)
@@ -197,6 +197,12 @@ export default function OverallCodeMeasuresPanel(props: Readonly<OverallCodeMeas
               <RatingComponent
                 branchLike={branch}
                 componentKey={component.key}
+                getTooltip={(rating) =>
+                  intl.formatMessage({ id: `metric.security_review_rating.tooltip.${rating}` })
+                }
+                getLabel={(rating) =>
+                  intl.formatMessage({ id: 'metric.has_rating_X' }, { 0: rating })
+                }
                 ratingMetric={MetricKey.security_review_rating}
                 size="md"
               />
diff --git a/server/sonar-web/src/main/js/apps/overview/branches/SoftwareImpactMeasureBreakdownCard.tsx b/server/sonar-web/src/main/js/apps/overview/branches/SoftwareImpactMeasureBreakdownCard.tsx
deleted file mode 100644 (file)
index 463c20e..0000000
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * 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.
- */
-import styled from '@emotion/styled';
-import { Tooltip } from '@sonarsource/echoes-react';
-import classNames from 'classnames';
-import { DiscreetLinkBox, themeColor, themeContrast } from 'design-system';
-import * as React from 'react';
-import { useIntl } from 'react-intl';
-import { formatMeasure } from '~sonar-aligned/helpers/measures';
-import { getComponentIssuesUrl } from '~sonar-aligned/helpers/urls';
-import { MetricType } from '~sonar-aligned/types/metrics';
-import { DEFAULT_ISSUES_QUERY } from '../../../components/shared/utils';
-import { Branch } from '../../../types/branch-like';
-import { SoftwareImpactSeverity, SoftwareQuality } from '../../../types/clean-code-taxonomy';
-import { Component } from '../../../types/types';
-
-export interface SoftwareImpactMeasureBreakdownCardProps {
-  active?: boolean;
-  branch?: Branch;
-  component: Component;
-  severity: SoftwareImpactSeverity;
-  softwareQuality: SoftwareQuality;
-  value?: string;
-}
-
-export function SoftwareImpactMeasureBreakdownCard(
-  props: Readonly<SoftwareImpactMeasureBreakdownCardProps>,
-) {
-  const { softwareQuality, component, value, severity, active, branch } = props;
-
-  const intl = useIntl();
-
-  const url = getComponentIssuesUrl(component.key, {
-    ...DEFAULT_ISSUES_QUERY,
-    impactSoftwareQualities: softwareQuality,
-    impactSeverities: severity,
-    branch: branch?.name,
-  });
-
-  const testId = `overview__software-impact-${softwareQuality}-severity-${severity}`;
-  const cardClasses =
-    'sw-w-1/3 sw-px-2 sw-py-1 sw-rounded-1 sw-text-xs sw-font-semibold sw-select-none sw-flex sw-gap-1 sw-justify-center sw-items-center';
-
-  if (!value) {
-    return (
-      <StyledBreakdownCard
-        data-testid={testId}
-        className={classNames(cardClasses, severity, {
-          active,
-        })}
-      >
-        -
-      </StyledBreakdownCard>
-    );
-  }
-
-  return (
-    <Tooltip
-      content={intl.formatMessage({
-        id: `overview.measures.software_impact.severity.${severity}.tooltip`,
-      })}
-    >
-      <StyledBreakdownLinkCard
-        data-testid={testId}
-        className={classNames(cardClasses, severity, {
-          active,
-        })}
-        aria-label={intl.formatMessage(
-          {
-            id: 'overview.measures.software_impact.severity.see_x_open_issues',
-          },
-          {
-            count: formatMeasure(value, MetricType.ShortInteger),
-            softwareQuality: intl.formatMessage({
-              id: `software_quality.${softwareQuality}`,
-            }),
-            severity: intl.formatMessage({
-              id: `overview.measures.software_impact.severity.${severity}.tooltip`,
-            }),
-          },
-        )}
-        disabled={component.needIssueSync}
-        to={url}
-      >
-        <span>{formatMeasure(value, MetricType.ShortInteger)}</span>
-        <span>
-          {intl.formatMessage({
-            id: `overview.measures.software_impact.severity.${severity}`,
-          })}
-        </span>
-      </StyledBreakdownLinkCard>
-    </Tooltip>
-  );
-}
-
-const StyledBreakdownCard = styled.div`
-  background-color: ${themeColor('overviewSoftwareImpactSeverityNeutral')};
-
-  &.active.HIGH {
-    background-color: ${themeColor('overviewSoftwareImpactSeverityHigh')};
-    color: ${themeContrast('overviewSoftwareImpactSeverityHigh')};
-  }
-  &.active.MEDIUM {
-    background-color: ${themeColor('overviewSoftwareImpactSeverityMedium')};
-    color: ${themeContrast('overviewSoftwareImpactSeverityMedium')};
-  }
-  &.active.LOW {
-    background-color: ${themeColor('overviewSoftwareImpactSeverityLow')};
-    color: ${themeContrast('overviewSoftwareImpactSeverityLow')};
-  }
-`;
-const StyledBreakdownLinkCard = StyledBreakdownCard.withComponent(DiscreetLinkBox);
-
-export default SoftwareImpactMeasureBreakdownCard;
index 37c5e3248a807e1188eddbb80adaac9887da57f6..96cd155eecab5eaca5432a521816c1b9573ec26e 100644 (file)
@@ -33,15 +33,10 @@ import {
 import { isDefined } from '../../../helpers/types';
 import { useIsLegacyCCTMode } from '../../../queries/settings';
 import { Branch } from '../../../types/branch-like';
-import {
-  SoftwareImpactMeasureData,
-  SoftwareImpactSeverity,
-  SoftwareQuality,
-} from '../../../types/clean-code-taxonomy';
+import { SoftwareImpactMeasureData, SoftwareQuality } from '../../../types/clean-code-taxonomy';
 import { QualityGateStatusConditionEnhanced } from '../../../types/quality-gates';
 import { Component, MeasureEnhanced } from '../../../types/types';
 import { Status, softwareQualityToMeasure } from '../utils';
-import SoftwareImpactMeasureBreakdownCard from './SoftwareImpactMeasureBreakdownCard';
 import SoftwareImpactMeasureRating from './SoftwareImpactMeasureRating';
 
 export interface SoftwareImpactBreakdownCardProps {
@@ -79,13 +74,6 @@ export function SoftwareImpactMeasureCard(props: Readonly<SoftwareImpactBreakdow
     branch: branch?.name,
   });
 
-  // We highlight the highest severity breakdown card with non-zero count
-  const highlightedSeverity =
-    measure &&
-    [SoftwareImpactSeverity.High, SoftwareImpactSeverity.Medium, SoftwareImpactSeverity.Low].find(
-      (severity) => measure[severity] > 0,
-    );
-
   const countTooltipOverlay = intl.formatMessage({
     id: 'overview.measures.software_impact.count_tooltip',
   });
@@ -149,25 +137,6 @@ export function SoftwareImpactMeasureCard(props: Readonly<SoftwareImpactBreakdow
             />
           </div>
         </div>
-        {measure && (
-          <div className="sw-flex sw-gap-2">
-            {[
-              SoftwareImpactSeverity.High,
-              SoftwareImpactSeverity.Medium,
-              SoftwareImpactSeverity.Low,
-            ].map((severity) => (
-              <SoftwareImpactMeasureBreakdownCard
-                branch={branch}
-                key={severity}
-                component={component}
-                softwareQuality={softwareQuality}
-                value={measure?.[severity]?.toString()}
-                severity={severity}
-                active={highlightedSeverity === severity}
-              />
-            ))}
-          </div>
-        )}
       </div>
     </div>
   );
index 4a99bdfa447daf06a461d9e0ed804969b9a59e62..0bc93912b3750d03a2d5199091c30dfecb1703b7 100644 (file)
@@ -44,7 +44,7 @@ import { mockQualityGateProjectStatus } from '../../../../helpers/mocks/quality-
 import { mockLoggedInUser, mockMeasure, mockPaging } from '../../../../helpers/testMocks';
 import { renderComponent } from '../../../../helpers/testReactTestingUtils';
 import { ComponentPropsType } from '../../../../helpers/testUtils';
-import { SoftwareImpactSeverity, SoftwareQuality } from '../../../../types/clean-code-taxonomy';
+import { SoftwareQuality } from '../../../../types/clean-code-taxonomy';
 import { ProjectAnalysisEventCategory } from '../../../../types/project-activity';
 import { SettingsKey } from '../../../../types/settings';
 import { CaycStatus } from '../../../../types/types';
@@ -299,53 +299,21 @@ describe('project overview', () => {
 
     await user.click(await ui.overallCodeButton.find());
 
-    ui.expectSoftwareImpactMeasureCard(
-      SoftwareQuality.Security,
-      'B',
-      {
-        total: 1,
-        [SoftwareImpactSeverity.High]: 0,
-        [SoftwareImpactSeverity.Medium]: 1,
-        [SoftwareImpactSeverity.Low]: 0,
-      },
-      [false, true, false],
-    );
+    ui.expectSoftwareImpactMeasureCard(SoftwareQuality.Security, 'B', 1);
     await ui.expectSoftwareImpactMeasureCardRatingTooltip(
       SoftwareQuality.Security,
       'B',
       'overview.measures.software_impact.improve_rating_tooltip.software_quality.SECURITY.software_quality.security.B.overview.measures.software_impact.severity.LOW.improve_tooltip',
     );
 
-    ui.expectSoftwareImpactMeasureCard(
-      SoftwareQuality.Reliability,
-      'A',
-      {
-        total: 3,
-        [SoftwareImpactSeverity.High]: 0,
-        [SoftwareImpactSeverity.Medium]: 2,
-        [SoftwareImpactSeverity.Low]: 1,
-      },
-      [false, true, false],
-      undefined,
-      true,
-    );
+    ui.expectSoftwareImpactMeasureCard(SoftwareQuality.Reliability, 'A', 3, undefined, true);
     await ui.expectSoftwareImpactMeasureCardRatingTooltip(
       SoftwareQuality.Reliability,
       'A',
       'overview.measures.software_impact.improve_rating_tooltip.A.software_quality.RELIABILITY.software_quality.reliability.A.overview.measures.software_impact.severity.LOW.improve_tooltip',
     );
 
-    ui.expectSoftwareImpactMeasureCard(
-      SoftwareQuality.Maintainability,
-      'D',
-      {
-        total: 2,
-        [SoftwareImpactSeverity.High]: 0,
-        [SoftwareImpactSeverity.Medium]: 0,
-        [SoftwareImpactSeverity.Low]: 1,
-      },
-      [false, false, true],
-    );
+    ui.expectSoftwareImpactMeasureCard(SoftwareQuality.Maintainability, 'D', 2);
     await ui.expectSoftwareImpactMeasureCardRatingTooltip(
       SoftwareQuality.Maintainability,
       'D',
@@ -360,18 +328,7 @@ describe('project overview', () => {
 
     await user.click(await ui.overallCodeButton.find());
 
-    ui.expectSoftwareImpactMeasureCard(
-      SoftwareQuality.Maintainability,
-      'D',
-      {
-        total: 2,
-        [SoftwareImpactSeverity.High]: 0,
-        [SoftwareImpactSeverity.Medium]: 0,
-        [SoftwareImpactSeverity.Low]: 1,
-      },
-      [false, false, true],
-      '',
-    );
+    ui.expectSoftwareImpactMeasureCard(SoftwareQuality.Maintainability, 'D', 2, '');
   });
 
   it('should render old measures if software impact are missing', async () => {
index d9832064480331b1d10c7e3569fbbd83fd1ae9e1..9367882d9a1224b4825364db0cebe5e7c1d55756 100644 (file)
  */
 import userEvent from '@testing-library/user-event';
 import { byLabelText, byRole, byTestId, byText } from '~sonar-aligned/helpers/testSelector';
-import {
-  SoftwareImpactMeasureData,
-  SoftwareImpactSeverity,
-  SoftwareQuality,
-} from '../../../types/clean-code-taxonomy';
+import { SoftwareImpactSeverity, SoftwareQuality } from '../../../types/clean-code-taxonomy';
 
 export const getPageObjects = () => {
   const user = userEvent.setup();
@@ -41,8 +37,7 @@ export const getPageObjects = () => {
     expectSoftwareImpactMeasureCard: (
       softwareQuality: SoftwareQuality,
       rating?: string,
-      data?: SoftwareImpactMeasureData,
-      severitiesActiveState?: boolean[],
+      total?: number,
       branch = 'master',
       failed = false,
     ) => {
@@ -59,58 +54,17 @@ export const getPageObjects = () => {
           byText(rating, { exact: true }).get(ui.softwareImpactMeasureCard(softwareQuality).get()),
         ).toBeInTheDocument();
       }
-      if (data) {
+      if (total !== undefined) {
         const branchQuery = branch ? `&branch=${branch}` : '';
 
         expect(
           byRole('link', {
-            name: `overview.measures.software_impact.see_list_of_x_open_issues.${data.total}.software_quality.${softwareQuality}`,
+            name: `overview.measures.software_impact.see_list_of_x_open_issues.${total}.software_quality.${softwareQuality}`,
           }).get(),
         ).toHaveAttribute(
           'href',
           `/project/issues?issueStatuses=OPEN%2CCONFIRMED&impactSoftwareQualities=${softwareQuality}${branchQuery}&id=foo`,
         );
-        expect(
-          byRole('link', {
-            name: `overview.measures.software_impact.severity.see_x_open_issues.${
-              data[SoftwareImpactSeverity.High]
-            }.software_quality.${softwareQuality}.overview.measures.software_impact.severity.HIGH.tooltip`,
-          }).get(),
-        ).toHaveAttribute(
-          'href',
-          `/project/issues?issueStatuses=OPEN%2CCONFIRMED&impactSoftwareQualities=${softwareQuality}&impactSeverities=${SoftwareImpactSeverity.High}${branchQuery}&id=foo`,
-        );
-        expect(
-          byRole('link', {
-            name: `overview.measures.software_impact.severity.see_x_open_issues.${
-              data[SoftwareImpactSeverity.Medium]
-            }.software_quality.${softwareQuality}.overview.measures.software_impact.severity.MEDIUM.tooltip`,
-          }).get(),
-        ).toBeInTheDocument();
-        expect(
-          byRole('link', {
-            name: `overview.measures.software_impact.severity.see_x_open_issues.${
-              data[SoftwareImpactSeverity.Low]
-            }.software_quality.${softwareQuality}.overview.measures.software_impact.severity.LOW.tooltip`,
-          }).get(),
-        ).toBeInTheDocument();
-      }
-      if (severitiesActiveState) {
-        ui.expectSoftwareImpactMeasureBreakdownCard(
-          softwareQuality,
-          SoftwareImpactSeverity.High,
-          severitiesActiveState[0],
-        );
-        ui.expectSoftwareImpactMeasureBreakdownCard(
-          softwareQuality,
-          SoftwareImpactSeverity.Medium,
-          severitiesActiveState[1],
-        );
-        ui.expectSoftwareImpactMeasureBreakdownCard(
-          softwareQuality,
-          SoftwareImpactSeverity.Low,
-          severitiesActiveState[2],
-        );
       }
     },
     expectSoftwareImpactMeasureCardToHaveOldMeasures: (
index fa7e2786ea54d1b96264597ffc01b721ebe29b72..0d94c17277850534fb5cfc07049b010f07bed566 100644 (file)
@@ -68,9 +68,7 @@ export const BRANCH_OVERVIEW_METRICS: string[] = [
   MetricKey.security_hotspots_reviewed,
   MetricKey.new_security_hotspots_reviewed,
   MetricKey.security_review_rating,
-  MetricKey.software_quality_security_review_rating,
   MetricKey.new_security_review_rating,
-  MetricKey.new_software_quality_security_review_rating,
 
   // code smells
   MetricKey.code_smells,
index 80b1d177e9271358c45871c22667acdac84c85bf..f059e20362770a12e6ead7e09daf934ca3babe35 100644 (file)
@@ -253,8 +253,7 @@ export default class ProjectActivityGraphs extends React.PureComponent<Props, St
   };
 
   render() {
-    const { analyses, leakPeriodDate, loading, measuresHistory, metrics, query, isLegacy } =
-      this.props;
+    const { analyses, leakPeriodDate, loading, measuresHistory, metrics, query } = this.props;
     const { graphEndDate, graphStartDate, series } = this.state;
 
     return (
@@ -278,7 +277,6 @@ export default class ProjectActivityGraphs extends React.PureComponent<Props, St
           graphs={this.state.graphs}
           leakPeriodDate={leakPeriodDate}
           loading={loading}
-          isLegacy={isLegacy}
           measuresHistory={measuresHistory}
           removeCustomMetric={this.handleRemoveCustomMetric}
           selectedDate={query.selectedDate}
index 1e29192a07947f6bf4c1d1771429a23798d73ab6..e8132d76bef57f6fee2f75136b343b5bb9246d3b 100644 (file)
@@ -609,7 +609,6 @@ describe('ratings', () => {
     expect(await ui.graphs.findAll()).toHaveLength(1);
     expect(ui.metricChangedInfoBtn.get()).toBeInTheDocument();
     expect(ui.gapInfoMessage.get()).toBeInTheDocument();
-    expect(byText('E').query()).not.toBeInTheDocument();
   });
 
   it('should not show old rating if new one was always there', async () => {
@@ -652,10 +651,9 @@ describe('ratings', () => {
     expect(await ui.graphs.findAll()).toHaveLength(1);
     expect(ui.metricChangedInfoBtn.query()).not.toBeInTheDocument();
     expect(ui.gapInfoMessage.query()).not.toBeInTheDocument();
-    expect(byText('E').query()).not.toBeInTheDocument();
   });
 
-  it('should show E if no new metrics', async () => {
+  it('should not show change info button if no new metrics', async () => {
     timeMachineHandler.setMeasureHistory([
       mockMeasureHistory({
         metric: MetricKey.reliability_rating,
@@ -686,10 +684,9 @@ describe('ratings', () => {
     expect(await ui.graphs.findAll()).toHaveLength(1);
     expect(ui.metricChangedInfoBtn.query()).not.toBeInTheDocument();
     expect(ui.gapInfoMessage.query()).not.toBeInTheDocument();
-    expect(byText('E').get()).toBeInTheDocument();
   });
 
-  it('should not show gaps message and metric change button, but should show E in legacy mode', async () => {
+  it('should not show gaps message and metric change button in legacy mode', async () => {
     settingsHandler.set(SettingsKey.LegacyMode, 'true');
     timeMachineHandler.setMeasureHistory([
       mockMeasureHistory({
@@ -746,7 +743,6 @@ describe('ratings', () => {
     expect(await ui.graphs.findAll()).toHaveLength(1);
     expect(ui.metricChangedInfoBtn.query()).not.toBeInTheDocument();
     expect(ui.gapInfoMessage.query()).not.toBeInTheDocument();
-    expect(byText('E').get()).toBeInTheDocument();
   });
 });
 
index 63fe3f135e97d1f740e0308740949163475b73ec..927df18e7aa99d2c204fd697f38897916abc6317 100644 (file)
@@ -88,20 +88,20 @@ it('should show legacy filters', async () => {
   settingsHandler.set(SettingsKey.LegacyMode, 'true');
   renderPageSidebar();
 
-  expect(await screen.findAllByText('E')).toHaveLength(4);
-  expect(screen.queryByText(/projects.facets.rating_option/)).not.toBeInTheDocument();
-  expect(screen.queryByText('projects.facets.maintainability.description')).not.toBeInTheDocument();
-  expect(screen.queryByText('projects.facets.security_review.description')).not.toBeInTheDocument();
+  expect(await screen.findAllByText(/projects.facets.rating_option/)).toHaveLength(20);
+  expect(screen.getByText('projects.facets.rating_option.security.legacy.1')).toBeInTheDocument();
+  expect(
+    screen.getByText('projects.facets.rating_option.reliability.legacy.1'),
+  ).toBeInTheDocument();
 });
 
 it('should show non legacy filters', async () => {
   settingsHandler.set(SettingsKey.LegacyMode, 'false');
   renderPageSidebar();
 
-  expect(await screen.findAllByText(/projects.facets.rating_option/)).toHaveLength(16);
-  expect(screen.queryAllByText('E')).toHaveLength(0);
-  expect(screen.getByText('projects.facets.maintainability.description')).toBeInTheDocument();
-  expect(screen.getByText('projects.facets.security_review.description')).toBeInTheDocument();
+  expect(await screen.findAllByText(/projects.facets.rating_option/)).toHaveLength(20);
+  expect(screen.getByText('projects.facets.rating_option.security.1')).toBeInTheDocument();
+  expect(screen.getByText('projects.facets.rating_option.reliability.1')).toBeInTheDocument();
 });
 
 function renderPageSidebar(overrides: Partial<PageSidebarProps> = {}, currentUser?: CurrentUser) {
index e062e3961027ab9b1c3c8ceefcc87e4b7820a1a5..998ac41cc8c8c96d851e7e53693da023f6d403a8 100644 (file)
@@ -38,7 +38,7 @@ const MEASURES = {
   [MetricKey.reliability_rating]: '1.0',
   [MetricKey.security_rating]: '1.0',
   [MetricKey.sqale_rating]: '1.0',
-  [MetricKey.security_review_rating]: '1.0',
+  [MetricKey.security_review_rating]: '3.0',
   [MetricKey.new_bugs]: '12',
 };
 
@@ -123,7 +123,7 @@ describe('upgrade scenario (awaiting scan)', () => {
     }),
     [MetricKey.security_review_rating]: mockMeasure({
       metric: MetricKey.security_review_rating,
-      value: '1',
+      value: '3',
     }),
   };
 
@@ -140,9 +140,9 @@ describe('upgrade scenario (awaiting scan)', () => {
       metric: MetricKey.software_quality_security_rating,
       value: '2',
     }),
-    [MetricKey.software_quality_security_review_rating]: mockMeasure({
-      metric: MetricKey.software_quality_security_review_rating,
-      value: '2',
+    [MetricKey.security_review_rating]: mockMeasure({
+      metric: MetricKey.security_review_rating,
+      value: '3',
     }),
   };
   beforeEach(() => {
@@ -174,7 +174,6 @@ describe('upgrade scenario (awaiting scan)', () => {
         [MetricKey.software_quality_maintainability_rating]: '2',
         [MetricKey.software_quality_reliability_rating]: '2',
         [MetricKey.software_quality_security_rating]: '2',
-        [MetricKey.software_quality_security_review_rating]: '2',
         [MetricKey.code_smells]: '4',
         [MetricKey.bugs]: '5',
         [MetricKey.vulnerabilities]: '6',
@@ -183,7 +182,8 @@ describe('upgrade scenario (awaiting scan)', () => {
     expect(screen.getByText('1')).toBeInTheDocument();
     expect(screen.getByText('2')).toBeInTheDocument();
     expect(screen.getByText('3')).toBeInTheDocument();
-    await waitFor(() => expect(screen.getAllByText('B')).toHaveLength(4));
+    await waitFor(() => expect(screen.getAllByText('B')).toHaveLength(3));
+    await waitFor(() => expect(screen.getAllByText('C')).toHaveLength(1));
     expect(screen.queryByText('projects.awaiting_scan')).not.toBeInTheDocument();
     expect(screen.queryByText('4')).not.toBeInTheDocument();
     expect(screen.queryByText('5')).not.toBeInTheDocument();
@@ -208,7 +208,8 @@ describe('upgrade scenario (awaiting scan)', () => {
     expect(screen.getByText('4')).toBeInTheDocument();
     expect(screen.getByText('5')).toBeInTheDocument();
     expect(screen.getByText('6')).toBeInTheDocument();
-    expect(screen.getAllByText('A')).toHaveLength(4);
+    expect(screen.getAllByText('A')).toHaveLength(3);
+    expect(screen.getAllByText('C')).toHaveLength(1);
   });
 
   it('should display awaiting analysis badge, show new software qualities, but old ratings', async () => {
@@ -236,7 +237,8 @@ describe('upgrade scenario (awaiting scan)', () => {
     expect(screen.queryByText('4')).not.toBeInTheDocument();
     expect(screen.queryByText('5')).not.toBeInTheDocument();
     expect(screen.queryByText('6')).not.toBeInTheDocument();
-    await waitFor(() => expect(screen.getAllByText('A')).toHaveLength(4));
+    await waitFor(() => expect(screen.getAllByText('A')).toHaveLength(3));
+    expect(screen.getAllByText('C')).toHaveLength(1);
   });
 
   it('should display awaiting analysis badge and show the old measures for Application', async () => {
@@ -296,7 +298,8 @@ describe('upgrade scenario (awaiting scan)', () => {
     expect(screen.getByText('4')).toBeInTheDocument();
     expect(screen.getByText('5')).toBeInTheDocument();
     expect(screen.getByText('6')).toBeInTheDocument();
-    await waitFor(() => expect(screen.getAllByText('A')).toHaveLength(4));
+    await waitFor(() => expect(screen.getAllByText('A')).toHaveLength(3));
+    expect(screen.getAllByText('C')).toHaveLength(1);
     expect(screen.queryByText('projects.awaiting_scan')).not.toBeInTheDocument();
   });
 
@@ -323,7 +326,6 @@ describe('upgrade scenario (awaiting scan)', () => {
         [MetricKey.software_quality_maintainability_rating]: '2',
         [MetricKey.software_quality_reliability_rating]: '2',
         [MetricKey.software_quality_security_rating]: '2',
-        [MetricKey.software_quality_security_review_rating]: '2',
         [MetricKey.code_smells]: '4',
         [MetricKey.bugs]: '5',
         [MetricKey.vulnerabilities]: '6',
@@ -335,7 +337,8 @@ describe('upgrade scenario (awaiting scan)', () => {
     expect(screen.queryByText('1')).not.toBeInTheDocument();
     expect(screen.queryByText('2')).not.toBeInTheDocument();
     expect(screen.queryByText('3')).not.toBeInTheDocument();
-    await waitFor(() => expect(screen.getAllByText('A')).toHaveLength(4));
+    await waitFor(() => expect(screen.getAllByText('A')).toHaveLength(3));
+    expect(screen.getAllByText('C')).toHaveLength(1);
     expect(screen.queryByText('B')).not.toBeInTheDocument();
     expect(screen.queryByText('projects.awaiting_scan')).not.toBeInTheDocument();
   });
index 5dbbfc52654b2c780027f5a5a54dff936eefd55c..cd9aad480510d32479384bb11764312f8754fc90 100644 (file)
@@ -17,6 +17,7 @@
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
+import { Spinner } from '@sonarsource/echoes-react';
 import { MetricsRatingBadge, RatingEnum } from 'design-system';
 import * as React from 'react';
 import { useIntl } from 'react-intl';
@@ -37,9 +38,8 @@ interface Props {
   value?: any;
 }
 
-export default function RatingFacet(props: Props) {
+export default function RatingFacet(props: Readonly<Props>) {
   const { facet, maxFacetValue, name, property, value } = props;
-  const { data: isLegacy } = useIsLegacyCCTMode();
 
   const renderAccessibleLabel = React.useCallback(
     (option: number) => {
@@ -65,14 +65,14 @@ export default function RatingFacet(props: Props) {
       facet={facet}
       header={translate('metric_domain', name)}
       description={
-        !isLegacy && hasDescription(property)
+        hasDescription(property)
           ? translate(`projects.facets.${property.replace('new_', '')}.description`)
           : undefined
       }
       highlightUnder={1}
       maxFacetValue={maxFacetValue}
       onQueryChange={props.onQueryChange}
-      options={isLegacy ? [1, 2, 3, 4, 5] : [1, 2, 3, 4]}
+      options={[1, 2, 3, 4, 5]}
       property={property}
       renderAccessibleLabel={renderAccessibleLabel}
       renderOption={(option) => renderOption(option, property)}
@@ -93,25 +93,24 @@ function RatingOption({
   option,
   property,
 }: Readonly<{ option: string | number; property: string }>) {
-  const { data: isLegacy } = useIsLegacyCCTMode();
+  const { data: isLegacy, isLoading } = useIsLegacyCCTMode();
   const intl = useIntl();
 
   const ratingFormatted = formatMeasure(option, MetricType.Rating);
+  const propertyWithoutPrefix = property.replace('new_', '');
+  const isSecurityOrReliability = ['security', 'reliability'].includes(propertyWithoutPrefix);
   return (
-    <>
+    <Spinner isLoading={isLoading}>
       <MetricsRatingBadge
         label={ratingFormatted}
         rating={ratingFormatted as RatingEnum}
-        isLegacy={isLegacy}
         size="xs"
       />
-      {!isLegacy && (
-        <span className="sw-ml-2">
-          {intl.formatMessage({
-            id: `projects.facets.rating_option.${property.replace('new_', '')}.${option}`,
-          })}
-        </span>
-      )}
-    </>
+      <span className="sw-ml-2">
+        {intl.formatMessage({
+          id: `projects.facets.rating_option.${propertyWithoutPrefix}${isLegacy && isSecurityOrReliability ? '.legacy' : ''}.${option}`,
+        })}
+      </span>
+    </Spinner>
   );
 }
index 21ec392bfbea28938ce7cd2cc72a127516fddc35..709e2626f52b5d0c351fcd7ae94b911797503dea 100644 (file)
@@ -102,7 +102,6 @@ export const METRICS = [
   MetricKey.software_quality_maintainability_rating,
   MetricKey.security_hotspots_reviewed,
   MetricKey.security_review_rating,
-  MetricKey.software_quality_security_review_rating,
   MetricKey.duplicated_lines_density,
   MetricKey.coverage,
   MetricKey.ncloc,
@@ -138,8 +137,8 @@ export const LEGACY_FACETS = [
 export const FACETS = [
   MetricKey.software_quality_reliability_rating,
   MetricKey.software_quality_security_rating,
-  MetricKey.software_quality_security_review_rating,
   MetricKey.software_quality_maintainability_rating,
+  MetricKey.security_review_rating,
   MetricKey.coverage,
   MetricKey.duplicated_lines_density,
   MetricKey.ncloc,
@@ -166,8 +165,8 @@ export const LEGACY_LEAK_FACETS = [
 export const LEAK_FACETS = [
   MetricKey.new_software_quality_reliability_rating,
   MetricKey.new_software_quality_security_rating,
-  MetricKey.new_software_quality_security_review_rating,
   MetricKey.new_software_quality_maintainability_rating,
+  MetricKey.new_security_review_rating,
   MetricKey.new_coverage,
   MetricKey.new_duplicated_lines_density,
   MetricKey.new_lines,
@@ -318,8 +317,6 @@ export const propertyToMetricMap: Dict<string | undefined> = {
   new_reliability: 'new_software_quality_reliability_rating',
   security: 'software_quality_security_rating',
   new_security: 'new_software_quality_security_rating',
-  security_review: 'software_quality_security_review_rating',
-  new_security_review: 'new_software_quality_security_review_rating',
   maintainability: 'software_quality_maintainability_rating',
   new_maintainability: 'new_software_quality_maintainability_rating',
 };
index 4f0bfadf919e13afc27c84e5b721939bdfd3c6c6..f97e31fa6856e32cc2c263d836c71582ca815132 100644 (file)
@@ -50,8 +50,6 @@ const FORBIDDEN_METRICS: string[] = [
   MetricKey.new_software_quality_reliability_rating,
   MetricKey.software_quality_security_rating,
   MetricKey.new_software_quality_security_rating,
-  MetricKey.software_quality_security_review_rating,
-  MetricKey.new_software_quality_security_review_rating,
   MetricKey.effort_to_reach_software_quality_maintainability_rating_a,
   MetricKey.software_quality_maintainability_remediation_effort,
   MetricKey.new_software_quality_maintainability_remediation_effort,
index 028c1ccd46e1afa0ff0154143b5db1e1d7671335..1317281641bed42f28bad5a307be22a8a4e9512c 100644 (file)
@@ -41,7 +41,6 @@ interface Props {
   graphEndDate?: Date;
   graphStartDate?: Date;
   isCustom?: boolean;
-  isLegacy?: boolean;
   leakPeriodDate?: Date;
   measuresHistory: MeasureHistory[];
   metricsType: string;
@@ -69,7 +68,6 @@ export default function GraphHistory(props: Readonly<Props>) {
     series,
     showAreas,
     graphDescription,
-    isLegacy,
   } = props;
   const [tooltipIdx, setTooltipIdx] = React.useState<number | undefined>(undefined);
   const [tooltipXPos, setTooltipXPos] = React.useState<number | undefined>(undefined);
@@ -124,7 +122,6 @@ export default function GraphHistory(props: Readonly<Props>) {
                 splitPointDate={measuresHistory.find((m) => m.splitPointDate)?.splitPointDate}
                 metricType={metricsType}
                 selectedDate={selectedDate}
-                isLegacy={isLegacy}
                 series={series}
                 showAreas={showAreas}
                 startDate={graphStartDate}
index 9be16362121d0c57ac0019c4cd32051fb8c855b5..5bf2137e1d84a5c892deafb89cd4649bddab726b 100644 (file)
@@ -33,7 +33,6 @@ interface Props {
   graphEndDate?: Date;
   graphStartDate?: Date;
   graphs: Serie[][];
-  isLegacy?: boolean;
   leakPeriodDate?: Date;
   loading: boolean;
   measuresHistory: MeasureHistory[];
@@ -67,8 +66,7 @@ export default class GraphsHistory extends React.PureComponent<Props, State> {
   };
 
   render() {
-    const { analyses, graph, loading, series, ariaLabel, canShowDataAsTable, isLegacy } =
-      this.props;
+    const { analyses, graph, loading, series, ariaLabel, canShowDataAsTable } = this.props;
     const isCustom = isCustomGraph(graph);
 
     if (loading) {
@@ -107,7 +105,6 @@ export default class GraphsHistory extends React.PureComponent<Props, State> {
               graphStartDate={this.props.graphStartDate}
               isCustom={isCustom}
               key={idx}
-              isLegacy={isLegacy}
               leakPeriodDate={this.props.leakPeriodDate}
               measuresHistory={this.props.measuresHistory}
               metricsType={getSeriesMetricType(graphSeries)}
index 632410a237018f8b057676779a9fba0fd2f2fa39..2f86e9e75ed8aad4daa231f824c7a4a308e26f10 100644 (file)
@@ -51,7 +51,6 @@ export interface PropsWithoutTheme {
   height: number;
   hideGrid?: boolean;
   hideXAxis?: boolean;
-  isLegacy?: boolean;
   leakPeriodDate?: Date;
   // used to avoid same y ticks labels
   maxYTicksCount?: number;
@@ -145,10 +144,7 @@ export class AdvancedTimelineClass extends React.PureComponent<Props, State> {
   }
 
   getRatingScale = (availableHeight: number) => {
-    const { isLegacy } = this.props;
-    return scalePoint<number>()
-      .domain(isLegacy ? [5, 4, 3, 2, 1] : [4, 3, 2, 1])
-      .range([availableHeight, 0]);
+    return scalePoint<number>().domain([5, 4, 3, 2, 1]).range([availableHeight, 0]);
   };
 
   getLevelScale = (availableHeight: number) => {
index fbf0cad0c047298faf4ec94c5b13013608680413..ed7dfb4d3017c641ad88596341ee026a43a61b7e 100644 (file)
@@ -61,18 +61,9 @@ it('should render correctly', () => {
   checkSnapShot({ zoomSpeed: 2 }, 'zoomSpeed');
   checkSnapShot({ leakPeriodDate: new Date('2019-10-02T00:00:00.000Z') }, 'leakPeriodDate');
   checkSnapShot({ basisCurve: true }, 'basisCurve');
-  checkSnapShot({ isLegacy: false }, 'not legacy');
   checkSnapShot(
-    { isLegacy: false, splitPointDate: new Date('2019-10-02T00:00:00.000Z') },
-    'not legacy + split point, but not Rating',
-  );
-  checkSnapShot(
-    {
-      isLegacy: false,
-      splitPointDate: new Date('2019-10-02T00:00:00.000Z'),
-      metricType: MetricType.Rating,
-    },
-    'not legacy + split point',
+    { splitPointDate: new Date('2019-10-02T00:00:00.000Z') },
+    'split point, but not Rating',
   );
 });
 
@@ -113,7 +104,6 @@ function renderComponent(props?: Partial<PropsWithoutTheme>) {
         ]}
         width={100}
         zoomSpeed={1}
-        isLegacy
         {...props}
       />
     </TooltipProvider>,
index 93c34d14707d1022fdacbe91eefa4e86bbbbfa74..b44f642ade90f7f52aa14a6240f170bcea4664b8 100644 (file)
@@ -1913,171 +1913,7 @@ exports[`should render correctly: no height 1`] = `null`;
 
 exports[`should render correctly: no width 1`] = `null`;
 
-exports[`should render correctly: not legacy + split point 1`] = `
-<svg
-  class="line-chart"
-  height="100"
-  width="100"
->
-  <g
-    transform="translate(50, 26)"
-  >
-    <g>
-      <g>
-        <line
-          class="line-chart-grid"
-          x1="0"
-          x2="40"
-          y1="24"
-          y2="24"
-        />
-      </g>
-      <g>
-        <line
-          class="line-chart-grid"
-          x1="0"
-          x2="40"
-          y1="16"
-          y2="16"
-        />
-      </g>
-      <g>
-        <line
-          class="line-chart-grid"
-          x1="0"
-          x2="40"
-          y1="8"
-          y2="8"
-        />
-      </g>
-      <g>
-        <line
-          class="line-chart-grid"
-          x1="0"
-          x2="40"
-          y1="0"
-          y2="0"
-        />
-      </g>
-    </g>
-    <g
-      transform="translate(0, 20)"
-    >
-      <text
-        class="line-chart-tick sw-body-sm"
-        text-anchor="end"
-        transform="rotate(-35, 15, 24)"
-        x="15"
-        y="24"
-      >
-        October
-      </text>
-      <text
-        class="line-chart-tick sw-body-sm"
-        text-anchor="end"
-        transform="rotate(-35, 20, 24)"
-        x="20"
-        y="24"
-      >
-        06 AM
-      </text>
-      <text
-        class="line-chart-tick sw-body-sm"
-        text-anchor="end"
-        transform="rotate(-35, 25, 24)"
-        x="25"
-        y="24"
-      >
-        12 PM
-      </text>
-      <text
-        class="line-chart-tick sw-body-sm"
-        text-anchor="end"
-        transform="rotate(-35, 30, 24)"
-        x="30"
-        y="24"
-      >
-        06 PM
-      </text>
-      <text
-        class="line-chart-tick sw-body-sm"
-        text-anchor="end"
-        transform="rotate(-35, 35, 24)"
-        x="35"
-        y="24"
-      >
-        Wed 02
-      </text>
-      <text
-        class="line-chart-tick sw-body-sm"
-        text-anchor="end"
-        transform="rotate(-35, 40, 24)"
-        x="40"
-        y="24"
-      >
-        06 AM
-      </text>
-      <text
-        class="line-chart-tick sw-body-sm"
-        text-anchor="end"
-        transform="rotate(-35, 45, 24)"
-        x="45"
-        y="24"
-      >
-        12 PM
-      </text>
-      <text
-        class="line-chart-tick sw-body-sm"
-        text-anchor="end"
-        transform="rotate(-35, 50, 24)"
-        x="50"
-        y="24"
-      >
-        06 PM
-      </text>
-    </g>
-    <g>
-      <path
-        class="line-chart-path line-chart-path-0"
-        d="M0,0L20,8"
-        stroke="rgb(85,170,223)"
-        stroke-dasharray="0"
-      />
-      <path
-        class="line-chart-path line-chart-path-1"
-        d="M40,16Z"
-        stroke="rgb(58,127,173)"
-        stroke-dasharray="3"
-      />
-    </g>
-    <g>
-      <circle
-        cx="40"
-        cy="16"
-        fill="rgb(58,127,173)"
-        r="2"
-        stroke="white"
-        stroke-width="1"
-      />
-    </g>
-    <rect
-      class="chart-mouse-events-overlay"
-      height="24"
-      width="40"
-    />
-    <line
-      class="line-tooltip"
-      stroke-dasharray="2"
-      x1="20"
-      x2="20"
-      y1="24"
-      y2="-10"
-    />
-  </g>
-</svg>
-`;
-
-exports[`should render correctly: not legacy + split point, but not Rating 1`] = `
+exports[`should render correctly: rating metric 1`] = `
 <svg
   class="line-chart"
   height="100"
@@ -2101,107 +1937,8 @@ exports[`should render correctly: not legacy + split point, but not Rating 1`] =
           class="line-chart-grid"
           x1="0"
           x2="40"
-          y1="22.4"
-          y2="22.4"
-        />
-      </g>
-      <g>
-        <line
-          class="line-chart-grid"
-          x1="0"
-          x2="40"
-          y1="20.8"
-          y2="20.8"
-        />
-      </g>
-      <g>
-        <line
-          class="line-chart-grid"
-          x1="0"
-          x2="40"
-          y1="19.200000000000003"
-          y2="19.200000000000003"
-        />
-      </g>
-      <g>
-        <line
-          class="line-chart-grid"
-          x1="0"
-          x2="40"
-          y1="17.6"
-          y2="17.6"
-        />
-      </g>
-      <g>
-        <line
-          class="line-chart-grid"
-          x1="0"
-          x2="40"
-          y1="16"
-          y2="16"
-        />
-      </g>
-      <g>
-        <line
-          class="line-chart-grid"
-          x1="0"
-          x2="40"
-          y1="14.400000000000002"
-          y2="14.400000000000002"
-        />
-      </g>
-      <g>
-        <line
-          class="line-chart-grid"
-          x1="0"
-          x2="40"
-          y1="12.800000000000002"
-          y2="12.800000000000002"
-        />
-      </g>
-      <g>
-        <line
-          class="line-chart-grid"
-          x1="0"
-          x2="40"
-          y1="11.2"
-          y2="11.2"
-        />
-      </g>
-      <g>
-        <line
-          class="line-chart-grid"
-          x1="0"
-          x2="40"
-          y1="9.600000000000001"
-          y2="9.600000000000001"
-        />
-      </g>
-      <g>
-        <line
-          class="line-chart-grid"
-          x1="0"
-          x2="40"
-          y1="8"
-          y2="8"
-        />
-      </g>
-      <g>
-        <line
-          class="line-chart-grid"
-          x1="0"
-          x2="40"
-          y1="6.399999999999999"
-          y2="6.399999999999999"
-        />
-      </g>
-      <g>
-        <line
-          class="line-chart-grid"
-          x1="0"
-          x2="40"
-          y1="4.800000000000002"
-          y2="4.800000000000002"
+          y1="18"
+          y2="18"
         />
       </g>
       <g>
@@ -2209,8 +1946,8 @@ exports[`should render correctly: not legacy + split point, but not Rating 1`] =
           class="line-chart-grid"
           x1="0"
           x2="40"
-          y1="3.1999999999999993"
-          y2="3.1999999999999993"
+          y1="12"
+          y2="12"
         />
       </g>
       <g>
@@ -2218,8 +1955,8 @@ exports[`should render correctly: not legacy + split point, but not Rating 1`] =
           class="line-chart-grid"
           x1="0"
           x2="40"
-          y1="1.6000000000000023"
-          y2="1.6000000000000023"
+          y1="6"
+          y2="6"
         />
       </g>
       <g>
@@ -2311,13 +2048,13 @@ exports[`should render correctly: not legacy + split point, but not Rating 1`] =
     <g>
       <path
         class="line-chart-path line-chart-path-0"
-        d="M0,16L20,8"
+        d="M0,0L20,6"
         stroke="rgb(85,170,223)"
         stroke-dasharray="0"
       />
       <path
         class="line-chart-path line-chart-path-1"
-        d="M40,0Z"
+        d="M40,12Z"
         stroke="rgb(58,127,173)"
         stroke-dasharray="3"
       />
@@ -2325,7 +2062,7 @@ exports[`should render correctly: not legacy + split point, but not Rating 1`] =
     <g>
       <circle
         cx="40"
-        cy="0"
+        cy="12"
         fill="rgb(58,127,173)"
         r="2"
         stroke="white"
@@ -2337,19 +2074,11 @@ exports[`should render correctly: not legacy + split point, but not Rating 1`] =
       height="24"
       width="40"
     />
-    <line
-      class="line-tooltip"
-      stroke-dasharray="2"
-      x1="20"
-      x2="20"
-      y1="24"
-      y2="-10"
-    />
   </g>
 </svg>
 `;
 
-exports[`should render correctly: not legacy 1`] = `
+exports[`should render correctly: selected date 1`] = `
 <svg
   class="line-chart"
   height="100"
@@ -2604,167 +2333,27 @@ exports[`should render correctly: not legacy 1`] = `
         stroke-width="1"
       />
     </g>
-    <rect
-      class="chart-mouse-events-overlay"
-      height="24"
-      width="40"
-    />
-  </g>
-</svg>
-`;
-
-exports[`should render correctly: rating metric 1`] = `
-<svg
-  class="line-chart"
-  height="100"
-  width="100"
->
-  <g
-    transform="translate(50, 26)"
-  >
     <g>
-      <g>
-        <line
-          class="line-chart-grid"
-          x1="0"
-          x2="40"
-          y1="24"
-          y2="24"
-        />
-      </g>
-      <g>
-        <line
-          class="line-chart-grid"
-          x1="0"
-          x2="40"
-          y1="18"
-          y2="18"
-        />
-      </g>
-      <g>
-        <line
-          class="line-chart-grid"
-          x1="0"
-          x2="40"
-          y1="12"
-          y2="12"
-        />
-      </g>
-      <g>
-        <line
-          class="line-chart-grid"
-          x1="0"
-          x2="40"
-          y1="6"
-          y2="6"
-        />
-      </g>
-      <g>
-        <line
-          class="line-chart-grid"
-          x1="0"
-          x2="40"
-          y1="0"
-          y2="0"
-        />
-      </g>
-    </g>
-    <g
-      transform="translate(0, 20)"
-    >
-      <text
-        class="line-chart-tick sw-body-sm"
-        text-anchor="end"
-        transform="rotate(-35, 15, 24)"
-        x="15"
-        y="24"
-      >
-        October
-      </text>
-      <text
-        class="line-chart-tick sw-body-sm"
-        text-anchor="end"
-        transform="rotate(-35, 20, 24)"
-        x="20"
-        y="24"
-      >
-        06 AM
-      </text>
-      <text
-        class="line-chart-tick sw-body-sm"
-        text-anchor="end"
-        transform="rotate(-35, 25, 24)"
-        x="25"
-        y="24"
-      >
-        12 PM
-      </text>
-      <text
-        class="line-chart-tick sw-body-sm"
-        text-anchor="end"
-        transform="rotate(-35, 30, 24)"
-        x="30"
-        y="24"
-      >
-        06 PM
-      </text>
-      <text
-        class="line-chart-tick sw-body-sm"
-        text-anchor="end"
-        transform="rotate(-35, 35, 24)"
-        x="35"
-        y="24"
-      >
-        Wed 02
-      </text>
-      <text
-        class="line-chart-tick sw-body-sm"
-        text-anchor="end"
-        transform="rotate(-35, 40, 24)"
-        x="40"
-        y="24"
-      >
-        06 AM
-      </text>
-      <text
-        class="line-chart-tick sw-body-sm"
-        text-anchor="end"
-        transform="rotate(-35, 45, 24)"
-        x="45"
-        y="24"
-      >
-        12 PM
-      </text>
-      <text
-        class="line-chart-tick sw-body-sm"
-        text-anchor="end"
-        transform="rotate(-35, 50, 24)"
-        x="50"
-        y="24"
-      >
-        06 PM
-      </text>
-    </g>
-    <g>
-      <path
-        class="line-chart-path line-chart-path-0"
-        d="M0,0L20,6"
-        stroke="rgb(85,170,223)"
-        stroke-dasharray="0"
+      <line
+        class="line-tooltip"
+        x1="0"
+        x2="0"
+        y1="24"
+        y2="0"
       />
-      <path
-        class="line-chart-path line-chart-path-1"
-        d="M40,12Z"
-        stroke="rgb(58,127,173)"
-        stroke-dasharray="3"
+      <circle
+        cx="0"
+        cy="16"
+        fill="rgb(85,170,223)"
+        r="4"
+        stroke="white"
+        stroke-width="1"
       />
-    </g>
-    <g>
       <circle
-        cx="40"
-        cy="12"
+        cx="0"
+        cy="0"
         fill="rgb(58,127,173)"
-        r="2"
+        r="4"
         stroke="white"
         stroke-width="1"
       />
@@ -2778,7 +2367,7 @@ exports[`should render correctly: rating metric 1`] = `
 </svg>
 `;
 
-exports[`should render correctly: selected date 1`] = `
+exports[`should render correctly: split point, but not Rating 1`] = `
 <svg
   class="line-chart"
   height="100"
@@ -3033,36 +2622,19 @@ exports[`should render correctly: selected date 1`] = `
         stroke-width="1"
       />
     </g>
-    <g>
-      <line
-        class="line-tooltip"
-        x1="0"
-        x2="0"
-        y1="24"
-        y2="0"
-      />
-      <circle
-        cx="0"
-        cy="16"
-        fill="rgb(85,170,223)"
-        r="4"
-        stroke="white"
-        stroke-width="1"
-      />
-      <circle
-        cx="0"
-        cy="0"
-        fill="rgb(58,127,173)"
-        r="4"
-        stroke="white"
-        stroke-width="1"
-      />
-    </g>
     <rect
       class="chart-mouse-events-overlay"
       height="24"
       width="40"
     />
+    <line
+      class="line-tooltip"
+      stroke-dasharray="2"
+      x1="20"
+      x2="20"
+      y1="24"
+      y2="-10"
+    />
   </g>
 </svg>
 `;
diff --git a/server/sonar-web/src/main/js/components/facets/Facet.tsx b/server/sonar-web/src/main/js/components/facets/Facet.tsx
new file mode 100644 (file)
index 0000000..0273f0f
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ * 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.
+ */
+import classNames from 'classnames';
+import { FacetBox, FacetItem } from 'design-system';
+import { orderBy, sortBy, without } from 'lodash';
+import * as React from 'react';
+import { formatMeasure } from '~sonar-aligned/helpers/measures';
+import { MetricType } from '~sonar-aligned/types/metrics';
+import { FacetKey } from '../../apps/coding-rules/query';
+import { FacetItemsList } from '../../apps/issues/sidebar/FacetItemsList';
+import { MultipleSelectionHint } from '../../apps/issues/sidebar/MultipleSelectionHint';
+import { translate } from '../../helpers/l10n';
+import { Dict } from '../../types/types';
+import Tooltip from '../controls/Tooltip';
+
+export interface BasicProps {
+  fetching?: boolean;
+  help?: React.ReactNode;
+  onChange: (changes: Dict<string | string[] | undefined>) => void;
+  onToggle: (facet: FacetKey) => void;
+  open: boolean;
+  stats?: Dict<number>;
+  values: string[];
+}
+
+interface Props extends BasicProps {
+  disabled?: boolean;
+  disabledHelper?: string;
+  options?: string[];
+  property: FacetKey;
+  renderFooter?: () => React.ReactNode;
+  renderName?: (value: string, disabled: boolean) => React.ReactNode;
+  renderTextName?: (value: string) => string;
+  singleSelection?: boolean;
+}
+
+export default class Facet extends React.PureComponent<Props> {
+  handleItemClick = (itemValue: string, multiple: boolean) => {
+    const { values } = this.props;
+    let newValue;
+    if (this.props.singleSelection) {
+      const value = values.length ? values[0] : undefined;
+      newValue = itemValue === value ? undefined : itemValue;
+    } else if (multiple) {
+      newValue = orderBy(
+        values.includes(itemValue) ? without(values, itemValue) : [...values, itemValue],
+      );
+    } else {
+      newValue = values.includes(itemValue) && values.length < 2 ? [] : [itemValue];
+    }
+    this.props.onChange({ [this.props.property]: newValue });
+  };
+
+  handleHeaderClick = () => this.props.onToggle(this.props.property);
+
+  handleClear = () => this.props.onChange({ [this.props.property]: [] });
+
+  getStat = (value: string) => this.props.stats && this.props.stats[value];
+
+  renderItem = (value: string) => {
+    const active = this.props.values.includes(value);
+    const stat = this.getStat(value);
+    const disabled = stat === 0 || typeof stat === 'undefined';
+    const { renderName = defaultRenderName, renderTextName = defaultRenderName } = this.props;
+
+    return (
+      <FacetItem
+        className="it__search-navigator-facet"
+        active={active}
+        key={value}
+        name={renderName(value, disabled)}
+        onClick={this.handleItemClick}
+        stat={stat && formatMeasure(stat, MetricType.ShortInteger)}
+        value={value}
+        tooltip={renderTextName(value)}
+      />
+    );
+  };
+
+  render() {
+    const {
+      disabled,
+      disabledHelper,
+      open,
+      property,
+      renderTextName = defaultRenderName,
+      stats,
+      help,
+      values,
+      fetching,
+    } = this.props;
+    const items =
+      this.props.options ||
+      (stats &&
+        sortBy(
+          Object.keys(stats),
+          (key) => -stats[key],
+          (key) => renderTextName(key).toLowerCase(),
+        ));
+    const headerId = `facet_${property}`;
+    const nbSelectableItems =
+      items?.filter((item) => (stats ? stats[item] : undefined)).length ?? 0;
+    const nbSelectedItems = values.length;
+
+    return (
+      <FacetBox
+        className={classNames('it__search-navigator-facet-box it__search-navigator-facet-header', {
+          'it__search-navigator-facet-box-forbidden': disabled,
+        })}
+        data-property={property}
+        loading={fetching}
+        clearIconLabel={translate('clear')}
+        count={values.length}
+        id={headerId}
+        name={translate('coding_rules.facet', property)}
+        onClear={this.handleClear}
+        onClick={disabled ? undefined : this.handleHeaderClick}
+        open={open && !disabled}
+        disabled={disabled}
+        disabledHelper={disabledHelper}
+        tooltipComponent={Tooltip}
+        help={help}
+      >
+        {open && items !== undefined && (
+          <FacetItemsList labelledby={headerId}>{items.map(this.renderItem)}</FacetItemsList>
+        )}
+
+        {open && this.props.renderFooter !== undefined && this.props.renderFooter()}
+
+        <MultipleSelectionHint
+          nbSelectableItems={nbSelectableItems}
+          nbSelectedItems={nbSelectedItems}
+        />
+      </FacetBox>
+    );
+  }
+}
+
+function defaultRenderName(value: string) {
+  return value;
+}
diff --git a/server/sonar-web/src/main/js/components/facets/SeverityFacet.tsx b/server/sonar-web/src/main/js/components/facets/SeverityFacet.tsx
new file mode 100644 (file)
index 0000000..3f7cae9
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * 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.
+ */
+
+import { Popover } from '@sonarsource/echoes-react';
+import { BareButton, HelperHintIcon } from 'design-system';
+import * as React from 'react';
+import { useIntl } from 'react-intl';
+import { IMPACT_SEVERITIES } from '../../helpers/constants';
+import { DocLink } from '../../helpers/doc-links';
+import { translate } from '../../helpers/l10n';
+import DocumentationLink from '../common/DocumentationLink';
+import SoftwareImpactSeverityIcon from '../icon-mappers/SoftwareImpactSeverityIcon';
+import Facet, { BasicProps } from './Facet';
+
+export default function SeverityFacet(props: Readonly<BasicProps>) {
+  const intl = useIntl();
+  const renderName = React.useCallback(
+    (severity: string, disabled: boolean) => (
+      <div className="sw-flex sw-items-center">
+        <SoftwareImpactSeverityIcon severity={severity} disabled={disabled} />
+        <span className="sw-ml-1">{translate('severity_impact', severity)}</span>
+      </div>
+    ),
+    [],
+  );
+
+  const renderTextName = React.useCallback(
+    (severity: string) => translate('severity_impact', severity),
+    [],
+  );
+
+  return (
+    <Facet
+      {...props}
+      options={IMPACT_SEVERITIES}
+      property="impactSeverities"
+      renderName={renderName}
+      renderTextName={renderTextName}
+      help={
+        <Popover
+          title={intl.formatMessage({ id: 'severity_impact.levels' })}
+          description={
+            <>
+              <p>{intl.formatMessage({ id: 'severity_impact.help.line1' })}</p>
+              <p className="sw-mt-2">{intl.formatMessage({ id: 'severity_impact.help.line2' })}</p>
+            </>
+          }
+          footer={
+            <DocumentationLink to={DocLink.CleanCodeIntroduction}>
+              {intl.formatMessage({ id: 'learn_more' })}
+            </DocumentationLink>
+          }
+        >
+          <BareButton aria-label={intl.formatMessage({ id: 'more_information' })}>
+            <HelperHintIcon />
+          </BareButton>
+        </Popover>
+      }
+    />
+  );
+}
index 61251a5db8c274ffa0c74768a724a02a5b7b62f7..9d1cebd666babdced9b595ef524b0fa0250a55d7 100644 (file)
@@ -19,7 +19,9 @@
  */
 import {
   IconProps,
+  SoftwareImpactSeverityBlockerIcon,
   SoftwareImpactSeverityHighIcon,
+  SoftwareImpactSeverityInfoIcon,
   SoftwareImpactSeverityLowIcon,
   SoftwareImpactSeverityMediumIcon,
 } from 'design-system';
@@ -33,10 +35,14 @@ interface Props extends IconProps {
   severity: string | null | undefined;
 }
 
+const defaultIconSize = 14;
+
 const severityIcons: Dict<(props: IconProps) => React.ReactElement> = {
+  [SoftwareImpactSeverity.Blocker]: SoftwareImpactSeverityBlockerIcon,
   [SoftwareImpactSeverity.High]: SoftwareImpactSeverityHighIcon,
   [SoftwareImpactSeverity.Medium]: SoftwareImpactSeverityMediumIcon,
   [SoftwareImpactSeverity.Low]: SoftwareImpactSeverityLowIcon,
+  [SoftwareImpactSeverity.Info]: SoftwareImpactSeverityInfoIcon,
 };
 
 export default function SoftwareImpactSeverityIcon({ severity, ...iconProps }: Readonly<Props>) {
@@ -45,5 +51,12 @@ export default function SoftwareImpactSeverityIcon({ severity, ...iconProps }: R
   }
 
   const DesiredIcon = severityIcons[severity];
-  return <DesiredIcon {...iconProps} aria-label={translate('severity', severity)} />;
+  return (
+    <DesiredIcon
+      {...iconProps}
+      width={iconProps?.width ?? defaultIconSize}
+      height={iconProps?.height ?? defaultIconSize}
+      aria-label={translate('severity_impact', severity)}
+    />
+  );
 }
index 60e1437baf7715036d142ee13c3d9ff34000f103..f37449d3e9830d917f9fbff9ada83698f981a798 100644 (file)
@@ -28,7 +28,6 @@ export const KNOWN_RATINGS = [
   MetricKey.software_quality_maintainability_rating,
   MetricKey.software_quality_reliability_rating,
   MetricKey.software_quality_security_rating,
-  MetricKey.software_quality_security_review_rating,
   'maintainability_rating', // Needed to provide the label for "new_maintainability_rating"
 ];
 
index 20ec0c2b7df1169a3d43ecc4cc3c2177a0c79cc4..9e386598774222a1f4b0788339166acc0b92396e 100644 (file)
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 
-import { Pill } from 'design-system';
+import { Popover } from '@sonarsource/echoes-react';
+import { Pill, PillVariant } from 'design-system';
 import React from 'react';
-import DocHelpTooltip from '~sonar-aligned/components/controls/DocHelpTooltip';
 import { DocLink } from '../../helpers/doc-links';
 import { translate } from '../../helpers/l10n';
 import { CleanCodeAttribute, CleanCodeAttributeCategory } from '../../types/clean-code-taxonomy';
+import DocumentationLink from '../common/DocumentationLink';
 
 export interface Props {
   className?: string;
@@ -36,35 +37,26 @@ export function CleanCodeAttributePill(props: Readonly<Props>) {
   const { className, cleanCodeAttributeCategory, cleanCodeAttribute, type = 'issue' } = props;
 
   return (
-    <DocHelpTooltip
-      content={
-        <>
-          <p className="sw-mb-4">
-            {translate(
-              type,
-              cleanCodeAttribute ? 'clean_code_attribute' : 'clean_code_attribute_category',
-              cleanCodeAttribute ?? cleanCodeAttributeCategory,
-              'title',
-            )}
-          </p>
-          <p>
-            {translate(
-              'issue',
-              cleanCodeAttribute ? 'clean_code_attribute' : 'clean_code_attribute_category',
-              cleanCodeAttribute ?? cleanCodeAttributeCategory,
-              'advice',
-            )}
-          </p>
-        </>
+    <Popover
+      title={translate(
+        type,
+        cleanCodeAttribute ? 'clean_code_attribute' : 'clean_code_attribute_category',
+        cleanCodeAttribute ?? cleanCodeAttributeCategory,
+        'title',
+      )}
+      description={translate(
+        'issue',
+        cleanCodeAttribute ? 'clean_code_attribute' : 'clean_code_attribute_category',
+        cleanCodeAttribute ?? cleanCodeAttributeCategory,
+        'advice',
+      )}
+      footer={
+        <DocumentationLink to={DocLink.CleanCodeIntroduction}>
+          {translate('learn_more')}
+        </DocumentationLink>
       }
-      links={[
-        {
-          href: DocLink.CleanCodeIntroduction,
-          label: translate('learn_more'),
-        },
-      ]}
     >
-      <Pill variant="accent" data-guiding-id="issue-1" className={className}>
+      <Pill variant={PillVariant.Accent} data-guiding-id="issue-1" className={className}>
         <span className="sw-font-semibold">
           {translate(type, 'clean_code_attribute_category', cleanCodeAttributeCategory)}
         </span>
@@ -72,6 +64,6 @@ export function CleanCodeAttributePill(props: Readonly<Props>) {
           <span> | {translate(type, 'clean_code_attribute', cleanCodeAttribute)}</span>
         )}
       </Pill>
-    </DocHelpTooltip>
+    </Popover>
   );
 }
index f0ae5848ac202bd4d1a8c5107baf921866fc98fd..88b531e6232db1fdbed0165da5eaba9edc4f2091 100644 (file)
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 
+import { Popover } from '@sonarsource/echoes-react';
 import classNames from 'classnames';
-import { Pill } from 'design-system';
+import { Pill, PillVariant } from 'design-system';
+import { noop } from 'lodash';
 import React from 'react';
 import { FormattedMessage } from 'react-intl';
-import DocHelpTooltip from '~sonar-aligned/components/controls/DocHelpTooltip';
 import { DocLink } from '../../helpers/doc-links';
 import { translate } from '../../helpers/l10n';
 import { SoftwareImpactSeverity } from '../../types/clean-code-taxonomy';
+import DocumentationLink from '../common/DocumentationLink';
 import SoftwareImpactSeverityIcon from '../icon-mappers/SoftwareImpactSeverityIcon';
 
 export interface Props {
@@ -39,34 +41,50 @@ export default function SoftwareImpactPill(props: Props) {
   const { className, severity, quality, type = 'issue' } = props;
 
   const variant = {
-    [SoftwareImpactSeverity.High]: 'danger',
-    [SoftwareImpactSeverity.Medium]: 'warning',
-    [SoftwareImpactSeverity.Low]: 'info',
-  }[severity] as 'danger' | 'warning' | 'info';
+    [SoftwareImpactSeverity.Blocker]: PillVariant.Critical,
+    [SoftwareImpactSeverity.High]: PillVariant.Danger,
+    [SoftwareImpactSeverity.Medium]: PillVariant.Warning,
+    [SoftwareImpactSeverity.Low]: PillVariant.Caution,
+    [SoftwareImpactSeverity.Info]: PillVariant.Info,
+  }[severity];
 
   return (
-    <DocHelpTooltip
-      content={
-        <FormattedMessage
-          id={`${type}.impact.severity.tooltip`}
-          defaultMessage={translate(`${type}.impact.severity.tooltip`)}
-          values={{
-            severity: translate('severity', severity).toLowerCase(),
-            quality: quality.toLowerCase(),
-          }}
-        />
+    <Popover
+      title={translate('severity_impact.title')}
+      description={
+        <>
+          <FormattedMessage
+            id={`${type}.impact.severity.tooltip`}
+            values={{
+              severity: translate('severity_impact', severity).toLowerCase(),
+              quality: quality.toLowerCase(),
+            }}
+          />
+          <p className="sw-mt-2">
+            <span className="sw-mr-1">{translate('severity_impact.help.line1')}</span>
+            {translate('severity_impact.help.line2')}
+          </p>
+        </>
+      }
+      footer={
+        <DocumentationLink to={DocLink.CleanCodeIntroduction}>
+          {translate('learn_more')}
+        </DocumentationLink>
       }
-      links={[
-        {
-          href: DocLink.CleanCodeIntroduction,
-          label: translate('learn_more'),
-        },
-      ]}
     >
-      <Pill className={classNames('sw-flex sw-gap-1 sw-items-center', className)} variant={variant}>
+      <Pill
+        className={classNames('sw-flex sw-gap-1 sw-items-center', className)}
+        onClick={noop}
+        variant={variant}
+      >
         {quality}
-        <SoftwareImpactSeverityIcon severity={severity} data-guiding-id="issue-3" />
+        <SoftwareImpactSeverityIcon
+          width={14}
+          height={14}
+          severity={severity}
+          data-guiding-id="issue-3"
+        />
       </Pill>
-    </DocHelpTooltip>
+    </Popover>
   );
 }
index 3bc09b6b130f0f2b5b93c7dab6863e2bfc7deaaa..476e9cd98bf6741f52d0257c8026b5f719445bef 100644 (file)
@@ -34,9 +34,11 @@ interface SoftwareImpactPillListProps extends React.HTMLAttributes<HTMLUListElem
 }
 
 const severityMap = {
-  [SoftwareImpactSeverity.High]: 2,
-  [SoftwareImpactSeverity.Medium]: 1,
-  [SoftwareImpactSeverity.Low]: 0,
+  [SoftwareImpactSeverity.Blocker]: 4,
+  [SoftwareImpactSeverity.High]: 3,
+  [SoftwareImpactSeverity.Medium]: 2,
+  [SoftwareImpactSeverity.Low]: 1,
+  [SoftwareImpactSeverity.Info]: 0,
 };
 
 export default function SoftwareImpactPillList({
index 6d7ebd1b895b2541f27be45a302cc3aa9a89e6b5..62c3e9d46c4fbb717aa54ea3be1e6f6811dc4ecf 100644 (file)
@@ -68,10 +68,7 @@ export const mergeRatingMeasureHistory = (
 
   const historyMapper = (historyItem: { date: string; value?: string }) => ({
     date: parseDateFn(historyItem.date),
-    value:
-      softwareQualityMeasuresMap.size > 0 && historyItem.value === '5.0'
-        ? '4.0'
-        : historyItem.value,
+    value: historyItem.value,
   });
 
   return historyDataFiltered.map((measure) => {
index ce344a723208036c754ff5ffade8cf739bcaf888..2e70556dfaa65dbc205038a3d8e3de90ed6a5f28 100644 (file)
@@ -112,11 +112,9 @@ export const LEAK_OLD_TAXONOMY_METRICS = [
 ];
 
 export const OLD_TAXONOMY_RATINGS = [
-  MetricKey.releasability_rating,
   MetricKey.sqale_rating,
   MetricKey.security_rating,
   MetricKey.reliability_rating,
-  MetricKey.security_review_rating,
   MetricKey.sqale_index,
   MetricKey.reliability_remediation_effort,
   MetricKey.security_remediation_effort,
@@ -128,7 +126,6 @@ export const LEAK_OLD_TAXONOMY_RATINGS = [
   MetricKey.new_maintainability_rating,
   MetricKey.new_security_rating,
   MetricKey.new_reliability_rating,
-  MetricKey.new_security_review_rating,
   MetricKey.new_technical_debt,
   MetricKey.new_security_remediation_effort,
   MetricKey.new_reliability_remediation_effort,
@@ -191,9 +188,6 @@ export const DEPRECATED_ACTIVITY_METRICS = [
 ];
 
 export const SOFTWARE_QUALITY_RATING_METRICS_MAP: Record<string, MetricKey> = {
-  [MetricKey.releasability_rating]: MetricKey.software_quality_releasability_rating,
-  [MetricKey.releasability_rating_distribution]:
-    MetricKey.software_quality_releasability_rating_distribution,
   [MetricKey.sqale_rating]: MetricKey.software_quality_maintainability_rating,
   [MetricKey.maintainability_rating_distribution]:
     MetricKey.software_quality_maintainability_rating_distribution,
@@ -202,9 +196,6 @@ export const SOFTWARE_QUALITY_RATING_METRICS_MAP: Record<string, MetricKey> = {
   [MetricKey.reliability_rating]: MetricKey.software_quality_reliability_rating,
   [MetricKey.reliability_rating_distribution]:
     MetricKey.software_quality_reliability_rating_distribution,
-  [MetricKey.security_review_rating]: MetricKey.software_quality_security_review_rating,
-  [MetricKey.security_review_rating_distribution]:
-    MetricKey.software_quality_security_review_rating_distribution,
   [MetricKey.reliability_remediation_effort]:
     MetricKey.software_quality_reliability_remediation_effort,
   [MetricKey.security_remediation_effort]: MetricKey.software_quality_security_remediation_effort,
@@ -214,20 +205,14 @@ export const SOFTWARE_QUALITY_RATING_METRICS_MAP: Record<string, MetricKey> = {
     MetricKey.effort_to_reach_software_quality_maintainability_rating_a,
   [MetricKey.last_change_on_maintainability_rating]:
     MetricKey.last_change_on_software_quality_maintainability_rating,
-  [MetricKey.last_change_on_releasability_rating]:
-    MetricKey.last_change_on_software_quality_releasability_rating,
   [MetricKey.last_change_on_reliability_rating]:
     MetricKey.last_change_on_software_quality_reliability_rating,
   [MetricKey.last_change_on_security_rating]:
     MetricKey.last_change_on_software_quality_security_rating,
-  [MetricKey.last_change_on_security_review_rating]:
-    MetricKey.last_change_on_software_quality_security_review_rating,
   [MetricKey.maintainability_rating_effort]:
     MetricKey.software_quality_maintainability_rating_effort,
   [MetricKey.reliability_rating_effort]: MetricKey.software_quality_reliability_rating_effort,
   [MetricKey.security_rating_effort]: MetricKey.software_quality_security_rating_effort,
-  [MetricKey.security_review_rating_effort]:
-    MetricKey.software_quality_security_review_rating_effort,
   [MetricKey.new_maintainability_rating]: MetricKey.new_software_quality_maintainability_rating,
   [MetricKey.new_maintainability_rating_distribution]:
     MetricKey.new_software_quality_maintainability_rating_distribution,
@@ -237,9 +222,6 @@ export const SOFTWARE_QUALITY_RATING_METRICS_MAP: Record<string, MetricKey> = {
   [MetricKey.new_reliability_rating]: MetricKey.new_software_quality_reliability_rating,
   [MetricKey.new_reliability_rating_distribution]:
     MetricKey.new_software_quality_reliability_rating_distribution,
-  [MetricKey.new_security_review_rating]: MetricKey.new_software_quality_security_review_rating,
-  [MetricKey.new_security_review_rating_distribution]:
-    MetricKey.new_software_quality_security_review_rating_distribution,
   [MetricKey.new_technical_debt]: MetricKey.new_software_quality_maintainability_remediation_effort,
   [MetricKey.new_reliability_remediation_effort]:
     MetricKey.new_software_quality_reliability_remediation_effort,
@@ -249,11 +231,9 @@ export const SOFTWARE_QUALITY_RATING_METRICS_MAP: Record<string, MetricKey> = {
 };
 
 export const SOFTWARE_QUALITY_RATING_METRICS = [
-  MetricKey.software_quality_releasability_rating,
   MetricKey.software_quality_maintainability_rating,
   MetricKey.software_quality_security_rating,
   MetricKey.software_quality_reliability_rating,
-  MetricKey.software_quality_security_review_rating,
   MetricKey.software_quality_maintainability_remediation_effort,
   MetricKey.software_quality_reliability_remediation_effort,
   MetricKey.software_quality_security_remediation_effort,
@@ -262,7 +242,6 @@ export const SOFTWARE_QUALITY_RATING_METRICS = [
   MetricKey.new_software_quality_maintainability_rating,
   MetricKey.new_software_quality_security_rating,
   MetricKey.new_software_quality_reliability_rating,
-  MetricKey.new_software_quality_security_review_rating,
   MetricKey.new_software_quality_maintainability_remediation_effort,
   MetricKey.new_software_quality_reliability_remediation_effort,
   MetricKey.new_software_quality_security_remediation_effort,
index d88e3fcf552b3efc937527efd2e760ba8105879d..7ce4c1604edca0f561f1f8cb296eb2575a02ddb0 100644 (file)
@@ -69,10 +69,8 @@ export enum MetricKey {
   last_change_on_security_rating = 'last_change_on_security_rating',
   last_change_on_security_review_rating = 'last_change_on_security_review_rating',
   last_change_on_software_quality_maintainability_rating = 'last_change_on_software_quality_maintainability_rating',
-  last_change_on_software_quality_releasability_rating = 'last_change_on_software_quality_releasability_rating',
   last_change_on_software_quality_reliability_rating = 'last_change_on_software_quality_reliability_rating',
   last_change_on_software_quality_security_rating = 'last_change_on_software_quality_security_rating',
-  last_change_on_software_quality_security_review_rating = 'last_change_on_software_quality_security_review_rating',
   last_commit_date = 'last_commit_date',
   leak_projects = 'leak_projects',
   line_coverage = 'line_coverage',
@@ -129,9 +127,7 @@ export enum MetricKey {
   new_security_remediation_effort = 'new_security_remediation_effort',
   new_software_quality_security_remediation_effort = 'new_software_quality_security_remediation_effort',
   new_security_review_rating = 'new_security_review_rating',
-  new_software_quality_security_review_rating = 'new_software_quality_security_review_rating',
   new_security_review_rating_distribution = 'new_security_review_rating_distribution',
-  new_software_quality_security_review_rating_distribution = 'new_software_quality_security_review_rating_distribution',
   new_sqale_debt_ratio = 'new_sqale_debt_ratio',
   new_software_quality_maintainability_debt_ratio = 'new_software_quality_maintainability_debt_ratio',
   new_technical_debt = 'new_technical_debt',
@@ -152,9 +148,7 @@ export enum MetricKey {
   quality_profiles = 'quality_profiles',
   releasability_effort = 'releasability_effort',
   releasability_rating = 'releasability_rating',
-  software_quality_releasability_rating = 'software_quality_releasability_rating',
   releasability_rating_distribution = 'releasability_rating_distribution',
-  software_quality_releasability_rating_distribution = 'software_quality_releasability_rating_distribution',
   reliability_issues = 'reliability_issues',
   reliability_rating = 'reliability_rating',
   software_quality_reliability_rating = 'software_quality_reliability_rating',
@@ -177,11 +171,8 @@ export enum MetricKey {
   security_remediation_effort = 'security_remediation_effort',
   software_quality_security_remediation_effort = 'software_quality_security_remediation_effort',
   security_review_rating = 'security_review_rating',
-  software_quality_security_review_rating = 'software_quality_security_review_rating',
   security_review_rating_distribution = 'security_review_rating_distribution',
-  software_quality_security_review_rating_distribution = 'software_quality_security_review_rating_distribution',
   security_review_rating_effort = 'security_review_rating_effort',
-  software_quality_security_review_rating_effort = 'software_quality_security_review_rating_effort',
   skipped_tests = 'skipped_tests',
   sonarjava_feedback = 'sonarjava_feedback',
   sqale_debt_ratio = 'sqale_debt_ratio',
index 13bea9be601013310491df1494ed77e093c5f360..d5a80809f20a6a85840dca7a7b2c2efa6f824644 100644 (file)
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 export enum SoftwareImpactSeverity {
+  Blocker = 'BLOCKER',
   High = 'HIGH',
   Medium = 'MEDIUM',
   Low = 'LOW',
+  Info = 'INFO',
 }
 
 export enum CleanCodeAttributeCategory {
@@ -60,7 +62,9 @@ export interface SoftwareImpact {
 
 export interface SoftwareImpactMeasureData {
   total: number;
+  [SoftwareImpactSeverity.Blocker]: number;
   [SoftwareImpactSeverity.High]: number;
   [SoftwareImpactSeverity.Medium]: number;
   [SoftwareImpactSeverity.Low]: number;
+  [SoftwareImpactSeverity.Info]: number;
 }
index 7f43b94e89a43f22db1a309647504f879c2da695..8db287d862827702ca80adc2e26c928e7b83a44d 100644 (file)
@@ -19,6 +19,7 @@
  */
 declare namespace jest {
   interface Matchers<R> {
+    toHaveAPopoverWithContent(content: string): Promise<CustomMatcherResult>;
     toHaveATooltipWithContent(content: string): Promise<CustomMatcherResult>;
     toHaveNoA11yViolations(): Promise<CustomMatcherResult>;
   }
index 9d6344f96e21f076a5c4521f685d62f39d6be235..eb678ce714dda54dc2df17a5884c515f6fcf2701 100644 (file)
@@ -21,7 +21,6 @@ package org.sonar.server.measure.index;
 
 import com.google.common.collect.ArrayListMultimap;
 import com.google.common.collect.Multimap;
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
@@ -107,11 +106,9 @@ import static org.sonar.api.measures.CoreMetrics.SQALE_RATING_KEY;
 import static org.sonar.core.metric.SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_MAINTAINABILITY_RATING_KEY;
 import static org.sonar.core.metric.SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_RELIABILITY_RATING_KEY;
 import static org.sonar.core.metric.SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_SECURITY_RATING_KEY;
-import static org.sonar.core.metric.SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_SECURITY_REVIEW_RATING_KEY;
 import static org.sonar.core.metric.SoftwareQualitiesMetrics.SOFTWARE_QUALITY_MAINTAINABILITY_RATING_KEY;
 import static org.sonar.core.metric.SoftwareQualitiesMetrics.SOFTWARE_QUALITY_RELIABILITY_RATING_KEY;
 import static org.sonar.core.metric.SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_RATING_KEY;
-import static org.sonar.core.metric.SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_REVIEW_RATING_KEY;
 import static org.sonar.server.es.EsUtils.escapeSpecialRegexChars;
 import static org.sonar.server.es.EsUtils.termsToMap;
 import static org.sonar.server.es.IndexType.FIELD_INDEX_TYPE;
@@ -175,14 +172,12 @@ public class ProjectMeasuresIndex {
     NEW_SECURITY_REVIEW_RATING(new RatingMeasureFacet(NEW_SECURITY_REVIEW_RATING_KEY)),
 
     //Software quality ratings
-    SOFTWARE_QUALITY_MAINTAINABILITY_RATING(new RatingMeasureFacet(SOFTWARE_QUALITY_MAINTAINABILITY_RATING_KEY, 4)),
-    NEW_SOFTWARE_QUALITY_MAINTAINABILITY_RATING(new RatingMeasureFacet(NEW_SOFTWARE_QUALITY_MAINTAINABILITY_RATING_KEY, 4)),
-    SOFTWARE_QUALITY_RELIABILITY_RATING(new RatingMeasureFacet(SOFTWARE_QUALITY_RELIABILITY_RATING_KEY, 4)),
-    NEW_SOFTWARE_QUALITY_RELIABILITY_RATING(new RatingMeasureFacet(NEW_SOFTWARE_QUALITY_RELIABILITY_RATING_KEY, 4)),
-    SOFTWARE_QUALITY_SECURITY_RATING(new RatingMeasureFacet(SOFTWARE_QUALITY_SECURITY_RATING_KEY, 4)),
-    NEW_SOFTWARE_QUALITY_SECURITY_RATING(new RatingMeasureFacet(NEW_SOFTWARE_QUALITY_SECURITY_RATING_KEY, 4)),
-    SOFTWARE_QUALITY_SECURITY_REVIEW_RATING(new RatingMeasureFacet(SOFTWARE_QUALITY_SECURITY_REVIEW_RATING_KEY, 4)),
-    NEW_SOFTWARE_QUALITY_SECURITY_REVIEW_RATING(new RatingMeasureFacet(NEW_SOFTWARE_QUALITY_SECURITY_REVIEW_RATING_KEY, 4)),
+    SOFTWARE_QUALITY_MAINTAINABILITY_RATING(new RatingMeasureFacet(SOFTWARE_QUALITY_MAINTAINABILITY_RATING_KEY)),
+    NEW_SOFTWARE_QUALITY_MAINTAINABILITY_RATING(new RatingMeasureFacet(NEW_SOFTWARE_QUALITY_MAINTAINABILITY_RATING_KEY)),
+    SOFTWARE_QUALITY_RELIABILITY_RATING(new RatingMeasureFacet(SOFTWARE_QUALITY_RELIABILITY_RATING_KEY)),
+    NEW_SOFTWARE_QUALITY_RELIABILITY_RATING(new RatingMeasureFacet(NEW_SOFTWARE_QUALITY_RELIABILITY_RATING_KEY)),
+    SOFTWARE_QUALITY_SECURITY_RATING(new RatingMeasureFacet(SOFTWARE_QUALITY_SECURITY_RATING_KEY)),
+    NEW_SOFTWARE_QUALITY_SECURITY_RATING(new RatingMeasureFacet(NEW_SOFTWARE_QUALITY_SECURITY_RATING_KEY)),
 
     SECURITY_HOTSPOTS_REVIEWED(new RangeMeasureFacet(SECURITY_HOTSPOTS_REVIEWED_KEY, SECURITY_REVIEW_RATING_THRESHOLDS)),
     NEW_SECURITY_HOTSPOTS_REVIEWED(new RangeMeasureFacet(NEW_SECURITY_HOTSPOTS_REVIEWED_KEY, SECURITY_REVIEW_RATING_THRESHOLDS)),
@@ -588,20 +583,14 @@ public class ProjectMeasuresIndex {
   private static class RatingMeasureFacet extends MeasureFacet {
 
     private RatingMeasureFacet(String metricKey) {
-      super(metricKey, new MetricRatingFacetBuilder(metricKey, 5));
-    }
-
-    private RatingMeasureFacet(String metricKey, int maxRating) {
-      super(metricKey, new MetricRatingFacetBuilder(metricKey, maxRating));
+      super(metricKey, new MetricRatingFacetBuilder(metricKey));
     }
 
     private static class MetricRatingFacetBuilder implements FacetBuilder {
       private final String metricKey;
-      private final int maxRating;
 
-      private MetricRatingFacetBuilder(String metricKey, int maxRating) {
+      private MetricRatingFacetBuilder(String metricKey) {
         this.metricKey = metricKey;
-        this.maxRating = maxRating;
       }
 
       @Override
@@ -609,20 +598,19 @@ public class ProjectMeasuresIndex {
         return topAggregationHelper.buildTopAggregation(
           facet.getName(), facet.getTopAggregationDef(),
           NO_EXTRA_FILTER,
-          t -> t.subAggregation(createMeasureRatingFacet(metricKey, maxRating)));
+          t -> t.subAggregation(createMeasureRatingFacet(metricKey)));
       }
 
-      private static AbstractAggregationBuilder<?> createMeasureRatingFacet(String metricKey, int maxRating) {
-        List<KeyedFilter> filter = new ArrayList<>();
-        for (int i = 1; i <= maxRating; i++) {
-          filter.add(new KeyedFilter(String.valueOf(i), termQuery(FIELD_MEASURES_MEASURE_VALUE, Double.valueOf(i))));
-        }
-
+      private static AbstractAggregationBuilder<?> createMeasureRatingFacet(String metricKey) {
         return AggregationBuilders.nested("nested_" + metricKey, FIELD_MEASURES)
           .subAggregation(
             AggregationBuilders.filter("filter_" + metricKey, termsQuery(FIELD_MEASURES_MEASURE_KEY, metricKey))
-              .subAggregation(filters(metricKey, filter.toArray(new KeyedFilter[0])
-              )));
+              .subAggregation(filters(metricKey,
+                new KeyedFilter("1", termQuery(FIELD_MEASURES_MEASURE_VALUE, 1D)),
+                new KeyedFilter("2", termQuery(FIELD_MEASURES_MEASURE_VALUE, 2D)),
+                new KeyedFilter("3", termQuery(FIELD_MEASURES_MEASURE_VALUE, 3D)),
+                new KeyedFilter("4", termQuery(FIELD_MEASURES_MEASURE_VALUE, 4D)),
+                new KeyedFilter("5", termQuery(FIELD_MEASURES_MEASURE_VALUE, 5D)))));
       }
     }
   }
index 40a8403f08994c8ba3d3eeb85b8680dbf552dfa4..ffeffc9647ec9e6d12d1e6e0e770ef0c89dae600 100644 (file)
@@ -52,6 +52,7 @@ import static org.sonar.api.issue.Issue.STATUS_REOPENED;
 import static org.sonar.api.issue.Issue.STATUS_RESOLVED;
 import static org.sonar.api.issue.impact.SoftwareQuality.MAINTAINABILITY;
 import static org.sonar.api.issue.impact.SoftwareQuality.RELIABILITY;
+import static org.sonar.api.issue.impact.SoftwareQuality.SECURITY;
 import static org.sonar.api.rule.Severity.BLOCKER;
 import static org.sonar.api.rule.Severity.CRITICAL;
 import static org.sonar.api.rule.Severity.INFO;
@@ -115,7 +116,8 @@ class IssueIndexFacetsTest extends IssueIndexTestCommon {
       newDoc("I4", project.uuid(), file2),
       newDoc("I5", project.uuid(), file3));
 
-    assertThatFacetHasOnly(IssueQuery.builder(), "files", entry("src/NAME_ABCD", 1L), entry("src/NAME_BCDE", 2L), entry("src/NAME_CDEF", 1L));
+    assertThatFacetHasOnly(IssueQuery.builder(), "files", entry("src/NAME_ABCD", 1L), entry("src/NAME_BCDE", 2L), entry("src/NAME_CDEF",
+      1L));
   }
 
   @Test
@@ -147,13 +149,15 @@ class IssueIndexFacetsTest extends IssueIndexTestCommon {
   void facet_on_directories_return_100_entries_plus_selected_values() {
     ComponentDto project = newPrivateProjectDto();
     indexIssues(
-      rangeClosed(1, 110).mapToObj(i -> newDoc(newFileDto(project, newDirectory(project, "dir" + i)), project.uuid()).setDirectoryPath("a" + i)).toArray(IssueDoc[]::new));
+      rangeClosed(1, 110).mapToObj(i -> newDoc(newFileDto(project, newDirectory(project, "dir" + i)), project.uuid()).setDirectoryPath("a"
+        + i)).toArray(IssueDoc[]::new));
     IssueDoc issue1 = newDoc(newFileDto(project, newDirectory(project, "path1")), project.uuid()).setDirectoryPath("directory1");
     IssueDoc issue2 = newDoc(newFileDto(project, newDirectory(project, "path2")), project.uuid()).setDirectoryPath("directory2");
     indexIssues(issue1, issue2);
 
     assertThatFacetHasSize(IssueQuery.builder().build(), "directories", 100);
-    assertThatFacetHasSize(IssueQuery.builder().directories(asList(issue1.directoryPath(), issue2.directoryPath())).build(), "directories", 102);
+    assertThatFacetHasSize(IssueQuery.builder().directories(asList(issue1.directoryPath(), issue2.directoryPath())).build(), "directories"
+      , 102);
   }
 
   @Test
@@ -275,7 +279,8 @@ class IssueIndexFacetsTest extends IssueIndexTestCommon {
     ComponentDto file = newFileDto(project);
 
     indexIssues(
-      newDoc("I1", project.uuid(), file).setType(RuleType.VULNERABILITY).setSansTop25(asList("porous-defenses", "risky-resource", "insecure-interaction")),
+      newDoc("I1", project.uuid(), file).setType(RuleType.VULNERABILITY).setSansTop25(asList("porous-defenses", "risky-resource",
+        "insecure-interaction")),
       newDoc("I2", project.uuid(), file).setType(RuleType.VULNERABILITY).setSansTop25(singletonList("porous-defenses")),
       newDoc("I3", project.uuid(), file));
 
@@ -458,7 +463,8 @@ class IssueIndexFacetsTest extends IssueIndexTestCommon {
     indexIssues(issue1, issue2);
 
     assertThatFacetHasSize(IssueQuery.builder().build(), "assignees", 100);
-    assertThatFacetHasSize(IssueQuery.builder().assigneeUuids(asList(issue1.assigneeUuid(), issue2.assigneeUuid())).build(), "assignees", 102);
+    assertThatFacetHasSize(IssueQuery.builder().assigneeUuids(asList(issue1.assigneeUuid(), issue2.assigneeUuid())).build(), "assignees",
+      102);
   }
 
   @Test
@@ -588,8 +594,8 @@ class IssueIndexFacetsTest extends IssueIndexTestCommon {
     SearchOptions options = fixtureForCreatedAtFacet();
 
     SearchResponse result = underTest.search(IssueQuery.builder()
-      .createdAfter(parseDateTime("2014-09-01T00:00:00+0100"))
-      .createdBefore(parseDateTime("2014-09-21T00:00:00+0100")).build(),
+        .createdAfter(parseDateTime("2014-09-01T00:00:00+0100"))
+        .createdBefore(parseDateTime("2014-09-21T00:00:00+0100")).build(),
       options);
     Map<String, Long> createdAt = new Facets(result, system2.getDefaultTimeZone().toZoneId()).get("createdAt");
     assertThat(createdAt).containsOnly(
@@ -604,8 +610,8 @@ class IssueIndexFacetsTest extends IssueIndexTestCommon {
     SearchOptions options = fixtureForCreatedAtFacet();
 
     SearchResponse result = underTest.search(IssueQuery.builder()
-      .createdAfter(parseDateTime("2014-09-01T00:00:00+0100"))
-      .createdBefore(parseDateTime("2015-01-19T00:00:00+0100")).build(),
+        .createdAfter(parseDateTime("2014-09-01T00:00:00+0100"))
+        .createdBefore(parseDateTime("2015-01-19T00:00:00+0100")).build(),
       options);
     Map<String, Long> createdAt = new Facets(result, system2.getDefaultTimeZone().toZoneId()).get("createdAt");
     assertThat(createdAt).containsOnly(
@@ -622,8 +628,8 @@ class IssueIndexFacetsTest extends IssueIndexTestCommon {
     SearchOptions options = fixtureForCreatedAtFacet();
 
     SearchResponse result = underTest.search(IssueQuery.builder()
-      .createdAfter(parseDateTime("2011-01-01T00:00:00+0100"))
-      .createdBefore(parseDateTime("2016-01-01T00:00:00+0100")).build(),
+        .createdAfter(parseDateTime("2011-01-01T00:00:00+0100"))
+        .createdBefore(parseDateTime("2016-01-01T00:00:00+0100")).build(),
       options);
     Map<String, Long> createdAt = new Facets(result, system2.getDefaultTimeZone().toZoneId()).get("createdAt");
     assertThat(createdAt).containsOnly(
@@ -640,8 +646,8 @@ class IssueIndexFacetsTest extends IssueIndexTestCommon {
     SearchOptions options = fixtureForCreatedAtFacet();
 
     SearchResponse result = underTest.search(IssueQuery.builder()
-      .createdAfter(parseDateTime("2014-09-01T00:00:00-0100"))
-      .createdBefore(parseDateTime("2014-09-02T00:00:00-0100")).build(),
+        .createdAfter(parseDateTime("2014-09-01T00:00:00-0100"))
+        .createdBefore(parseDateTime("2014-09-02T00:00:00-0100")).build(),
       options);
     Map<String, Long> createdAt = new Facets(result, system2.getDefaultTimeZone().toZoneId()).get("createdAt");
     assertThat(createdAt).containsOnly(
@@ -673,7 +679,7 @@ class IssueIndexFacetsTest extends IssueIndexTestCommon {
     SearchOptions searchOptions = fixtureForCreatedAtFacet();
 
     SearchResponse result = underTest.search(IssueQuery.builder()
-      .createdBefore(parseDateTime("2016-01-01T00:00:00+0100")).build(),
+        .createdBefore(parseDateTime("2016-01-01T00:00:00+0100")).build(),
       searchOptions);
     Map<String, Long> createdAt = new Facets(result, system2.getDefaultTimeZone().toZoneId()).get("createdAt");
     assertThat(createdAt).containsOnly(
@@ -739,8 +745,8 @@ class IssueIndexFacetsTest extends IssueIndexTestCommon {
 
     indexIssues(
       newDoc("I1", project.uuid(), file).setImpacts(Map.of(
-        MAINTAINABILITY, org.sonar.api.issue.impact.Severity.HIGH,
-        RELIABILITY, org.sonar.api.issue.impact.Severity.MEDIUM))
+          MAINTAINABILITY, org.sonar.api.issue.impact.Severity.HIGH,
+          RELIABILITY, org.sonar.api.issue.impact.Severity.MEDIUM))
         .setTags(singletonList("my-tag")),
       newDoc("I2", project.uuid(), file).setImpacts(Map.of(
         MAINTAINABILITY, org.sonar.api.issue.impact.Severity.LOW)),
@@ -765,14 +771,15 @@ class IssueIndexFacetsTest extends IssueIndexTestCommon {
       entry("RELIABILITY", 1L),
       entry("SECURITY", 0L));
 
-    assertThatFacetHasOnly(IssueQuery.builder().impactSeverities(Set.of(org.sonar.api.issue.impact.Severity.HIGH.name())), "impactSoftwareQualities",
+    assertThatFacetHasOnly(IssueQuery.builder().impactSeverities(Set.of(org.sonar.api.issue.impact.Severity.HIGH.name())),
+      "impactSoftwareQualities",
       entry("MAINTAINABILITY", 1L),
       entry("RELIABILITY", 1L),
       entry("SECURITY", 0L));
 
     assertThatFacetHasOnly(IssueQuery.builder()
-      .tags(singletonList("my-tag"))
-      .impactSeverities(Set.of(org.sonar.api.issue.impact.Severity.HIGH.name())), "impactSoftwareQualities",
+        .tags(singletonList("my-tag"))
+        .impactSeverities(Set.of(org.sonar.api.issue.impact.Severity.HIGH.name())), "impactSoftwareQualities",
       entry("MAINTAINABILITY", 1L),
       entry("RELIABILITY", 0L),
       entry("SECURITY", 0L));
@@ -786,23 +793,26 @@ class IssueIndexFacetsTest extends IssueIndexTestCommon {
     indexIssues(
       newDoc("I1", project.uuid(), file).setImpacts(Map.of(
         MAINTAINABILITY, org.sonar.api.issue.impact.Severity.LOW,
-        RELIABILITY, org.sonar.api.issue.impact.Severity.LOW)));
+        RELIABILITY, org.sonar.api.issue.impact.Severity.LOW,
+        SECURITY, org.sonar.api.issue.impact.Severity.BLOCKER)));
 
     assertThatFacetHasOnly(IssueQuery.builder()
-      .impactSoftwareQualities(Set.of(MAINTAINABILITY.name()))
-      .impactSeverities(Set.of(org.sonar.api.issue.impact.Severity.LOW.name())),
+        .impactSoftwareQualities(Set.of(MAINTAINABILITY.name()))
+        .impactSeverities(Set.of(org.sonar.api.issue.impact.Severity.LOW.name())),
       "impactSoftwareQualities",
       entry("MAINTAINABILITY", 1L),
       entry("RELIABILITY", 1L),
       entry("SECURITY", 0L));
 
     assertThatFacetHasOnly(IssueQuery.builder()
-      .impactSoftwareQualities(Set.of(MAINTAINABILITY.name()))
-      .impactSeverities(Set.of(org.sonar.api.issue.impact.Severity.LOW.name())),
+        .impactSoftwareQualities(Set.of(MAINTAINABILITY.name()))
+        .impactSeverities(Set.of(org.sonar.api.issue.impact.Severity.LOW.name())),
       "impactSeverities",
       entry("HIGH", 0L),
       entry("MEDIUM", 0L),
-      entry("LOW", 1L));
+      entry("LOW", 1L),
+      entry("INFO", 0L),
+      entry("BLOCKER", 0L));
   }
 
   @Test
@@ -817,14 +827,18 @@ class IssueIndexFacetsTest extends IssueIndexTestCommon {
       newDoc("I2", project.uuid(), file).setImpacts(Map.of(
         MAINTAINABILITY, org.sonar.api.issue.impact.Severity.LOW)),
       newDoc("I3", project.uuid(), file).setImpacts(Map.of(
-        RELIABILITY, org.sonar.api.issue.impact.Severity.HIGH)),
+        RELIABILITY, org.sonar.api.issue.impact.Severity.HIGH,
+        SECURITY, org.sonar.api.issue.impact.Severity.BLOCKER)),
       newDoc("I4", project.uuid(), file).setImpacts(Map.of(
-        MAINTAINABILITY, org.sonar.api.issue.impact.Severity.LOW)));
+        MAINTAINABILITY, org.sonar.api.issue.impact.Severity.LOW,
+        RELIABILITY, org.sonar.api.issue.impact.Severity.INFO)));
 
     assertThatFacetHasOnly(IssueQuery.builder(), "impactSeverities",
       entry("HIGH", 2L),
       entry("MEDIUM", 1L),
-      entry("LOW", 2L));
+      entry("LOW", 2L),
+      entry("BLOCKER", 1L),
+      entry("INFO", 1L));
   }
 
   @Test
@@ -846,7 +860,9 @@ class IssueIndexFacetsTest extends IssueIndexTestCommon {
     assertThatFacetHasOnly(IssueQuery.builder().impactSoftwareQualities(Set.of(MAINTAINABILITY.name())), "impactSeverities",
       entry("HIGH", 1L),
       entry("MEDIUM", 0L),
-      entry("LOW", 2L));
+      entry("LOW", 2L),
+      entry("BLOCKER", 0L),
+      entry("INFO", 0L));
   }
 
   @Test
index 8b21640cbb5517bc475da488d560e0134c89dc67..8c2c0ad6af8d7cc734a419bcf0203fcbda723a7f 100644 (file)
@@ -85,8 +85,6 @@ class ProjectMeasuresIndexTest {
   private static final String NEW_SOFTWARE_QUALITY_RELIABILITY_RATING_KEY = "new_software_quality_reliability_rating";
   private static final String SOFTWARE_QUALITY_SECURITY_RATING_KEY = "software_quality_security_rating";
   private static final String NEW_SOFTWARE_QUALITY_SECURITY_RATING_KEY = "new_software_quality_security_rating";
-  private static final String SOFTWARE_QUALITY_SECURITY_REVIEW_RATING_KEY = "software_quality_security_review_rating";
-  private static final String NEW_SOFTWARE_QUALITY_SECURITY_REVIEW_RATING_KEY = "new_software_quality_security_review_rating";
 
   private static final String SECURITY_HOTSPOTS_REVIEWED = "security_hotspots_reviewed";
   private static final String NEW_SECURITY_HOTSPOTS_REVIEWED = "new_security_hotspots_reviewed";
@@ -127,8 +125,7 @@ class ProjectMeasuresIndexTest {
     return new String[]{
       SOFTWARE_QUALITY_MAINTAINABILITY_RATING_KEY, NEW_SOFTWARE_QUALITY_MAINTAINABILITY_RATING_KEY,
       SOFTWARE_QUALITY_RELIABILITY_RATING_KEY, NEW_SOFTWARE_QUALITY_RELIABILITY_RATING_KEY,
-      SOFTWARE_QUALITY_SECURITY_RATING_KEY, NEW_SOFTWARE_QUALITY_SECURITY_RATING_KEY,
-      SOFTWARE_QUALITY_SECURITY_REVIEW_RATING_KEY, NEW_SOFTWARE_QUALITY_SECURITY_REVIEW_RATING_KEY
+      SOFTWARE_QUALITY_SECURITY_RATING_KEY, NEW_SOFTWARE_QUALITY_SECURITY_RATING_KEY
     };
   }
 
@@ -1088,7 +1085,10 @@ class ProjectMeasuresIndexTest {
       newDoc(metricKey, 3d),
       // 2 docs with rating D
       newDoc(metricKey, 4d),
-      newDoc(metricKey, 4d));
+      newDoc(metricKey, 4d),
+      // 1 doc with rating E
+      newDoc(metricKey, 5d));
+
 
     Facets facets = underTest.search(new ProjectMeasuresQuery(), new SearchOptions().addFacets(metricKey)).getFacets();
 
@@ -1096,7 +1096,8 @@ class ProjectMeasuresIndexTest {
       entry("1", 3L),
       entry("2", 2L),
       entry("3", 4L),
-      entry("4", 2L));
+      entry("4", 2L),
+      entry("5", 1L));
   }
 
   @ParameterizedTest
@@ -1160,7 +1161,9 @@ class ProjectMeasuresIndexTest {
       newDoc(metricKey, 3d, NCLOC, 40000d, COVERAGE, 0d),
       newDoc(metricKey, 3d, NCLOC, 50000d, COVERAGE, 0d),
       // docs with rating D
-      newDoc(metricKey, 4d, NCLOC, 120000d, COVERAGE, 0d));
+      newDoc(metricKey, 4d, NCLOC, 120000d, COVERAGE, 0d),
+      // docs with rating E
+      newDoc(metricKey, 5d, NCLOC, 120000d, COVERAGE, 0d));
 
     Facets facets = underTest.search(new ProjectMeasuresQuery()
         .addMetricCriterion(MetricCriterion.create(metricKey, Operator.LT, 3d))
@@ -1172,7 +1175,8 @@ class ProjectMeasuresIndexTest {
       entry("1", 3L),
       entry("2", 2L),
       entry("3", 4L),
-      entry("4", 1L));
+      entry("4", 1L),
+      entry("5", 1L));
     // But facet on ncloc does well take into into filters
     assertThat(facets.get(NCLOC)).containsExactly(
       entry("*-1000.0", 3L),
@@ -1233,7 +1237,9 @@ class ProjectMeasuresIndexTest {
       // docs with rating C
       newDoc(metricKey, 3d),
       // docs with rating D
-      newDoc(metricKey, 4d));
+      newDoc(metricKey, 4d),
+      // docs with rating E
+      newDoc(metricKey, 5d));
 
     userSession.logIn(USER1);
     Facets facets = underTest.search(new ProjectMeasuresQuery(), new SearchOptions().addFacets(metricKey)).getFacets();
@@ -1242,7 +1248,8 @@ class ProjectMeasuresIndexTest {
       entry("1", 3L),
       entry("2", 2L),
       entry("3", 0L),
-      entry("4", 0L));
+      entry("4", 0L),
+      entry("5", 0L));
   }
 
   @Test
index f6c7f9307e5436a864a9a896a1ebc48c92847b9f..1ef9f7e82a049320e6d6c8ef6e2e42f28e7ee0e1 100644 (file)
@@ -23,9 +23,11 @@ import java.util.Arrays;
 import org.sonar.api.issue.impact.Severity;
 
 public enum ImpactSeverityRestEnum {
+  INFO(Severity.INFO),
   LOW(Severity.LOW),
   MEDIUM(Severity.MEDIUM),
-  HIGH(Severity.HIGH);
+  HIGH(Severity.HIGH),
+  BLOCKER(Severity.BLOCKER);
 
   private final Severity severity;
 
index 3269fa61cce0c8046a05e91a0cb1fbc16a189c88..f7744ed7dc438d007ae5f469f849f88f30613e80 100644 (file)
@@ -99,11 +99,9 @@ import static org.sonar.api.utils.DateUtils.formatDateTime;
 import static org.sonar.core.metric.SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_MAINTAINABILITY_RATING_KEY;
 import static org.sonar.core.metric.SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_RELIABILITY_RATING_KEY;
 import static org.sonar.core.metric.SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_SECURITY_RATING_KEY;
-import static org.sonar.core.metric.SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_SECURITY_REVIEW_RATING_KEY;
 import static org.sonar.core.metric.SoftwareQualitiesMetrics.SOFTWARE_QUALITY_MAINTAINABILITY_RATING_KEY;
 import static org.sonar.core.metric.SoftwareQualitiesMetrics.SOFTWARE_QUALITY_RELIABILITY_RATING_KEY;
 import static org.sonar.core.metric.SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_RATING_KEY;
-import static org.sonar.core.metric.SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_REVIEW_RATING_KEY;
 import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;
 import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_002;
 import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_003;
@@ -138,7 +136,7 @@ public class SearchProjectsActionIT {
   @DataProvider
   public static Object[][] software_quality_rating_metric_keys() {
     return new Object[][]{{SOFTWARE_QUALITY_MAINTAINABILITY_RATING_KEY}, {SOFTWARE_QUALITY_RELIABILITY_RATING_KEY},
-      {SOFTWARE_QUALITY_SECURITY_RATING_KEY}, {SOFTWARE_QUALITY_SECURITY_REVIEW_RATING_KEY}};
+      {SOFTWARE_QUALITY_SECURITY_RATING_KEY}};
   }
 
   @DataProvider
@@ -157,7 +155,7 @@ public class SearchProjectsActionIT {
   @DataProvider
   public static Object[][] new_software_quality_rating_metric_keys() {
     return new Object[][]{{NEW_SOFTWARE_QUALITY_MAINTAINABILITY_RATING_KEY}, {NEW_SOFTWARE_QUALITY_RELIABILITY_RATING_KEY},
-      {NEW_SOFTWARE_QUALITY_SECURITY_RATING_KEY}, {NEW_SOFTWARE_QUALITY_SECURITY_REVIEW_RATING_KEY}};
+      {NEW_SOFTWARE_QUALITY_SECURITY_RATING_KEY}};
   }
 
   @DataProvider
@@ -248,11 +246,9 @@ public class SearchProjectsActionIT {
       "new_software_quality_maintainability_rating",
       "new_software_quality_reliability_rating",
       "new_software_quality_security_rating",
-      "new_software_quality_security_review_rating",
       "software_quality_maintainability_rating",
       "software_quality_reliability_rating",
-      "software_quality_security_rating",
-      "software_quality_security_review_rating");
+      "software_quality_security_rating");
 
     Param asc = def.param("asc");
     assertThat(asc.defaultValue()).isEqualTo("true");
@@ -272,11 +268,9 @@ public class SearchProjectsActionIT {
       "new_software_quality_maintainability_rating",
       "new_software_quality_reliability_rating",
       "new_software_quality_security_rating",
-      "new_software_quality_security_review_rating",
       "software_quality_maintainability_rating",
       "software_quality_reliability_rating",
-      "software_quality_security_rating",
-      "software_quality_security_review_rating");
+      "software_quality_security_rating");
   }
 
   @Test
@@ -1005,6 +999,8 @@ public class SearchProjectsActionIT {
     insertProject(new Measure(ratingMetric, c -> c.setValue(1d)));
     insertProject(new Measure(ratingMetric, c -> c.setValue(1d)));
     insertProject(new Measure(ratingMetric, c -> c.setValue(3d)));
+    insertProject(new Measure(ratingMetric, c -> c.setValue(5d)));
+    insertProject(new Measure(ratingMetric, c -> c.setValue(5d)));
     index();
 
     SearchProjectsWsResponse result = call(request.setFacets(singletonList(ratingMetricKey)));
@@ -1018,7 +1014,8 @@ public class SearchProjectsActionIT {
         tuple("1", 2L),
         tuple("2", 0L),
         tuple("3", 1L),
-        tuple("4", 0L));
+        tuple("4", 0L),
+        tuple("5", 2L));
   }
 
   @Test
@@ -1068,7 +1065,8 @@ public class SearchProjectsActionIT {
         tuple("1", 2L),
         tuple("2", 0L),
         tuple("3", 1L),
-        tuple("4", 0L));
+        tuple("4", 0L),
+        tuple("5", 0L));
   }
 
   @Test
index 8a581d45c87f8f7ce839c6439ec7ba3f1bb20b2a..0b207e6a128b3dfebba889c95d01ea3b7a89ab0b 100644 (file)
@@ -40,6 +40,7 @@ import org.sonar.db.DbTester;
 import org.sonar.db.component.BranchType;
 import org.sonar.db.component.ComponentDto;
 import org.sonar.db.component.ProjectData;
+import org.sonar.db.issue.ImpactDto;
 import org.sonar.db.issue.IssueDto;
 import org.sonar.db.metric.MetricDto;
 import org.sonar.db.protobuf.DbIssues;
@@ -74,6 +75,7 @@ import static org.sonar.api.issue.Issue.STATUS_CLOSED;
 import static org.sonar.api.issue.Issue.STATUS_CONFIRMED;
 import static org.sonar.api.issue.Issue.STATUS_OPEN;
 import static org.sonar.api.issue.Issue.STATUS_RESOLVED;
+import static org.sonar.api.issue.impact.SoftwareQuality.MAINTAINABILITY;
 import static org.sonar.api.measures.CoreMetrics.ANALYSIS_FROM_SONARQUBE_9_4_KEY;
 import static org.sonar.api.utils.DateUtils.formatDateTime;
 import static org.sonar.api.utils.DateUtils.parseDate;
@@ -86,6 +88,7 @@ import static org.sonar.db.rule.RuleTesting.XOO_X1;
 import static org.sonar.db.rule.RuleTesting.XOO_X2;
 import static org.sonar.db.rule.RuleTesting.newRule;
 import static org.sonar.server.tester.UserSessionRule.standalone;
+import static org.sonarqube.ws.Common.Impact.newBuilder;
 
 @RunWith(DataProviderRunner.class)
 public class ListActionIT {
@@ -196,7 +199,8 @@ public class ListActionIT {
       .setMessageFormattings(DbIssues.MessageFormattings.newBuilder().addMessageFormatting(MESSAGE_FORMATTING).build())
       .setStatus(STATUS_OPEN)
       .setResolution(null)
-      .setSeverity("MAJOR")
+      .setSeverity("BLOCKER")
+      .replaceAllImpacts(List.of(new ImpactDto().setSoftwareQuality(MAINTAINABILITY).setSeverity(org.sonar.api.issue.impact.Severity.BLOCKER)))
       .setAuthorLogin("John")
       .setAssigneeUuid(simon.getUuid())
       .setTags(asList("bug", "owasp"))
@@ -217,12 +221,13 @@ public class ListActionIT {
       .extracting(
         Issue::getKey, Issue::getRule, Issue::getSeverity, Issue::getComponent, Issue::getResolution, Issue::getStatus, Issue::getMessage, Issue::getMessageFormattingsList,
         Issue::getEffort, Issue::getAssignee, Issue::getAuthor, Issue::getLine, Issue::getHash, Issue::getTagsList, Issue::getCreationDate, Issue::getUpdateDate,
-        Issue::getQuickFixAvailable, Issue::getCodeVariantsList)
+        Issue::getQuickFixAvailable, Issue::getCodeVariantsList, Issue::getImpactsList)
       .containsExactlyInAnyOrder(
-        tuple(issue.getKey(), rule.getKey().toString(), Severity.MAJOR, file.getKey(), "", STATUS_OPEN, "the message",
+        tuple(issue.getKey(), rule.getKey().toString(), Severity.BLOCKER, file.getKey(), "", STATUS_OPEN, "the message",
           MessageFormattingUtils.dbMessageFormattingListToWs(List.of(MESSAGE_FORMATTING)), "10min",
           simon.getLogin(), "John", 42, "a227e508d6646b55a086ee11d63b21e9", asList("bug", "owasp"), formatDateTime(issue.getIssueCreationDate()),
-          formatDateTime(issue.getIssueUpdateDate()), false, List.of("variant1", "variant2")));
+          formatDateTime(issue.getIssueUpdateDate()), false, List.of("variant1", "variant2"),
+          List.of(newBuilder().setSoftwareQuality(Common.SoftwareQuality.MAINTAINABILITY).setSeverity(Common.ImpactSeverity.ImpactSeverity_BLOCKER).build())));
 
     assertThat(response.getComponentsList())
       .extracting(
index 993d5b9abbfee7a283effe4fb277ecc2c36cbb8d..963d8e8771fb0a964baabddc49daed6d52e6f150 100644 (file)
@@ -28,12 +28,15 @@ import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.sonar.api.issue.Issue;
+import org.sonar.api.issue.impact.Severity;
+import org.sonar.api.issue.impact.SoftwareQuality;
 import org.sonar.api.resources.Qualifiers;
 import org.sonar.api.utils.System2;
 import org.sonar.db.DbTester;
 import org.sonar.db.component.ComponentDto;
 import org.sonar.db.component.ProjectData;
 import org.sonar.db.component.ResourceTypesRule;
+import org.sonar.db.issue.ImpactDto;
 import org.sonar.db.issue.IssueDbTester;
 import org.sonar.db.issue.IssueDto;
 import org.sonar.db.project.ProjectDto;
@@ -45,6 +48,7 @@ import org.sonar.db.user.UserDto;
 import org.sonar.server.component.ComponentFinder;
 import org.sonar.server.exceptions.ForbiddenException;
 import org.sonar.server.exceptions.NotFoundException;
+import org.sonar.server.issue.ImpactFormatter;
 import org.sonar.server.issue.TaintChecker;
 import org.sonar.server.issue.ws.pull.PullTaintActionProtobufObjectGenerator;
 import org.sonar.server.tester.UserSessionRule;
@@ -233,6 +237,8 @@ public class PullTaintActionIT {
       .setManualSeverity(true)
       .setMessage("message")
       .setMessageFormattings(DbIssues.MessageFormattings.newBuilder().addMessageFormatting(MESSAGE_FORMATTING).build())
+      .replaceAllImpacts(List.of(new ImpactDto().setSoftwareQuality(SoftwareQuality.MAINTAINABILITY).setSeverity(Severity.BLOCKER),
+        new ImpactDto().setSoftwareQuality(SoftwareQuality.RELIABILITY).setSeverity(Severity.HIGH)))
       .setIssueCreationTime(NOW)
       .setStatus(Issue.STATUS_OPEN)
       .setLocations(mainLocation.build())
@@ -265,7 +271,7 @@ public class PullTaintActionIT {
       .containsExactlyInAnyOrderElementsOf(issueDto.getEffectiveImpacts()
         .entrySet()
         .stream()
-        .map(entry -> tuple(Common.SoftwareQuality.valueOf(entry.getKey().name()), Common.ImpactSeverity.valueOf(entry.getValue().name())))
+        .map(entry -> tuple(Common.SoftwareQuality.valueOf(entry.getKey().name()), ImpactFormatter.mapImpactSeverity(entry.getValue())))
         .collect(toList()));
 
     Issues.Location location = taintLite.getMainLocation();
index 516107ac4c8c0633ca8679831a94090cf0bb739c..4f45dd7f18825336c624304febeff88fef8beb75 100644 (file)
@@ -164,18 +164,22 @@ public class SearchActionIT {
 
   private final DbClient dbClient = db.getDbClient();
   private final DbSession session = db.getSession();
-  private final IssueIndex issueIndex = new IssueIndex(es.client(), System2.INSTANCE, userSession, new WebAuthorizationTypeSupport(userSession));
+  private final IssueIndex issueIndex = new IssueIndex(es.client(), System2.INSTANCE, userSession,
+    new WebAuthorizationTypeSupport(userSession));
   private final IssueIndexer issueIndexer = new IssueIndexer(es.client(), dbClient, new IssueIteratorFactory(dbClient), null);
   private final IssueQueryFactory issueQueryFactory = new IssueQueryFactory(dbClient, Clock.systemUTC(), userSession);
   private final IssueFieldsSetter issueFieldsSetter = new IssueFieldsSetter();
   private final IssueWorkflow issueWorkflow = new IssueWorkflow(new FunctionExecutor(issueFieldsSetter), issueFieldsSetter);
-  private final SearchResponseLoader searchResponseLoader = new SearchResponseLoader(userSession, dbClient, new TransitionService(userSession, issueWorkflow));
+  private final SearchResponseLoader searchResponseLoader = new SearchResponseLoader(userSession, dbClient,
+    new TransitionService(userSession, issueWorkflow));
   private final Languages languages = new Languages();
   private final UserResponseFormatter userFormatter = new UserResponseFormatter(new AvatarResolverImpl());
-  private final SearchResponseFormat searchResponseFormat = new SearchResponseFormat(new Durations(), languages, new TextRangeResponseFormatter(), userFormatter);
+  private final SearchResponseFormat searchResponseFormat = new SearchResponseFormat(new Durations(), languages,
+    new TextRangeResponseFormatter(), userFormatter);
   private final IssueIndexSyncProgressChecker issueIndexSyncProgressChecker = new IssueIndexSyncProgressChecker(dbClient);
   private final WsActionTester ws = new WsActionTester(
-    new SearchAction(userSession, issueIndex, issueQueryFactory, issueIndexSyncProgressChecker, searchResponseLoader, searchResponseFormat, System2.INSTANCE, dbClient));
+    new SearchAction(userSession, issueIndex, issueQueryFactory, issueIndexSyncProgressChecker, searchResponseLoader,
+      searchResponseFormat, System2.INSTANCE, dbClient));
   private final PermissionIndexer permissionIndexer = new PermissionIndexer(dbClient, es.client(), issueIndexer);
 
   @Before
@@ -218,13 +222,16 @@ public class SearchActionIT {
 
     assertThat(response.getIssuesList())
       .extracting(
-        Issue::getKey, Issue::getRule, Issue::getSeverity, Issue::getComponent, Issue::getResolution, Issue::getStatus, Issue::getMessage, Issue::getMessageFormattingsList,
-        Issue::getEffort, Issue::getAssignee, Issue::getAuthor, Issue::getLine, Issue::getHash, Issue::getTagsList, Issue::getCreationDate, Issue::getUpdateDate,
+        Issue::getKey, Issue::getRule, Issue::getSeverity, Issue::getComponent, Issue::getResolution, Issue::getStatus, Issue::getMessage
+        , Issue::getMessageFormattingsList,
+        Issue::getEffort, Issue::getAssignee, Issue::getAuthor, Issue::getLine, Issue::getHash, Issue::getTagsList,
+        Issue::getCreationDate, Issue::getUpdateDate,
         Issue::getQuickFixAvailable, Issue::getCodeVariantsList)
       .containsExactlyInAnyOrder(
         tuple(issue.getKey(), rule.getKey().toString(), Severity.MAJOR, file.getKey(), RESOLUTION_FIXED, STATUS_RESOLVED, "the message",
           MessageFormattingUtils.dbMessageFormattingListToWs(List.of(MESSAGE_FORMATTING)), "10min",
-          simon.getLogin(), "John", 42, "a227e508d6646b55a086ee11d63b21e9", asList("bug", "owasp"), formatDateTime(issue.getIssueCreationDate()),
+          simon.getLogin(), "John", 42, "a227e508d6646b55a086ee11d63b21e9", asList("bug", "owasp"),
+          formatDateTime(issue.getIssueCreationDate()),
           formatDateTime(issue.getIssueUpdateDate()), false, List.of("variant1", "variant2")));
   }
 
@@ -250,7 +257,7 @@ public class SearchActionIT {
         .get(0)
         .getActions()
         .getActionsList())
-          .isEqualTo(asList(ACTION_SET_TAGS, COMMENT_KEY, ACTION_ASSIGN));
+      .isEqualTo(asList(ACTION_SET_TAGS, COMMENT_KEY, ACTION_ASSIGN));
 
     response = ws.newRequest()
       .setParam(PARAM_ADDITIONAL_FIELDS, "actions")
@@ -263,7 +270,7 @@ public class SearchActionIT {
         .get(0)
         .getActions()
         .getActionsList())
-          .isEqualTo(asList(ACTION_SET_TAGS, COMMENT_KEY));
+      .isEqualTo(asList(ACTION_SET_TAGS, COMMENT_KEY));
   }
 
   @Test
@@ -380,7 +387,8 @@ public class SearchActionIT {
     SearchWsResponse result = ws.newRequest().executeProtobuf(SearchWsResponse.class);
 
     assertThat(result.getIssuesCount()).isOne();
-    assertThat(result.getIssues(0).getFlows(0).getLocationsList()).extracting(Common.Location::getComponent, Common.Location::getMsg, Common.Location::getMsgFormattingsList)
+    assertThat(result.getIssues(0).getFlows(0).getLocationsList()).extracting(Common.Location::getComponent, Common.Location::getMsg,
+        Common.Location::getMsgFormattingsList)
       .containsExactlyInAnyOrder(
         tuple(file.getKey(), "FLOW MESSAGE", List.of()),
         tuple(anotherFile.getKey(), "ANOTHER FLOW MESSAGE", List.of(Common.MessageFormatting.newBuilder()
@@ -503,7 +511,8 @@ public class SearchActionIT {
       c -> c.setKey("PROJECT_KEY").setName("NAME_PROJECT_ID").setLongName("LONG_NAME_PROJECT_ID").setLanguage("java"));
     grantPermissionToAnyone(project.getProjectDto(), ISSUE_ADMIN);
     indexPermissions();
-    ComponentDto file = db.components().insertComponent(newFileDto(project.getMainBranchComponent(), null, "FILE_ID").setKey("FILE_KEY").setLanguage("js"));
+    ComponentDto file =
+      db.components().insertComponent(newFileDto(project.getMainBranchComponent(), null, "FILE_ID").setKey("FILE_KEY").setLanguage("js"));
 
     IssueDto issue = newIssue(newIssueRule(), project.getMainBranchComponent(), file)
       .setKee("82fd47d4-b650-4037-80bc-7b112bd4eac2")
@@ -613,7 +622,8 @@ public class SearchActionIT {
       c -> c.setKey("PROJECT_KEY").setName("NAME_PROJECT_ID").setLongName("LONG_NAME_PROJECT_ID").setLanguage("java")).getMainBranchComponent();
     ComponentDto file = db.components().insertComponent(newFileDto(project, null, "FILE_ID").setKey("FILE_KEY").setLanguage("java"));
     db.issues().insertIssue(rule, project, file, i -> i.setStatus(STATUS_OPEN));
-    IssueDto expectedIssue = db.issues().insertIssue(rule, project, file, i -> i.setStatus(STATUS_RESOLVED).setResolution(RESOLUTION_WONT_FIX));
+    IssueDto expectedIssue = db.issues().insertIssue(rule, project, file,
+      i -> i.setStatus(STATUS_RESOLVED).setResolution(RESOLUTION_WONT_FIX));
     db.issues().insertIssue(rule, project, file, i -> i.setStatus(STATUS_RESOLVED).setResolution(RESOLUTION_FALSE_POSITIVE));
     db.issues().insertIssue(rule, project, file, i -> i.setStatus(STATUS_RESOLVED).setResolution(RESOLUTION_FIXED));
     db.issues().insertIssue(rule, project, file, i -> i.setStatus(STATUS_CLOSED).setResolution(RESOLUTION_WONT_FIX));
@@ -728,14 +738,15 @@ public class SearchActionIT {
       .addImpact(new ImpactDto().setSoftwareQuality(SoftwareQuality.RELIABILITY).setSeverity(org.sonar.api.issue.impact.Severity.HIGH)));
     IssueDto issue3 = db.issues().insertIssue(rule, project, file, i -> i
       .addImpact(new ImpactDto().setSoftwareQuality(SoftwareQuality.SECURITY).setSeverity(org.sonar.api.issue.impact.Severity.MEDIUM))
-      .addImpact(new ImpactDto().setSoftwareQuality(SoftwareQuality.RELIABILITY).setSeverity(org.sonar.api.issue.impact.Severity.LOW)));
+      .addImpact(new ImpactDto().setSoftwareQuality(SoftwareQuality.RELIABILITY).setSeverity(org.sonar.api.issue.impact.Severity.INFO)));
     indexPermissionsAndIssues();
-    Map<Common.SoftwareQuality, Common.ImpactSeverity> expectedImpacts = Map.of(Common.SoftwareQuality.SECURITY, Common.ImpactSeverity.MEDIUM,
-      Common.SoftwareQuality.RELIABILITY, Common.ImpactSeverity.LOW,
+    Map<Common.SoftwareQuality, Common.ImpactSeverity> expectedImpacts = Map.of(Common.SoftwareQuality.SECURITY,
+      Common.ImpactSeverity.MEDIUM,
+      Common.SoftwareQuality.RELIABILITY, Common.ImpactSeverity.ImpactSeverity_INFO,
       Common.SoftwareQuality.MAINTAINABILITY, Common.ImpactSeverity.HIGH);
 
     SearchWsResponse response = ws.newRequest()
-      .setParam(PARAM_IMPACT_SEVERITIES, org.sonar.api.issue.impact.Severity.LOW.name())
+      .setParam(PARAM_IMPACT_SEVERITIES, org.sonar.api.issue.impact.Severity.INFO.name())
       .setParam(FACETS, PARAM_IMPACT_SOFTWARE_QUALITIES)
       .executeProtobuf(SearchWsResponse.class);
 
@@ -777,6 +788,9 @@ public class SearchActionIT {
       new ImpactDto(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.LOW),
       new ImpactDto(SoftwareQuality.SECURITY, org.sonar.api.issue.impact.Severity.MEDIUM),
       new ImpactDto(SoftwareQuality.RELIABILITY, org.sonar.api.issue.impact.Severity.LOW))));
+    IssueDto issue4 = db.issues().insertIssue(rule, project, file, i -> i.replaceAllImpacts(List.of(
+      new ImpactDto(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.INFO),
+      new ImpactDto(SoftwareQuality.SECURITY, org.sonar.api.issue.impact.Severity.BLOCKER))));
     indexPermissionsAndIssues();
 
     SearchWsResponse response = ws.newRequest()
@@ -785,7 +799,7 @@ public class SearchActionIT {
 
     assertThat(response.getIssuesList())
       .extracting(Issue::getKey)
-      .containsExactlyInAnyOrder(issue1.getKey(), issue2.getKey(), issue3.getKey());
+      .containsExactlyInAnyOrder(issue1.getKey(), issue2.getKey(), issue3.getKey(), issue4.getKey());
 
     Optional<Common.Facet> first = response.getFacets().getFacetsList()
       .stream().filter(facet -> facet.getProperty().equals(PARAM_IMPACT_SEVERITIES))
@@ -793,9 +807,11 @@ public class SearchActionIT {
     assertThat(first.get().getValuesList())
       .extracting(Common.FacetValue::getVal, Common.FacetValue::getCount)
       .containsExactlyInAnyOrder(
+        tuple("BLOCKER", 1L),
         tuple("HIGH", 2L),
         tuple("MEDIUM", 1L),
-        tuple("LOW", 1L));
+        tuple("LOW", 1L),
+        tuple("INFO", 1L));
   }
 
   @Test
@@ -809,7 +825,13 @@ public class SearchActionIT {
       new ImpactDto(SoftwareQuality.RELIABILITY, org.sonar.api.issue.impact.Severity.HIGH))));
     db.issues().insertIssue(rule, project, file, i -> i.replaceAllImpacts(List.of(
       new ImpactDto(SoftwareQuality.RELIABILITY, org.sonar.api.issue.impact.Severity.HIGH))));
+    IssueDto issue2 = db.issues().insertIssue(rule, project, file, i -> i.replaceAllImpacts(List.of(
+      new ImpactDto(SoftwareQuality.SECURITY, org.sonar.api.issue.impact.Severity.BLOCKER))));
     IssueDto issue3 = db.issues().insertIssue(rule, project, file, i -> i.replaceAllImpacts(List.of(
+      new ImpactDto(SoftwareQuality.SECURITY, org.sonar.api.issue.impact.Severity.BLOCKER))));
+    IssueDto issue4 = db.issues().insertIssue(rule, project, file, i -> i.replaceAllImpacts(List.of(
+      new ImpactDto(SoftwareQuality.SECURITY, org.sonar.api.issue.impact.Severity.INFO))));
+    IssueDto issue5 = db.issues().insertIssue(rule, project, file, i -> i.replaceAllImpacts(List.of(
       new ImpactDto(SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.LOW),
       new ImpactDto(SoftwareQuality.SECURITY, org.sonar.api.issue.impact.Severity.MEDIUM),
       new ImpactDto(SoftwareQuality.RELIABILITY, org.sonar.api.issue.impact.Severity.LOW))));
@@ -822,7 +844,7 @@ public class SearchActionIT {
 
     assertThat(response.getIssuesList())
       .extracting(Issue::getKey)
-      .containsExactlyInAnyOrder(issue1.getKey(), issue3.getKey());
+      .containsExactlyInAnyOrder(issue1.getKey(), issue2.getKey(), issue3.getKey(), issue4.getKey(), issue5.getKey());
 
     Optional<Common.Facet> first = response.getFacets().getFacetsList()
       .stream().filter(facet -> facet.getProperty().equals(PARAM_IMPACT_SEVERITIES))
@@ -832,7 +854,9 @@ public class SearchActionIT {
       .containsExactlyInAnyOrder(
         tuple("HIGH", 1L),
         tuple("MEDIUM", 1L),
-        tuple("LOW", 0L));
+        tuple("LOW", 0L),
+        tuple("INFO", 1L),
+        tuple("BLOCKER", 2L));
   }
 
   @Test
@@ -878,7 +902,8 @@ public class SearchActionIT {
   @Test
   public void issue_on_removed_file() {
     RuleDto rule = newIssueRule();
-    ComponentDto project = db.components().insertPublicProject("PROJECT_ID", c -> c.setKey("PROJECT_KEY").setKey("PROJECT_KEY")).getMainBranchComponent();
+    ComponentDto project =
+      db.components().insertPublicProject("PROJECT_ID", c -> c.setKey("PROJECT_KEY").setKey("PROJECT_KEY")).getMainBranchComponent();
     indexPermissions();
     ComponentDto removedFile = db.components().insertComponent(newFileDto(project).setUuid("REMOVED_FILE_ID")
       .setKey("REMOVED_FILE_KEY")
@@ -903,7 +928,8 @@ public class SearchActionIT {
   @Test
   public void apply_paging_with_one_component() {
     RuleDto rule = newIssueRule();
-    ComponentDto project = db.components().insertPublicProject("PROJECT_ID", c -> c.setKey("PROJECT_KEY").setKey("PROJECT_KEY")).getMainBranchComponent();
+    ComponentDto project =
+      db.components().insertPublicProject("PROJECT_ID", c -> c.setKey("PROJECT_KEY").setKey("PROJECT_KEY")).getMainBranchComponent();
     indexPermissions();
     ComponentDto file = db.components().insertComponent(newFileDto(project, null, "FILE_ID").setKey("FILE_KEY"));
     for (int i = 0; i < SearchOptions.MAX_PAGE_SIZE + 1; i++) {
@@ -921,7 +947,8 @@ public class SearchActionIT {
   public void filter_by_assigned_to_me() {
     UserDto alice = db.users().insertUser(u -> u.setLogin("alice").setName("Alice").setEmail("alice@email.com"));
     UserDto john = db.users().insertUser(u -> u.setLogin("john").setName("John").setEmail("john@email.com"));
-    ComponentDto project = db.components().insertPublicProject(c -> c.setUuid("PROJECT_ID").setKey("PROJECT_KEY").setBranchUuid("PROJECT_ID")).getMainBranchComponent();
+    ComponentDto project = db.components().insertPublicProject(c -> c.setUuid("PROJECT_ID").setKey("PROJECT_KEY").setBranchUuid(
+      "PROJECT_ID")).getMainBranchComponent();
     indexPermissions();
     ComponentDto file = db.components().insertComponent(newFileDto(project, null, "FILE_ID").setKey("FILE_KEY"));
     RuleDto rule = newIssueRule();
@@ -969,7 +996,7 @@ public class SearchActionIT {
     UserDto alice = db.users().insertUser(u -> u.setLogin("alice").setName("Alice").setEmail("alice@email.com"));
     ComponentDto project = db.components().insertPublicProject("PROJECT_ID",
       c -> c.setKey("PROJECT_KEY").setName("NAME_PROJECT_ID").setLongName("LONG_NAME_PROJECT_ID")).getMainBranchComponent();
-    SnapshotDto snapshotDto = db.components().insertSnapshot(project, s -> s.setLast(true).setPeriodDate(parseDateTime("2014-09-05T00:00:00+0100").getTime()));
+    db.components().insertSnapshot(project, s -> s.setLast(true).setPeriodDate(parseDateTime("2014-09-05T00:00:00+0100").getTime()));
     indexPermissions();
 
     ComponentDto file = db.components().insertComponent(newFileDto(project, null, "FILE_ID").setKey("FILE_KEY"));
@@ -1213,7 +1240,7 @@ public class SearchActionIT {
     assertThat(ws.newRequest()
       .setMultiParam("author", singletonList("unknown"))
       .executeProtobuf(SearchWsResponse.class).getIssuesList())
-        .isEmpty();
+      .isEmpty();
   }
 
   @Test
@@ -1362,7 +1389,8 @@ public class SearchActionIT {
 
     assertThat(parse.getAsJsonObject().get("issues").getAsJsonArray())
       .extracting(o -> o.getAsJsonObject().get("key").getAsString())
-      .containsExactly("82fd47d4-b650-4037-80bc-7b112bd4eac3", "82fd47d4-b650-4037-80bc-7b112bd4eac1", "82fd47d4-b650-4037-80bc-7b112bd4eac2");
+      .containsExactly("82fd47d4-b650-4037-80bc-7b112bd4eac3", "82fd47d4-b650-4037-80bc-7b112bd4eac1", "82fd47d4-b650-4037-80bc" +
+        "-7b112bd4eac2");
   }
 
   @Test
@@ -1372,12 +1400,15 @@ public class SearchActionIT {
     Consumer<RuleDto> ruleConsumer = ruleDefinitionDto -> ruleDefinitionDto
       .setSecurityStandards(Sets.newHashSet("cwe:20", "owaspTop10:a1", "pciDss-3.2:6.5.3", "owaspAsvs-4.0:12.3.1"))
       .setSystemTags(Sets.newHashSet("bad-practice", "cwe", "owasp-a1", "sans-top25-insecure", "sql"));
-    Consumer<IssueDto> issueConsumer = issueDto -> issueDto.setTags(Sets.newHashSet("bad-practice", "cwe", "owasp-a1", "sans-top25-insecure", "sql"));
+    Consumer<IssueDto> issueConsumer = issueDto -> issueDto.setTags(Sets.newHashSet("bad-practice", "cwe", "owasp-a1", "sans-top25" +
+      "-insecure", "sql"));
     RuleDto hotspotRule = db.rules().insertHotspotRule(ruleConsumer);
     db.issues().insertHotspot(hotspotRule, project, file, issueConsumer);
     RuleDto issueRule = db.rules().insertIssueRule(ruleConsumer);
-    IssueDto issueDto1 = db.issues().insertIssue(issueRule, project, file, issueConsumer, issueDto -> issueDto.setType(RuleType.VULNERABILITY));
-    IssueDto issueDto2 = db.issues().insertIssue(issueRule, project, file, issueConsumer, issueDto -> issueDto.setType(RuleType.VULNERABILITY));
+    IssueDto issueDto1 = db.issues().insertIssue(issueRule, project, file, issueConsumer,
+      issueDto -> issueDto.setType(RuleType.VULNERABILITY));
+    IssueDto issueDto2 = db.issues().insertIssue(issueRule, project, file, issueConsumer,
+      issueDto -> issueDto.setType(RuleType.VULNERABILITY));
     IssueDto issueDto3 = db.issues().insertIssue(issueRule, project, file, issueConsumer, issueDto -> issueDto.setType(CODE_SMELL));
     indexPermissionsAndIssues();
 
@@ -1402,13 +1433,15 @@ public class SearchActionIT {
   public void only_vulnerabilities_are_returned_by_owaspAsvs40_with_level() {
     ComponentDto project = db.components().insertPublicProject().getMainBranchComponent();
     ComponentDto file = db.components().insertComponent(newFileDto(project));
-    Consumer<IssueDto> issueConsumer = issueDto -> issueDto.setTags(Sets.newHashSet("bad-practice", "cwe", "owasp-a1", "sans-top25-insecure", "sql"));
+    Consumer<IssueDto> issueConsumer = issueDto -> issueDto.setTags(Sets.newHashSet("bad-practice", "cwe", "owasp-a1", "sans-top25" +
+      "-insecure", "sql"));
     RuleDto issueRule1 = db.rules().insertIssueRule(r -> r.setSecurityStandards(Set.of("owaspAsvs-4.0:1.7.2", "owaspAsvs-4.0:12.3.1")));
     RuleDto issueRule2 = db.rules().insertIssueRule(r -> r.setSecurityStandards(Set.of("owaspAsvs-4.0:2.2.5")));
     RuleDto issueRule3 = db.rules().insertIssueRule(r -> r.setSecurityStandards(Set.of("owaspAsvs-4.0:2.2.5", "owaspAsvs-4.0:12.1.3")));
-    IssueDto issueDto1 = db.issues().insertIssue(issueRule1, project, file, issueConsumer, issueDto -> issueDto.setType(RuleType.VULNERABILITY));
-    IssueDto issueDto2 = db.issues().insertIssue(issueRule2, project, file, issueConsumer, issueDto -> issueDto.setType(RuleType.VULNERABILITY));
-    IssueDto issueDto3 = db.issues().insertIssue(issueRule3, project, file, issueConsumer, issueDto -> issueDto.setType(RuleType.VULNERABILITY));
+    IssueDto issueDto1 = db.issues().insertIssue(issueRule1, project, file, issueConsumer,
+      issueDto -> issueDto.setType(RuleType.VULNERABILITY));
+    IssueDto issueDto3 = db.issues().insertIssue(issueRule3, project, file, issueConsumer,
+      issueDto -> issueDto.setType(RuleType.VULNERABILITY));
     indexPermissionsAndIssues();
 
     SearchWsResponse result = ws.newRequest()
@@ -1453,12 +1486,15 @@ public class SearchActionIT {
     Consumer<RuleDto> ruleConsumer = ruleDefinitionDto -> ruleDefinitionDto
       .setSecurityStandards(Sets.newHashSet("cwe:20", "owaspTop10:a1", "pciDss-3.2:6.5.3", "pciDss-3.2:10.1"))
       .setSystemTags(Sets.newHashSet("bad-practice", "cwe", "owasp-a1", "sans-top25-insecure", "sql"));
-    Consumer<IssueDto> issueConsumer = issueDto -> issueDto.setTags(Sets.newHashSet("bad-practice", "cwe", "owasp-a1", "sans-top25-insecure", "sql"));
+    Consumer<IssueDto> issueConsumer = issueDto -> issueDto.setTags(Sets.newHashSet("bad-practice", "cwe", "owasp-a1", "sans-top25" +
+      "-insecure", "sql"));
     RuleDto hotspotRule = db.rules().insertHotspotRule(ruleConsumer);
     db.issues().insertHotspot(hotspotRule, project, file, issueConsumer);
     RuleDto issueRule = db.rules().insertIssueRule(ruleConsumer);
-    IssueDto issueDto1 = db.issues().insertIssue(issueRule, project, file, issueConsumer, issueDto -> issueDto.setType(RuleType.VULNERABILITY));
-    IssueDto issueDto2 = db.issues().insertIssue(issueRule, project, file, issueConsumer, issueDto -> issueDto.setType(RuleType.VULNERABILITY));
+    IssueDto issueDto1 = db.issues().insertIssue(issueRule, project, file, issueConsumer,
+      issueDto -> issueDto.setType(RuleType.VULNERABILITY));
+    IssueDto issueDto2 = db.issues().insertIssue(issueRule, project, file, issueConsumer,
+      issueDto -> issueDto.setType(RuleType.VULNERABILITY));
     IssueDto issueDto3 = db.issues().insertIssue(issueRule, project, file, issueConsumer, issueDto -> issueDto.setType(CODE_SMELL));
     indexPermissionsAndIssues();
 
@@ -1488,12 +1524,15 @@ public class SearchActionIT {
     Consumer<RuleDto> ruleConsumer = ruleDefinitionDto -> ruleDefinitionDto
       .setSecurityStandards(Sets.newHashSet("cwe:20", "owaspTop10:a1", "pciDss-3.2:6.5.3", "pciDss-3.2:10.1"))
       .setSystemTags(Sets.newHashSet("bad-practice", "cwe", "owasp-a1", "sans-top25-insecure", "sql"));
-    Consumer<IssueDto> issueConsumer = issueDto -> issueDto.setTags(Sets.newHashSet("bad-practice", "cwe", "owasp-a1", "sans-top25-insecure", "sql"));
+    Consumer<IssueDto> issueConsumer = issueDto -> issueDto.setTags(Sets.newHashSet("bad-practice", "cwe", "owasp-a1", "sans-top25" +
+      "-insecure", "sql"));
     RuleDto hotspotRule = db.rules().insertHotspotRule(ruleConsumer);
     db.issues().insertHotspot(hotspotRule, project, file, issueConsumer);
     RuleDto issueRule = db.rules().insertIssueRule(ruleConsumer);
-    IssueDto issueDto1 = db.issues().insertIssue(issueRule, project, file, issueConsumer, issueDto -> issueDto.setType(RuleType.VULNERABILITY));
-    IssueDto issueDto2 = db.issues().insertIssue(issueRule, project, file, issueConsumer, issueDto -> issueDto.setType(RuleType.VULNERABILITY));
+    IssueDto issueDto1 = db.issues().insertIssue(issueRule, project, file, issueConsumer,
+      issueDto -> issueDto.setType(RuleType.VULNERABILITY));
+    IssueDto issueDto2 = db.issues().insertIssue(issueRule, project, file, issueConsumer,
+      issueDto -> issueDto.setType(RuleType.VULNERABILITY));
 
     // Rule 2
     ruleConsumer = ruleDefinitionDto -> ruleDefinitionDto
@@ -1503,8 +1542,10 @@ public class SearchActionIT {
     hotspotRule = db.rules().insertHotspotRule(ruleConsumer);
     db.issues().insertHotspot(hotspotRule, project, file, issueConsumer);
     issueRule = db.rules().insertIssueRule(ruleConsumer);
-    IssueDto issueDto3 = db.issues().insertIssue(issueRule, project, file, issueConsumer, issueDto -> issueDto.setType(RuleType.VULNERABILITY));
-    IssueDto issueDto4 = db.issues().insertIssue(issueRule, project, file, issueConsumer, issueDto -> issueDto.setType(RuleType.VULNERABILITY));
+    IssueDto issueDto3 = db.issues().insertIssue(issueRule, project, file, issueConsumer,
+      issueDto -> issueDto.setType(RuleType.VULNERABILITY));
+    IssueDto issueDto4 = db.issues().insertIssue(issueRule, project, file, issueConsumer,
+      issueDto -> issueDto.setType(RuleType.VULNERABILITY));
 
     // Rule 3
     ruleConsumer = ruleDefinitionDto -> ruleDefinitionDto
@@ -1514,8 +1555,10 @@ public class SearchActionIT {
     hotspotRule = db.rules().insertHotspotRule(ruleConsumer);
     db.issues().insertHotspot(hotspotRule, project, file, issueConsumer);
     issueRule = db.rules().insertIssueRule(ruleConsumer);
-    IssueDto issueDto5 = db.issues().insertIssue(issueRule, project, file, issueConsumer, issueDto -> issueDto.setType(RuleType.VULNERABILITY));
-    IssueDto issueDto6 = db.issues().insertIssue(issueRule, project, file, issueConsumer, issueDto -> issueDto.setType(RuleType.VULNERABILITY));
+    IssueDto issueDto5 = db.issues().insertIssue(issueRule, project, file, issueConsumer,
+      issueDto -> issueDto.setType(RuleType.VULNERABILITY));
+    IssueDto issueDto6 = db.issues().insertIssue(issueRule, project, file, issueConsumer,
+      issueDto -> issueDto.setType(RuleType.VULNERABILITY));
 
     indexPermissionsAndIssues();
 
@@ -1525,7 +1568,8 @@ public class SearchActionIT {
 
     assertThat(result.getIssuesList())
       .extracting(Issue::getKey)
-      .containsExactlyInAnyOrder(issueDto1.getKey(), issueDto2.getKey(), issueDto3.getKey(), issueDto4.getKey(), issueDto5.getKey(), issueDto6.getKey());
+      .containsExactlyInAnyOrder(issueDto1.getKey(), issueDto2.getKey(), issueDto3.getKey(), issueDto4.getKey(), issueDto5.getKey(),
+        issueDto6.getKey());
 
     result = ws.newRequest()
       .setParam("pciDss-3.2", "1")
@@ -1541,7 +1585,8 @@ public class SearchActionIT {
 
     assertThat(result.getIssuesList())
       .extracting(Issue::getKey)
-      .containsExactlyInAnyOrder(issueDto1.getKey(), issueDto2.getKey(), issueDto3.getKey(), issueDto4.getKey(), issueDto5.getKey(), issueDto6.getKey());
+      .containsExactlyInAnyOrder(issueDto1.getKey(), issueDto2.getKey(), issueDto3.getKey(), issueDto4.getKey(), issueDto5.getKey(),
+        issueDto6.getKey());
 
     result = ws.newRequest()
       .setParam("pciDss-3.2", "4")
@@ -1571,12 +1616,15 @@ public class SearchActionIT {
     Consumer<RuleDto> ruleConsumer = ruleDefinitionDto -> ruleDefinitionDto
       .setSecurityStandards(Sets.newHashSet("cwe:20", "owaspTop10:a1", "pciDss-4.0:6.5.3", "pciDss-4.0:10.1"))
       .setSystemTags(Sets.newHashSet("bad-practice", "cwe", "owasp-a1", "sans-top25-insecure", "sql"));
-    Consumer<IssueDto> issueConsumer = issueDto -> issueDto.setTags(Sets.newHashSet("bad-practice", "cwe", "owasp-a1", "sans-top25-insecure", "sql"));
+    Consumer<IssueDto> issueConsumer = issueDto -> issueDto.setTags(Sets.newHashSet("bad-practice", "cwe", "owasp-a1", "sans-top25" +
+      "-insecure", "sql"));
     RuleDto hotspotRule = db.rules().insertHotspotRule(ruleConsumer);
     db.issues().insertHotspot(hotspotRule, project, file, issueConsumer);
     RuleDto issueRule = db.rules().insertIssueRule(ruleConsumer);
-    IssueDto issueDto1 = db.issues().insertIssue(issueRule, project, file, issueConsumer, issueDto -> issueDto.setType(RuleType.VULNERABILITY));
-    IssueDto issueDto2 = db.issues().insertIssue(issueRule, project, file, issueConsumer, issueDto -> issueDto.setType(RuleType.VULNERABILITY));
+    IssueDto issueDto1 = db.issues().insertIssue(issueRule, project, file, issueConsumer,
+      issueDto -> issueDto.setType(RuleType.VULNERABILITY));
+    IssueDto issueDto2 = db.issues().insertIssue(issueRule, project, file, issueConsumer,
+      issueDto -> issueDto.setType(RuleType.VULNERABILITY));
     IssueDto issueDto3 = db.issues().insertIssue(issueRule, project, file, issueConsumer, issueDto -> issueDto.setType(CODE_SMELL));
     indexPermissionsAndIssues();
 
@@ -1606,12 +1654,15 @@ public class SearchActionIT {
     Consumer<RuleDto> ruleConsumer = ruleDefinitionDto -> ruleDefinitionDto
       .setSecurityStandards(Sets.newHashSet("cwe:20", "owaspTop10:a1", "pciDss-4.0:6.5.3", "pciDss-4.0:10.1"))
       .setSystemTags(Sets.newHashSet("bad-practice", "cwe", "owasp-a1", "sans-top25-insecure", "sql"));
-    Consumer<IssueDto> issueConsumer = issueDto -> issueDto.setTags(Sets.newHashSet("bad-practice", "cwe", "owasp-a1", "sans-top25-insecure", "sql"));
+    Consumer<IssueDto> issueConsumer = issueDto -> issueDto.setTags(Sets.newHashSet("bad-practice", "cwe", "owasp-a1", "sans-top25" +
+      "-insecure", "sql"));
     RuleDto hotspotRule = db.rules().insertHotspotRule(ruleConsumer);
     db.issues().insertHotspot(hotspotRule, project, file, issueConsumer);
     RuleDto issueRule = db.rules().insertIssueRule(ruleConsumer);
-    IssueDto issueDto1 = db.issues().insertIssue(issueRule, project, file, issueConsumer, issueDto -> issueDto.setType(RuleType.VULNERABILITY));
-    IssueDto issueDto2 = db.issues().insertIssue(issueRule, project, file, issueConsumer, issueDto -> issueDto.setType(RuleType.VULNERABILITY));
+    IssueDto issueDto1 = db.issues().insertIssue(issueRule, project, file, issueConsumer,
+      issueDto -> issueDto.setType(RuleType.VULNERABILITY));
+    IssueDto issueDto2 = db.issues().insertIssue(issueRule, project, file, issueConsumer,
+      issueDto -> issueDto.setType(RuleType.VULNERABILITY));
 
     // Rule 2
     ruleConsumer = ruleDefinitionDto -> ruleDefinitionDto
@@ -1621,8 +1672,10 @@ public class SearchActionIT {
     hotspotRule = db.rules().insertHotspotRule(ruleConsumer);
     db.issues().insertHotspot(hotspotRule, project, file, issueConsumer);
     issueRule = db.rules().insertIssueRule(ruleConsumer);
-    IssueDto issueDto3 = db.issues().insertIssue(issueRule, project, file, issueConsumer, issueDto -> issueDto.setType(RuleType.VULNERABILITY));
-    IssueDto issueDto4 = db.issues().insertIssue(issueRule, project, file, issueConsumer, issueDto -> issueDto.setType(RuleType.VULNERABILITY));
+    IssueDto issueDto3 = db.issues().insertIssue(issueRule, project, file, issueConsumer,
+      issueDto -> issueDto.setType(RuleType.VULNERABILITY));
+    IssueDto issueDto4 = db.issues().insertIssue(issueRule, project, file, issueConsumer,
+      issueDto -> issueDto.setType(RuleType.VULNERABILITY));
 
     // Rule 3
     ruleConsumer = ruleDefinitionDto -> ruleDefinitionDto
@@ -1632,8 +1685,8 @@ public class SearchActionIT {
     hotspotRule = db.rules().insertHotspotRule(ruleConsumer);
     db.issues().insertHotspot(hotspotRule, project, file, issueConsumer);
     issueRule = db.rules().insertIssueRule(ruleConsumer);
-    IssueDto issueDto5 = db.issues().insertIssue(issueRule, project, file, issueConsumer, issueDto -> issueDto.setType(RuleType.VULNERABILITY));
-    IssueDto issueDto6 = db.issues().insertIssue(issueRule, project, file, issueConsumer, issueDto -> issueDto.setType(RuleType.VULNERABILITY));
+    db.issues().insertIssue(issueRule, project, file, issueConsumer, issueDto -> issueDto.setType(RuleType.VULNERABILITY));
+    db.issues().insertIssue(issueRule, project, file, issueConsumer, issueDto -> issueDto.setType(RuleType.VULNERABILITY));
 
     indexPermissionsAndIssues();
 
@@ -1681,12 +1734,15 @@ public class SearchActionIT {
     Consumer<RuleDto> ruleConsumer = ruleDefinitionDto -> ruleDefinitionDto
       .setSecurityStandards(Sets.newHashSet("cwe:20", "cwe:564", "cwe:89", "cwe:943", "owaspTop10:a1"))
       .setSystemTags(Sets.newHashSet("bad-practice", "cwe", "owasp-a1", "sans-top25-insecure", "sql"));
-    Consumer<IssueDto> issueConsumer = issueDto -> issueDto.setTags(Sets.newHashSet("bad-practice", "cwe", "owasp-a1", "sans-top25-insecure", "sql"));
+    Consumer<IssueDto> issueConsumer = issueDto -> issueDto.setTags(Sets.newHashSet("bad-practice", "cwe", "owasp-a1", "sans-top25" +
+      "-insecure", "sql"));
     RuleDto hotspotRule = db.rules().insertHotspotRule(ruleConsumer);
     db.issues().insertHotspot(hotspotRule, project, file, issueConsumer);
     RuleDto issueRule = db.rules().insertIssueRule(ruleConsumer);
-    IssueDto issueDto1 = db.issues().insertIssue(issueRule, project, file, issueConsumer, issueDto -> issueDto.setType(RuleType.VULNERABILITY));
-    IssueDto issueDto2 = db.issues().insertIssue(issueRule, project, file, issueConsumer, issueDto -> issueDto.setType(RuleType.VULNERABILITY));
+    IssueDto issueDto1 = db.issues().insertIssue(issueRule, project, file, issueConsumer,
+      issueDto -> issueDto.setType(RuleType.VULNERABILITY));
+    IssueDto issueDto2 = db.issues().insertIssue(issueRule, project, file, issueConsumer,
+      issueDto -> issueDto.setType(RuleType.VULNERABILITY));
     IssueDto issueDto3 = db.issues().insertIssue(issueRule, project, file, issueConsumer, issueDto -> issueDto.setType(CODE_SMELL));
     indexPermissionsAndIssues();
 
@@ -1706,12 +1762,15 @@ public class SearchActionIT {
     Consumer<RuleDto> ruleConsumer = ruleDefinitionDto -> ruleDefinitionDto
       .setSecurityStandards(Sets.newHashSet("cwe:20", "cwe:564", "cwe:89", "cwe:943", "owaspTop10:a1", "owaspTop10-2021:a2"))
       .setSystemTags(Sets.newHashSet("bad-practice", "cwe", "owasp-a1", "sans-top25-insecure", "sql"));
-    Consumer<IssueDto> issueConsumer = issueDto -> issueDto.setTags(Sets.newHashSet("bad-practice", "cwe", "owasp-a1", "sans-top25-insecure", "sql"));
+    Consumer<IssueDto> issueConsumer = issueDto -> issueDto.setTags(Sets.newHashSet("bad-practice", "cwe", "owasp-a1", "sans-top25" +
+      "-insecure", "sql"));
     RuleDto hotspotRule = db.rules().insertHotspotRule(ruleConsumer);
     db.issues().insertHotspot(hotspotRule, project, file, issueConsumer);
     RuleDto issueRule = db.rules().insertIssueRule(ruleConsumer);
-    IssueDto issueDto1 = db.issues().insertIssue(issueRule, project, file, issueConsumer, issueDto -> issueDto.setType(RuleType.VULNERABILITY));
-    IssueDto issueDto2 = db.issues().insertIssue(issueRule, project, file, issueConsumer, issueDto -> issueDto.setType(RuleType.VULNERABILITY));
+    IssueDto issueDto1 = db.issues().insertIssue(issueRule, project, file, issueConsumer,
+      issueDto -> issueDto.setType(RuleType.VULNERABILITY));
+    IssueDto issueDto2 = db.issues().insertIssue(issueRule, project, file, issueConsumer,
+      issueDto -> issueDto.setType(RuleType.VULNERABILITY));
     IssueDto issueDto3 = db.issues().insertIssue(issueRule, project, file, issueConsumer, issueDto -> issueDto.setType(CODE_SMELL));
     indexPermissionsAndIssues();
 
@@ -1731,12 +1790,15 @@ public class SearchActionIT {
     Consumer<RuleDto> ruleConsumer = ruleDefinitionDto -> ruleDefinitionDto
       .setSecurityStandards(Sets.newHashSet("cwe:20", "cwe:564", "cwe:89", "cwe:943", "owaspTop10:a1", "owaspTop10-2021:a2"))
       .setSystemTags(Sets.newHashSet("bad-practice", "cwe", "owasp-a1", "sans-top25-insecure", "sql"));
-    Consumer<IssueDto> issueConsumer = issueDto -> issueDto.setTags(Sets.newHashSet("bad-practice", "cwe", "owasp-a1", "sans-top25-insecure", "sql"));
+    Consumer<IssueDto> issueConsumer = issueDto -> issueDto.setTags(Sets.newHashSet("bad-practice", "cwe", "owasp-a1", "sans-top25" +
+      "-insecure", "sql"));
     RuleDto hotspotRule = db.rules().insertHotspotRule(ruleConsumer);
     db.issues().insertHotspot(hotspotRule, project, file, issueConsumer);
     RuleDto issueRule = db.rules().insertIssueRule(ruleConsumer);
-    IssueDto issueDto1 = db.issues().insertIssue(issueRule, project, file, issueConsumer, issueDto -> issueDto.setType(RuleType.VULNERABILITY));
-    IssueDto issueDto2 = db.issues().insertIssue(issueRule, project, file, issueConsumer, issueDto -> issueDto.setType(RuleType.VULNERABILITY));
+    IssueDto issueDto1 = db.issues().insertIssue(issueRule, project, file, issueConsumer,
+      issueDto -> issueDto.setType(RuleType.VULNERABILITY));
+    IssueDto issueDto2 = db.issues().insertIssue(issueRule, project, file, issueConsumer,
+      issueDto -> issueDto.setType(RuleType.VULNERABILITY));
     IssueDto issueDto3 = db.issues().insertIssue(issueRule, project, file, issueConsumer, issueDto -> issueDto.setType(CODE_SMELL));
     indexPermissionsAndIssues();
 
@@ -1754,14 +1816,18 @@ public class SearchActionIT {
     ComponentDto project = db.components().insertPublicProject().getMainBranchComponent();
     ComponentDto file = db.components().insertComponent(newFileDto(project));
     Consumer<RuleDto> ruleConsumer = ruleDefinitionDto -> ruleDefinitionDto
-      .setSecurityStandards(Sets.newHashSet("cwe:20", "cwe:564", "stig-ASD_V5R3:V-222402", "stig-ASD_V5R3:V-222403", "stig-ASD_V5R3:V-222404", "ostig-ASD_V5R3:V-222405"))
+      .setSecurityStandards(Sets.newHashSet("cwe:20", "cwe:564", "stig-ASD_V5R3:V-222402", "stig-ASD_V5R3:V-222403", "stig-ASD_V5R3:V" +
+        "-222404", "ostig-ASD_V5R3:V-222405"))
       .setSystemTags(Sets.newHashSet("bad-practice", "cwe", "stig", "sans-top25-insecure", "sql"));
-    Consumer<IssueDto> issueConsumer = issueDto -> issueDto.setTags(Sets.newHashSet("bad-practice", "cwe", "stig", "sans-top25-insecure", "sql"));
+    Consumer<IssueDto> issueConsumer = issueDto -> issueDto.setTags(Sets.newHashSet("bad-practice", "cwe", "stig", "sans-top25-insecure",
+      "sql"));
     RuleDto hotspotRule = db.rules().insertHotspotRule(ruleConsumer);
     db.issues().insertHotspot(hotspotRule, project, file, issueConsumer);
     RuleDto issueRule = db.rules().insertIssueRule(ruleConsumer);
-    IssueDto issueDto1 = db.issues().insertIssue(issueRule, project, file, issueConsumer, issueDto -> issueDto.setType(RuleType.VULNERABILITY));
-    IssueDto issueDto2 = db.issues().insertIssue(issueRule, project, file, issueConsumer, issueDto -> issueDto.setType(RuleType.VULNERABILITY));
+    IssueDto issueDto1 = db.issues().insertIssue(issueRule, project, file, issueConsumer,
+      issueDto -> issueDto.setType(RuleType.VULNERABILITY));
+    IssueDto issueDto2 = db.issues().insertIssue(issueRule, project, file, issueConsumer,
+      issueDto -> issueDto.setType(RuleType.VULNERABILITY));
     db.issues().insertIssue(issueRule, project, file, issueConsumer, issueDto -> issueDto.setType(CODE_SMELL));
     indexPermissionsAndIssues();
 
@@ -1790,8 +1856,10 @@ public class SearchActionIT {
     RuleDto hotspotRule = db.rules().insertHotspotRule(ruleConsumer);
     db.issues().insertHotspot(hotspotRule, project, file, issueConsumer);
     RuleDto issueRule = db.rules().insertIssueRule(ruleConsumer);
-    IssueDto issueDto1 = db.issues().insertIssue(issueRule, project, file, issueConsumer, issueDto -> issueDto.setType(RuleType.VULNERABILITY));
-    IssueDto issueDto2 = db.issues().insertIssue(issueRule, project, file, issueConsumer, issueDto -> issueDto.setType(RuleType.VULNERABILITY));
+    IssueDto issueDto1 = db.issues().insertIssue(issueRule, project, file, issueConsumer,
+      issueDto -> issueDto.setType(RuleType.VULNERABILITY));
+    IssueDto issueDto2 = db.issues().insertIssue(issueRule, project, file, issueConsumer,
+      issueDto -> issueDto.setType(RuleType.VULNERABILITY));
     db.issues().insertIssue(issueRule, project, file, issueConsumer, issueDto -> issueDto.setType(CODE_SMELL));
     indexPermissionsAndIssues();
 
@@ -1830,8 +1898,10 @@ public class SearchActionIT {
     RuleDto hotspotRule = db.rules().insertHotspotRule(ruleConsumer);
     db.issues().insertHotspot(hotspotRule, project, file, issueConsumer);
     RuleDto issueRule = db.rules().insertIssueRule(ruleConsumer);
-    IssueDto issueDto1 = db.issues().insertIssue(issueRule, project, file, issueConsumer, issueDto -> issueDto.setType(RuleType.VULNERABILITY));
-    IssueDto issueDto2 = db.issues().insertIssue(issueRule, project, file, issueConsumer, issueDto -> issueDto.setType(RuleType.VULNERABILITY));
+    IssueDto issueDto1 = db.issues().insertIssue(issueRule, project, file, issueConsumer,
+      issueDto -> issueDto.setType(RuleType.VULNERABILITY));
+    IssueDto issueDto2 = db.issues().insertIssue(issueRule, project, file, issueConsumer,
+      issueDto -> issueDto.setType(RuleType.VULNERABILITY));
     IssueDto issueDto3 = db.issues().insertIssue(issueRule, project, file, issueConsumer, issueDto -> issueDto.setType(CODE_SMELL));
     indexPermissionsAndIssues();
 
@@ -1855,8 +1925,10 @@ public class SearchActionIT {
     RuleDto hotspotRule = db.rules().insertHotspotRule(ruleConsumer);
     db.issues().insertHotspot(hotspotRule, project, file, issueConsumer);
     RuleDto issueRule = db.rules().insertIssueRule(ruleConsumer);
-    IssueDto issueDto1 = db.issues().insertIssue(issueRule, project, file, issueConsumer, issueDto -> issueDto.setType(RuleType.VULNERABILITY));
-    IssueDto issueDto2 = db.issues().insertIssue(issueRule, project, file, issueConsumer, issueDto -> issueDto.setType(RuleType.VULNERABILITY));
+    IssueDto issueDto1 = db.issues().insertIssue(issueRule, project, file, issueConsumer,
+      issueDto -> issueDto.setType(RuleType.VULNERABILITY));
+    IssueDto issueDto2 = db.issues().insertIssue(issueRule, project, file, issueConsumer,
+      issueDto -> issueDto.setType(RuleType.VULNERABILITY));
     IssueDto issueDto3 = db.issues().insertIssue(issueRule, project, file, issueConsumer, issueDto -> issueDto.setType(CODE_SMELL));
     indexPermissionsAndIssues();
 
@@ -1900,7 +1972,8 @@ public class SearchActionIT {
     indexPermissionsAndIssues();
 
     SearchWsResponse result = ws.newRequest()
-      .setParam("issues", Stream.of(bugIssue, vulnerabilityIssue, codeSmellIssue, hotspot).map(IssueDto::getKey).collect(Collectors.joining(",")))
+      .setParam("issues",
+        Stream.of(bugIssue, vulnerabilityIssue, codeSmellIssue, hotspot).map(IssueDto::getKey).collect(Collectors.joining(",")))
       .executeProtobuf(SearchWsResponse.class);
 
     assertThat(result.getIssuesList())
@@ -1915,12 +1988,15 @@ public class SearchActionIT {
     Consumer<RuleDto> ruleConsumer = ruleDefinitionDto -> ruleDefinitionDto
       .setSecurityStandards(Sets.newHashSet("cwe:20", "cwe:564", "cwe:89", "cwe:943", "owaspTop10:a1"))
       .setSystemTags(Sets.newHashSet("bad-practice", "cwe", "owasp-a1", "sans-top25-insecure", "sql"));
-    Consumer<IssueDto> issueConsumer = issueDto -> issueDto.setTags(Sets.newHashSet("bad-practice", "cwe", "owasp-a1", "sans-top25-insecure", "sql"));
+    Consumer<IssueDto> issueConsumer = issueDto -> issueDto.setTags(Sets.newHashSet("bad-practice", "cwe", "owasp-a1", "sans-top25" +
+      "-insecure", "sql"));
     RuleDto hotspotRule = db.rules().insertHotspotRule(ruleConsumer);
     db.issues().insertHotspot(hotspotRule, project, file, issueConsumer);
     RuleDto issueRule = db.rules().insertIssueRule(ruleConsumer);
-    IssueDto issueDto1 = db.issues().insertIssue(issueRule, project, file, issueConsumer, issueDto -> issueDto.setType(RuleType.VULNERABILITY));
-    IssueDto issueDto2 = db.issues().insertIssue(issueRule, project, file, issueConsumer, issueDto -> issueDto.setType(RuleType.VULNERABILITY));
+    IssueDto issueDto1 = db.issues().insertIssue(issueRule, project, file, issueConsumer,
+      issueDto -> issueDto.setType(RuleType.VULNERABILITY));
+    IssueDto issueDto2 = db.issues().insertIssue(issueRule, project, file, issueConsumer,
+      issueDto -> issueDto.setType(RuleType.VULNERABILITY));
     indexPermissions();
     indexIssues();
 
@@ -2014,8 +2090,10 @@ public class SearchActionIT {
     ComponentDto file = db.components().insertComponent(newFileDto(project));
     RuleDto issueRule = db.rules().insertIssueRule();
     IssueDto bugIssue = db.issues().insertIssue(issueRule, project, file, i -> i.setType(RuleType.BUG).setSeverity(Severity.MAJOR.name()));
-    IssueDto vulnerabilityIssue = db.issues().insertIssue(issueRule, project, file, i -> i.setType(RuleType.VULNERABILITY).setSeverity(Severity.MAJOR.name()));
-    IssueDto codeSmellIssue = db.issues().insertIssue(issueRule, project, file, i -> i.setType(CODE_SMELL).setSeverity(Severity.MAJOR.name()));
+    IssueDto vulnerabilityIssue = db.issues().insertIssue(issueRule, project, file,
+      i -> i.setType(RuleType.VULNERABILITY).setSeverity(Severity.MAJOR.name()));
+    IssueDto codeSmellIssue = db.issues().insertIssue(issueRule, project, file,
+      i -> i.setType(CODE_SMELL).setSeverity(Severity.MAJOR.name()));
     RuleDto hotspotRule = db.rules().insertHotspotRule();
     db.issues().insertHotspot(hotspotRule, project, file, i -> i.setSeverity(Severity.MAJOR.name()));
     indexPermissions();
@@ -2152,8 +2230,10 @@ public class SearchActionIT {
     assertThat(def.params()).extracting("key").containsExactlyInAnyOrder(
       "additionalFields", "asc", "assigned", "assignees", "author", "components", "branch", "pullRequest", "createdAfter", "createdAt",
       "createdBefore", "createdInLast", "directories", "facets", "files", "issues", "scopes", "languages", "onComponentOnly",
-      "p", "projects", "ps", "resolutions", "resolved", "rules", "s", "severities", "statuses", "tags", "types", "pciDss-3.2", "pciDss-4.0", "owaspAsvs-4.0",
-      "owaspAsvsLevel", "owaspTop10", "owaspTop10-2021", "stig-ASD_V5R3", "casa", "sansTop25", "cwe", "sonarsourceSecurity", "timeZone", "inNewCodePeriod", "codeVariants",
+      "p", "projects", "ps", "resolutions", "resolved", "rules", "s", "severities", "statuses", "tags", "types", "pciDss-3.2", "pciDss-4" +
+        ".0", "owaspAsvs-4.0",
+      "owaspAsvsLevel", "owaspTop10", "owaspTop10-2021", "stig-ASD_V5R3", "casa", "sansTop25", "cwe", "sonarsourceSecurity", "timeZone",
+      "inNewCodePeriod", "codeVariants",
       "cleanCodeAttributeCategories", "impactSeverities", "impactSoftwareQualities", "issueStatuses", "fixedInPullRequest",
       "prioritizedRule");
 
@@ -2163,8 +2243,10 @@ public class SearchActionIT {
     assertThat(branch.since()).isEqualTo("6.6");
 
     WebService.Param projectUuids = def.param("projects");
-    assertThat(projectUuids.description()).isEqualTo("To retrieve issues associated to a specific list of projects (comma-separated list of project keys). " +
-      "This parameter is mostly used by the Issues page, please prefer usage of the componentKeys parameter. If this parameter is set, projectUuids must not be set.");
+    assertThat(projectUuids.description()).isEqualTo("To retrieve issues associated to a specific list of projects (comma-separated list " +
+      "of project keys). " +
+      "This parameter is mostly used by the Issues page, please prefer usage of the componentKeys parameter. If this parameter is set, " +
+      "projectUuids must not be set.");
   }
 
   @Test
@@ -2391,9 +2473,9 @@ public class SearchActionIT {
   private RuleDto newIssueRule(String ruleKey, Consumer<RuleDto> consumer) {
     RuleDto rule = newRule(RuleKey.of("xoo", ruleKey),
       createDefaultRuleDescriptionSection(uuidFactory.create(), "Rule desc"))
-        .setLanguage("xoo")
-        .setName("Rule name")
-        .setStatus(RuleStatus.READY);
+      .setLanguage("xoo")
+      .setName("Rule name")
+      .setStatus(RuleStatus.READY);
     consumer.accept(rule);
     db.rules().insert(rule);
     return rule;
index 0575fd65d47876e1cc659396ec34f6bfc3ebf97b..ebb4b714130354f38a0aefedca1f55f194fb4078 100644 (file)
@@ -31,12 +31,12 @@ import org.sonar.core.util.SequenceUuidFactory;
 import org.sonar.core.util.UuidFactory;
 import org.sonar.db.DbTester;
 import org.sonar.db.rule.RuleDto;
+import org.sonar.server.common.rule.RuleCreator;
 import org.sonar.server.common.rule.service.RuleService;
 import org.sonar.server.common.text.MacroInterpreter;
 import org.sonar.server.es.EsTester;
 import org.sonar.server.exceptions.ForbiddenException;
 import org.sonar.server.exceptions.UnauthorizedException;
-import org.sonar.server.common.rule.RuleCreator;
 import org.sonar.server.rule.RuleDescriptionFormatter;
 import org.sonar.server.rule.index.RuleIndexer;
 import org.sonar.server.tester.UserSessionRule;
@@ -166,7 +166,7 @@ public class CreateActionIT {
       .setParam("markdownDescription", "Description")
       .setParam("status", "BETA")
       .setParam("cleanCodeAttribute", "MODULAR")
-      .setParam("impacts", "RELIABILITY=HIGH;SECURITY=LOW")
+      .setParam("impacts", "RELIABILITY=BLOCKER;SECURITY=INFO")
       .execute().getInput();
 
     String expectedResult = """
@@ -176,7 +176,7 @@ public class CreateActionIT {
           "repo": "java",
           "name": "My custom rule",
           "htmlDesc": "Description",
-          "severity": "MINOR",
+          "severity": "INFO",
           "status": "BETA",
           "type": "VULNERABILITY",
           "internalKey": "configKey_S001",
@@ -188,11 +188,11 @@ public class CreateActionIT {
           "impacts": [
             {
               "softwareQuality": "RELIABILITY",
-              "severity": "HIGH"
+              "severity": "BLOCKER"
             },
             {
               "softwareQuality": "SECURITY",
-              "severity": "LOW"
+              "severity": "INFO"
             }
           ]
         }
index cb8219e5d2e748f650889058cf710820a9fbf48a..768a7225065cc3b9505a9a4dd5a11ab6e83cf55e 100644 (file)
@@ -499,7 +499,8 @@ public class SearchActionIT {
     db.rules().insert(
       r -> r.replaceAllDefaultImpacts(List.of(new ImpactDto().setSoftwareQuality(SoftwareQuality.MAINTAINABILITY).setSeverity(Severity.HIGH))));
     db.rules().insert(
-      r -> r.replaceAllDefaultImpacts(List.of(new ImpactDto().setSoftwareQuality(SoftwareQuality.RELIABILITY).setSeverity(Severity.MEDIUM))));
+      r -> r.replaceAllDefaultImpacts(List.of(new ImpactDto().setSoftwareQuality(SoftwareQuality.RELIABILITY).setSeverity(Severity.MEDIUM),
+        new ImpactDto().setSoftwareQuality(SoftwareQuality.MAINTAINABILITY).setSeverity(Severity.INFO))));
     indexRules();
 
     SearchResponse result = ws.newRequest()
@@ -507,7 +508,7 @@ public class SearchActionIT {
       .setParam("impactSoftwareQualities", SoftwareQuality.MAINTAINABILITY.name())
       .executeProtobuf(SearchResponse.class);
     assertThat(result.getFacets().getFacets(0).getValuesList()).extracting(v -> entry(v.getVal(), v.getCount()))
-      .contains(entry(Severity.HIGH.name(), 1L), entry(Severity.MEDIUM.name(), 0L), entry(Severity.LOW.name(), 0L));
+      .contains(entry(Severity.HIGH.name(), 1L), entry(Severity.MEDIUM.name(), 0L), entry(Severity.LOW.name(), 0L), entry(Severity.INFO.name(), 1L));
   }
 
   @Test
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ImpactFormatter.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ImpactFormatter.java
new file mode 100644 (file)
index 0000000..3d01812
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * 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.server.issue;
+
+import org.sonar.api.issue.impact.Severity;
+import org.sonarqube.ws.Common;
+
+public class ImpactFormatter {
+  private ImpactFormatter() {
+  }
+
+  public static Common.ImpactSeverity mapImpactSeverity(Severity severity) {
+    return switch (severity) {
+      case BLOCKER -> Common.ImpactSeverity.ImpactSeverity_BLOCKER;
+      case HIGH -> Common.ImpactSeverity.HIGH;
+      case MEDIUM -> Common.ImpactSeverity.MEDIUM;
+      case LOW -> Common.ImpactSeverity.LOW;
+      case INFO -> Common.ImpactSeverity.ImpactSeverity_INFO;
+    };
+  }
+}
index e309eed6fae5a2e9c018a84416a10f22bd14c6e6..49e9e0a6cb0e6a22536b7778c1748a9e1a43373c 100644 (file)
@@ -49,6 +49,7 @@ import org.sonar.db.rule.RuleDto;
 import org.sonar.db.user.UserDto;
 import org.sonar.markdown.Markdown;
 import org.sonar.server.es.Facets;
+import org.sonar.server.issue.ImpactFormatter;
 import org.sonar.server.issue.TextRangeResponseFormatter;
 import org.sonar.server.issue.index.IssueScope;
 import org.sonar.server.issue.workflow.Transition;
@@ -184,7 +185,7 @@ public class SearchResponseFormat {
       .stream()
       .map(entry -> Common.Impact.newBuilder()
         .setSoftwareQuality(Common.SoftwareQuality.valueOf(entry.getKey().name()))
-        .setSeverity(Common.ImpactSeverity.valueOf(entry.getValue().name()))
+        .setSeverity(ImpactFormatter.mapImpactSeverity(entry.getValue()))
         .build())
       .toList());
 
index 18850ad9883be8b23eddaa5ae60772ed7ad583b4..a31082b669336516e69ed8283449cdb9d5851121 100644 (file)
@@ -34,6 +34,7 @@ import org.sonar.db.component.ComponentDto;
 import org.sonar.db.issue.IssueDto;
 import org.sonar.db.protobuf.DbIssues;
 import org.sonar.db.rule.RuleDto;
+import org.sonar.server.issue.ImpactFormatter;
 import org.sonar.server.user.UserSession;
 import org.sonar.server.ws.MessageFormattingUtils;
 import org.sonarqube.ws.Common;
@@ -108,7 +109,7 @@ public class PullTaintActionProtobufObjectGenerator implements ProtobufObjectGen
     taintBuilder.addAllImpacts(issueDto.getEffectiveImpacts().entrySet()
       .stream().map(entry -> Common.Impact.newBuilder()
         .setSoftwareQuality(Common.SoftwareQuality.valueOf(entry.getKey().name()))
-        .setSeverity(Common.ImpactSeverity.valueOf(entry.getValue().name()))
+        .setSeverity(ImpactFormatter.mapImpactSeverity(entry.getValue()))
         .build())
       .toList());
 
index 3f5871bd5fd3463df3c057992ad783b9f6ffb7d2..b857f21915dae0d6c7a21f020339e5a1d9bf9a41 100644 (file)
@@ -50,7 +50,6 @@ import static org.sonar.core.metric.SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALIT
 import static org.sonar.core.metric.SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_RELIABILITY_REMEDIATION_EFFORT;
 import static org.sonar.core.metric.SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_SECURITY_RATING;
 import static org.sonar.core.metric.SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_SECURITY_REMEDIATION_EFFORT;
-import static org.sonar.core.metric.SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_SECURITY_REVIEW_RATING;
 import static org.sonar.core.metric.SoftwareQualitiesMetrics.SOFTWARE_QUALITY_MAINTAINABILITY_DEBT_RATIO;
 import static org.sonar.core.metric.SoftwareQualitiesMetrics.SOFTWARE_QUALITY_MAINTAINABILITY_RATING;
 import static org.sonar.core.metric.SoftwareQualitiesMetrics.SOFTWARE_QUALITY_MAINTAINABILITY_REMEDIATION_EFFORT;
@@ -58,11 +57,9 @@ import static org.sonar.core.metric.SoftwareQualitiesMetrics.SOFTWARE_QUALITY_RE
 import static org.sonar.core.metric.SoftwareQualitiesMetrics.SOFTWARE_QUALITY_RELIABILITY_REMEDIATION_EFFORT;
 import static org.sonar.core.metric.SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_RATING;
 import static org.sonar.core.metric.SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_REMEDIATION_EFFORT;
-import static org.sonar.core.metric.SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_REVIEW_RATING;
 import static org.sonar.server.measure.Rating.RATING_BY_SEVERITY;
 import static org.sonar.server.measure.Rating.RATING_BY_SOFTWARE_QUALITY_SEVERITY;
 import static org.sonar.server.metric.IssueCountMetrics.PRIORITIZED_RULE_ISSUES;
-import static org.sonar.server.security.SecurityReviewRating.computeAToDRating;
 import static org.sonar.server.security.SecurityReviewRating.computePercent;
 import static org.sonar.server.security.SecurityReviewRating.computeRating;
 
@@ -311,13 +308,13 @@ public class MeasureUpdateFormulaFactoryImpl implements MeasureUpdateFormulaFact
       asList(NEW_SOFTWARE_QUALITY_MAINTAINABILITY_REMEDIATION_EFFORT, CoreMetrics.NEW_DEVELOPMENT_COST)),
 
     new MeasureUpdateFormula(SOFTWARE_QUALITY_MAINTAINABILITY_RATING, false, true,
-      (context, issues) -> context.setValue(context.getDebtRatingGrid().getAToDRatingForDensity(debtDensity(SOFTWARE_QUALITY_MAINTAINABILITY_REMEDIATION_EFFORT, context))),
-      (context, issues) -> context.setValue(context.getDebtRatingGrid().getAToDRatingForDensity(debtDensity(SOFTWARE_QUALITY_MAINTAINABILITY_REMEDIATION_EFFORT, context))),
+      (context, issues) -> context.setValue(context.getDebtRatingGrid().getRatingForDensity(debtDensity(SOFTWARE_QUALITY_MAINTAINABILITY_REMEDIATION_EFFORT, context))),
+      (context, issues) -> context.setValue(context.getDebtRatingGrid().getRatingForDensity(debtDensity(SOFTWARE_QUALITY_MAINTAINABILITY_REMEDIATION_EFFORT, context))),
       asList(SOFTWARE_QUALITY_MAINTAINABILITY_REMEDIATION_EFFORT, CoreMetrics.DEVELOPMENT_COST)),
 
     new MeasureUpdateFormula(NEW_SOFTWARE_QUALITY_MAINTAINABILITY_RATING, true, true,
-      (context, formula) -> context.setValue(context.getDebtRatingGrid().getAToDRatingForDensity(newDebtDensity(NEW_SOFTWARE_QUALITY_MAINTAINABILITY_REMEDIATION_EFFORT, context))),
-      (context, issues) -> context.setValue(context.getDebtRatingGrid().getAToDRatingForDensity(newDebtDensity(NEW_SOFTWARE_QUALITY_MAINTAINABILITY_REMEDIATION_EFFORT, context))),
+      (context, formula) -> context.setValue(context.getDebtRatingGrid().getRatingForDensity(newDebtDensity(NEW_SOFTWARE_QUALITY_MAINTAINABILITY_REMEDIATION_EFFORT, context))),
+      (context, issues) -> context.setValue(context.getDebtRatingGrid().getRatingForDensity(newDebtDensity(NEW_SOFTWARE_QUALITY_MAINTAINABILITY_REMEDIATION_EFFORT, context))),
       asList(NEW_SOFTWARE_QUALITY_MAINTAINABILITY_REMEDIATION_EFFORT, CoreMetrics.NEW_DEVELOPMENT_COST)),
 
     new MeasureUpdateFormula(EFFORT_TO_REACH_SOFTWARE_QUALITY_MAINTAINABILITY_RATING_A, false, true,
@@ -355,20 +352,6 @@ public class MeasureUpdateFormulaFactoryImpl implements MeasureUpdateFormulaFact
           .map(RATING_BY_SOFTWARE_QUALITY_SEVERITY::get)
           .orElse(Rating.A);
         context.setValue(rating);
-      }),
-
-    new MeasureUpdateFormula(SOFTWARE_QUALITY_SECURITY_REVIEW_RATING, false, true,
-      (context, formula) -> context.setValue(computeAToDRating(context.getValue(SECURITY_HOTSPOTS_REVIEWED).orElse(null))),
-      (context, issues) -> {
-        Optional<Double> percent = computePercent(issues.countHotspotsByStatus(Issue.STATUS_TO_REVIEW, false), issues.countHotspotsByStatus(Issue.STATUS_REVIEWED, false));
-        context.setValue(computeAToDRating(percent.orElse(null)));
-      }),
-
-    new MeasureUpdateFormula(NEW_SOFTWARE_QUALITY_SECURITY_REVIEW_RATING, true, true,
-      (context, formula) -> context.setValue(computeAToDRating(context.getValue(NEW_SECURITY_HOTSPOTS_REVIEWED).orElse(null))),
-      (context, issues) -> {
-        Optional<Double> percent = computePercent(issues.countHotspotsByStatus(Issue.STATUS_TO_REVIEW, true), issues.countHotspotsByStatus(Issue.STATUS_REVIEWED, true));
-        context.setValue(computeAToDRating(percent.orElse(null)));
       }));
 
   private static final Set<Metric> FORMULA_METRICS = MeasureUpdateFormulaFactory.extractMetrics(FORMULAS);
index e72cd6362b8fa43eddf7faa29718d4dd5826ee1e..16a11c4b5a5b34f9509921dcb673c4e840c1260a 100644 (file)
@@ -57,7 +57,6 @@ public class MeasuresWsModule extends Module {
       SoftwareQualitiesMetrics.SOFTWARE_QUALITY_MAINTAINABILITY_RATING,
       SoftwareQualitiesMetrics.SOFTWARE_QUALITY_RELIABILITY_RATING,
       SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_RATING,
-      SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_REVIEW_RATING,
       SoftwareQualitiesMetrics.SOFTWARE_QUALITY_MAINTAINABILITY_REMEDIATION_EFFORT,
       SoftwareQualitiesMetrics.SOFTWARE_QUALITY_RELIABILITY_REMEDIATION_EFFORT,
       SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_REMEDIATION_EFFORT,
@@ -67,7 +66,6 @@ public class MeasuresWsModule extends Module {
       SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_MAINTAINABILITY_RATING,
       SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_RELIABILITY_RATING,
       SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_SECURITY_RATING,
-      SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_SECURITY_REVIEW_RATING,
       SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_MAINTAINABILITY_REMEDIATION_EFFORT,
       SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_RELIABILITY_REMEDIATION_EFFORT,
       SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_SECURITY_REMEDIATION_EFFORT)
index d4f1ca3f818811d10ed5b423c242c21caa2cf6e7..5f695661ae895b0419c918fc66e72e9cdcfbe50b 100644 (file)
@@ -45,6 +45,7 @@ import org.sonar.db.rule.RuleParamDto;
 import org.sonar.db.user.UserDto;
 import org.sonar.markdown.Markdown;
 import org.sonar.server.common.text.MacroInterpreter;
+import org.sonar.server.issue.ImpactFormatter;
 import org.sonar.server.rule.RuleDescriptionFormatter;
 import org.sonar.server.rule.ws.RulesResponseFormatter.SearchResult;
 import org.sonarqube.ws.Common;
@@ -164,7 +165,7 @@ public class RuleMapper {
   }
 
   private static Common.Impact toImpact(ImpactDto impactDto) {
-    Common.ImpactSeverity severity = Common.ImpactSeverity.valueOf(impactDto.getSeverity().name());
+    Common.ImpactSeverity severity = ImpactFormatter.mapImpactSeverity(impactDto.getSeverity());
     Common.SoftwareQuality softwareQuality = Common.SoftwareQuality.valueOf(impactDto.getSoftwareQuality().name());
     return Common.Impact.newBuilder().setSeverity(severity).setSoftwareQuality(softwareQuality).build();
   }
diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/issue/ImpactFormatterTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/issue/ImpactFormatterTest.java
new file mode 100644 (file)
index 0000000..3a801c4
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * 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.server.issue;
+
+import org.junit.jupiter.api.Test;
+import org.sonar.api.issue.impact.Severity;
+import org.sonarqube.ws.Common;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class ImpactFormatterTest {
+
+  @Test
+  void mapImpactSeverity_shouldReturnExpectedValue() {
+    assertEquals(Common.ImpactSeverity.ImpactSeverity_BLOCKER, ImpactFormatter.mapImpactSeverity(Severity.BLOCKER));
+    assertEquals(Common.ImpactSeverity.HIGH, ImpactFormatter.mapImpactSeverity(Severity.HIGH));
+    assertEquals(Common.ImpactSeverity.MEDIUM, ImpactFormatter.mapImpactSeverity(Severity.MEDIUM));
+    assertEquals(Common.ImpactSeverity.LOW, ImpactFormatter.mapImpactSeverity(Severity.LOW));
+    assertEquals(Common.ImpactSeverity.ImpactSeverity_INFO, ImpactFormatter.mapImpactSeverity(Severity.INFO));
+  }
+}
index 1eb1e92a3774388218dfd403f3df1c063bafc12f..8d8d29c8a9eee7d5d6caa4c4fa6402bdf28497df 100644 (file)
@@ -52,7 +52,9 @@ import org.sonar.server.measure.Rating;
 import static java.util.Arrays.asList;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.junit.jupiter.params.provider.Arguments.arguments;
+import static org.sonar.api.issue.impact.Severity.BLOCKER;
 import static org.sonar.api.issue.impact.Severity.HIGH;
+import static org.sonar.api.issue.impact.Severity.INFO;
 import static org.sonar.api.issue.impact.Severity.LOW;
 import static org.sonar.api.issue.impact.Severity.MEDIUM;
 import static org.sonar.api.issue.impact.SoftwareQuality.MAINTAINABILITY;
@@ -167,12 +169,6 @@ class MeasureUpdateFormulaFactoryImplTest {
 
   @Test
   void computeHierarchy_shouldRecomputeSoftwareQualityMetricsCombiningOtherMetrics() {
-    new HierarchyTester(SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_REVIEW_RATING)
-      .withValue(SECURITY_HOTSPOTS_REVIEWED, 0d)
-      .expectedRating(Rating.D);
-    new HierarchyTester(SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_SECURITY_REVIEW_RATING)
-      .withValue(NEW_SECURITY_HOTSPOTS_REVIEWED, 0d)
-      .expectedRating(Rating.D);
     new HierarchyTester(SoftwareQualitiesMetrics.SOFTWARE_QUALITY_MAINTAINABILITY_DEBT_RATIO)
       .withValue(SOFTWARE_QUALITY_MAINTAINABILITY_REMEDIATION_EFFORT, 10d)
       .withValue(CoreMetrics.DEVELOPMENT_COST, "40")
@@ -187,11 +183,26 @@ class MeasureUpdateFormulaFactoryImplTest {
       .withValue(CoreMetrics.DEVELOPMENT_COST, "40")
       .expectedRating(Rating.D);
 
+    new HierarchyTester(SoftwareQualitiesMetrics.SOFTWARE_QUALITY_MAINTAINABILITY_RATING)
+      .withValue(SoftwareQualitiesMetrics.SOFTWARE_QUALITY_MAINTAINABILITY_REMEDIATION_EFFORT, 21d)
+      .withValue(CoreMetrics.DEVELOPMENT_COST, "40")
+      .expectedRating(Rating.E);
+
+    new HierarchyTester(SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_MAINTAINABILITY_RATING)
+      .withValue(SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_MAINTAINABILITY_REMEDIATION_EFFORT, 1d)
+      .withValue(CoreMetrics.NEW_DEVELOPMENT_COST, 40d)
+      .expectedRating(Rating.A);
+
     new HierarchyTester(SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_MAINTAINABILITY_RATING)
       .withValue(SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_MAINTAINABILITY_REMEDIATION_EFFORT, 10d)
       .withValue(CoreMetrics.NEW_DEVELOPMENT_COST, 40d)
       .expectedRating(Rating.D);
 
+    new HierarchyTester(SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_MAINTAINABILITY_RATING)
+      .withValue(SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_MAINTAINABILITY_REMEDIATION_EFFORT, 21d)
+      .withValue(CoreMetrics.NEW_DEVELOPMENT_COST, 40d)
+      .expectedRating(Rating.E);
+
     new HierarchyTester(SoftwareQualitiesMetrics.EFFORT_TO_REACH_SOFTWARE_QUALITY_MAINTAINABILITY_RATING_A)
       .withValue(SoftwareQualitiesMetrics.SOFTWARE_QUALITY_MAINTAINABILITY_REMEDIATION_EFFORT, 10d)
       .withValue(CoreMetrics.DEVELOPMENT_COST, "40")
@@ -264,7 +275,7 @@ class MeasureUpdateFormulaFactoryImplTest {
       newResolvedGroup(RuleType.BUG).setCount(7),
       // not bugs
       newGroup(RuleType.CODE_SMELL).setCount(11))
-        .assertThatValueIs(CoreMetrics.BUGS, 3 + 5);
+      .assertThatValueIs(CoreMetrics.BUGS, 3 + 5);
   }
 
   @Test
@@ -277,7 +288,7 @@ class MeasureUpdateFormulaFactoryImplTest {
       newResolvedGroup(RuleType.CODE_SMELL).setCount(7),
       // not code smells
       newGroup(RuleType.BUG).setCount(11))
-        .assertThatValueIs(CoreMetrics.CODE_SMELLS, 3 + 5);
+      .assertThatValueIs(CoreMetrics.CODE_SMELLS, 3 + 5);
   }
 
   @Test
@@ -290,7 +301,7 @@ class MeasureUpdateFormulaFactoryImplTest {
       newResolvedGroup(RuleType.VULNERABILITY).setCount(7),
       // not vulnerabilities
       newGroup(RuleType.BUG).setCount(11))
-        .assertThatValueIs(CoreMetrics.VULNERABILITIES, 3 + 5);
+      .assertThatValueIs(CoreMetrics.VULNERABILITIES, 3 + 5);
   }
 
   @Test
@@ -303,7 +314,7 @@ class MeasureUpdateFormulaFactoryImplTest {
       newResolvedGroup(RuleType.SECURITY_HOTSPOT).setCount(7),
       // not hotspots
       newGroup(RuleType.BUG).setCount(11))
-        .assertThatValueIs(CoreMetrics.SECURITY_HOTSPOTS, 3 + 5);
+      .assertThatValueIs(CoreMetrics.SECURITY_HOTSPOTS, 3 + 5);
   }
 
   @Test
@@ -311,18 +322,15 @@ class MeasureUpdateFormulaFactoryImplTest {
     with(
       newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED).setCount(3),
       newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW).setCount(1))
-        .assertThatValueIs(SECURITY_REVIEW_RATING, Rating.B)
-        .assertThatValueIs(SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_REVIEW_RATING, Rating.B);
+      .assertThatValueIs(SECURITY_REVIEW_RATING, Rating.B);
 
     withNoIssues()
-      .assertThatValueIs(SECURITY_REVIEW_RATING, Rating.A)
-      .assertThatValueIs(SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_REVIEW_RATING, Rating.A);
+      .assertThatValueIs(SECURITY_REVIEW_RATING, Rating.A);
 
     with(
       newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW).setCount(3).setInLeak(true),
       newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW).setCount(1).setInLeak(true))
-        .assertThatValueIs(SECURITY_REVIEW_RATING, Rating.E)
-        .assertThatValueIs(SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_REVIEW_RATING, Rating.D);
+      .assertThatValueIs(SECURITY_REVIEW_RATING, Rating.E);
   }
 
   @Test
@@ -330,7 +338,7 @@ class MeasureUpdateFormulaFactoryImplTest {
     with(
       newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED).setCount(3),
       newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW).setCount(1))
-        .assertThatValueIs(SECURITY_HOTSPOTS_REVIEWED, 75.0);
+      .assertThatValueIs(SECURITY_HOTSPOTS_REVIEWED, 75.0);
 
     withNoIssues()
       .assertNoValue(SECURITY_HOTSPOTS_REVIEWED);
@@ -341,7 +349,7 @@ class MeasureUpdateFormulaFactoryImplTest {
     with(
       newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED).setCount(3),
       newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW).setCount(1))
-        .assertThatValueIs(CoreMetrics.SECURITY_HOTSPOTS_REVIEWED_STATUS, 3.0);
+      .assertThatValueIs(CoreMetrics.SECURITY_HOTSPOTS_REVIEWED_STATUS, 3.0);
 
     withNoIssues()
       .assertThatValueIs(CoreMetrics.SECURITY_HOTSPOTS_REVIEWED_STATUS, 0.0);
@@ -352,7 +360,7 @@ class MeasureUpdateFormulaFactoryImplTest {
     with(
       newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_REVIEWED).setCount(3),
       newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW).setCount(1))
-        .assertThatValueIs(CoreMetrics.SECURITY_HOTSPOTS_TO_REVIEW_STATUS, 1.0);
+      .assertThatValueIs(CoreMetrics.SECURITY_HOTSPOTS_TO_REVIEW_STATUS, 1.0);
 
     withNoIssues()
       .assertThatValueIs(CoreMetrics.SECURITY_HOTSPOTS_TO_REVIEW_STATUS, 0.0);
@@ -380,11 +388,11 @@ class MeasureUpdateFormulaFactoryImplTest {
       newResolvedGroup(RuleType.VULNERABILITY).setSeverity(Severity.INFO).setCount(17),
       newResolvedGroup(RuleType.BUG).setSeverity(Severity.MAJOR).setCount(19),
       newResolvedGroup(RuleType.SECURITY_HOTSPOT).setSeverity(Severity.INFO).setCount(21))
-        .assertThatValueIs(CoreMetrics.BLOCKER_VIOLATIONS, 11 + 13)
-        .assertThatValueIs(CoreMetrics.CRITICAL_VIOLATIONS, 7)
-        .assertThatValueIs(CoreMetrics.MAJOR_VIOLATIONS, 3 + 5)
-        .assertThatValueIs(CoreMetrics.MINOR_VIOLATIONS, 0)
-        .assertThatValueIs(CoreMetrics.INFO_VIOLATIONS, 0);
+      .assertThatValueIs(CoreMetrics.BLOCKER_VIOLATIONS, 11 + 13)
+      .assertThatValueIs(CoreMetrics.CRITICAL_VIOLATIONS, 7)
+      .assertThatValueIs(CoreMetrics.MAJOR_VIOLATIONS, 3 + 5)
+      .assertThatValueIs(CoreMetrics.MINOR_VIOLATIONS, 0)
+      .assertThatValueIs(CoreMetrics.INFO_VIOLATIONS, 0);
   }
 
   @Test
@@ -404,8 +412,8 @@ class MeasureUpdateFormulaFactoryImplTest {
       // exclude unresolved
       newGroup(RuleType.VULNERABILITY).setCount(17),
       newGroup(RuleType.BUG).setCount(19))
-        .assertThatValueIs(CoreMetrics.FALSE_POSITIVE_ISSUES, 5)
-        .assertThatValueIs(CoreMetrics.ACCEPTED_ISSUES, 7 + 11);
+      .assertThatValueIs(CoreMetrics.FALSE_POSITIVE_ISSUES, 5)
+      .assertThatValueIs(CoreMetrics.ACCEPTED_ISSUES, 7 + 11);
   }
 
   @Test
@@ -424,9 +432,9 @@ class MeasureUpdateFormulaFactoryImplTest {
       // exclude security hotspot
       newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_OPEN).setCount(12),
       newResolvedGroup(Issue.RESOLUTION_FALSE_POSITIVE, Issue.STATUS_CLOSED).setCount(13))
-        .assertThatValueIs(CoreMetrics.CONFIRMED_ISSUES, 3 + 5)
-        .assertThatValueIs(CoreMetrics.OPEN_ISSUES, 9 + 11)
-        .assertThatValueIs(CoreMetrics.REOPENED_ISSUES, 7);
+      .assertThatValueIs(CoreMetrics.CONFIRMED_ISSUES, 3 + 5)
+      .assertThatValueIs(CoreMetrics.OPEN_ISSUES, 9 + 11)
+      .assertThatValueIs(CoreMetrics.REOPENED_ISSUES, 7);
   }
 
   @Test
@@ -443,7 +451,7 @@ class MeasureUpdateFormulaFactoryImplTest {
       newGroup(RuleType.BUG).setEffort(7.0),
       // exclude resolved
       newResolvedGroup(RuleType.CODE_SMELL).setEffort(17.0))
-        .assertThatValueIs(CoreMetrics.TECHNICAL_DEBT, 3.0 + 5.0);
+      .assertThatValueIs(CoreMetrics.TECHNICAL_DEBT, 3.0 + 5.0);
   }
 
   @Test
@@ -457,7 +465,7 @@ class MeasureUpdateFormulaFactoryImplTest {
       newGroup(RuleType.CODE_SMELL).setEffort(7.0),
       // exclude resolved
       newResolvedGroup(RuleType.BUG).setEffort(17.0))
-        .assertThatValueIs(CoreMetrics.RELIABILITY_REMEDIATION_EFFORT, 3.0 + 5.0);
+      .assertThatValueIs(CoreMetrics.RELIABILITY_REMEDIATION_EFFORT, 3.0 + 5.0);
   }
 
   @Test
@@ -471,19 +479,21 @@ class MeasureUpdateFormulaFactoryImplTest {
       newGroup(RuleType.CODE_SMELL).setEffort(7.0),
       // exclude resolved
       newResolvedGroup(RuleType.VULNERABILITY).setEffort(17.0))
-        .assertThatValueIs(CoreMetrics.SECURITY_REMEDIATION_EFFORT, 3.0 + 5.0);
+      .assertThatValueIs(CoreMetrics.SECURITY_REMEDIATION_EFFORT, 3.0 + 5.0);
   }
 
   private static Stream<Arguments> maintainabilityMetrics() {
     return Stream.of(
       arguments(TECHNICAL_DEBT, SQALE_DEBT_RATIO, SQALE_RATING),
-      arguments(SoftwareQualitiesMetrics.SOFTWARE_QUALITY_MAINTAINABILITY_REMEDIATION_EFFORT, SoftwareQualitiesMetrics.SOFTWARE_QUALITY_MAINTAINABILITY_DEBT_RATIO,
+      arguments(SoftwareQualitiesMetrics.SOFTWARE_QUALITY_MAINTAINABILITY_REMEDIATION_EFFORT,
+        SoftwareQualitiesMetrics.SOFTWARE_QUALITY_MAINTAINABILITY_DEBT_RATIO,
         SoftwareQualitiesMetrics.SOFTWARE_QUALITY_MAINTAINABILITY_RATING));
   }
 
   @ParameterizedTest
   @MethodSource("maintainabilityMetrics")
-  void test_sqale_debt_ratio_and_sqale_rating(Metric<?> maintainabilityRemediationEffortMetric, Metric<?> maintainabilityDebtRatioMetric, Metric<?> maintainabilityRatingMetric) {
+  void test_sqale_debt_ratio_and_sqale_rating(Metric<?> maintainabilityRemediationEffortMetric, Metric<?> maintainabilityDebtRatioMetric,
+    Metric<?> maintainabilityRatingMetric) {
     withNoIssues()
       .assertThatValueIs(maintainabilityDebtRatioMetric, 0)
       .assertThatValueIs(maintainabilityRatingMetric, Rating.A);
@@ -519,8 +529,8 @@ class MeasureUpdateFormulaFactoryImplTest {
       .andText(CoreMetrics.DEVELOPMENT_COST, "10")
       .assertThatValueIs(maintainabilityDebtRatioMetric, 200.0);
     switch (maintainabilityRatingMetric.key()) {
-      case SoftwareQualitiesMetrics.SOFTWARE_QUALITY_MAINTAINABILITY_RATING_KEY -> verifier.assertThatValueIs(maintainabilityRatingMetric, Rating.D);
-      case SQALE_RATING_KEY -> verifier.assertThatValueIs(maintainabilityRatingMetric, Rating.E);
+      case SoftwareQualitiesMetrics.SOFTWARE_QUALITY_MAINTAINABILITY_RATING_KEY, SQALE_RATING_KEY ->
+        verifier.assertThatValueIs(maintainabilityRatingMetric, Rating.E);
       default -> throw new IllegalArgumentException("Unexpected metric: " + maintainabilityRatingMetric.key());
     }
 
@@ -619,14 +629,14 @@ class MeasureUpdateFormulaFactoryImplTest {
       newGroup(RuleType.BUG).setSeverity(Severity.MINOR).setCount(5),
       // excluded, not a bug
       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.BLOCKER).setCount(3))
-        // highest severity of bugs is CRITICAL --> D
-        .assertThatValueIs(CoreMetrics.RELIABILITY_RATING, Rating.D);
+      // highest severity of bugs is CRITICAL --> D
+      .assertThatValueIs(CoreMetrics.RELIABILITY_RATING, Rating.D);
 
     with(
       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.MAJOR).setCount(3),
       newGroup(RuleType.VULNERABILITY).setSeverity(Severity.CRITICAL).setCount(5))
-        // no bugs --> A
-        .assertThatValueIs(CoreMetrics.RELIABILITY_RATING, Rating.A);
+      // no bugs --> A
+      .assertThatValueIs(CoreMetrics.RELIABILITY_RATING, Rating.A);
   }
 
   @Test
@@ -634,16 +644,28 @@ class MeasureUpdateFormulaFactoryImplTest {
     withNoIssues()
       .assertThatValueIs(SoftwareQualitiesMetrics.SOFTWARE_QUALITY_RELIABILITY_RATING, Rating.A);
 
+    with(
+      newImpactGroup(RELIABILITY, BLOCKER, 1))
+      .assertThatValueIs(SoftwareQualitiesMetrics.SOFTWARE_QUALITY_RELIABILITY_RATING, Rating.E);
+
+    with(
+      newImpactGroup(RELIABILITY, HIGH, 1))
+      .assertThatValueIs(SoftwareQualitiesMetrics.SOFTWARE_QUALITY_RELIABILITY_RATING, Rating.D);
+
     with(
       newImpactGroup(MAINTAINABILITY, HIGH, 1),
       newImpactGroup(RELIABILITY, MEDIUM, 1),
       newImpactGroup(RELIABILITY, LOW, 1),
       newImpactGroup(SECURITY, MEDIUM, 1))
-        .assertThatValueIs(SoftwareQualitiesMetrics.SOFTWARE_QUALITY_RELIABILITY_RATING, Rating.C);
+      .assertThatValueIs(SoftwareQualitiesMetrics.SOFTWARE_QUALITY_RELIABILITY_RATING, Rating.C);
 
     with(
       newImpactGroup(RELIABILITY, LOW, 1))
-        .assertThatValueIs(SoftwareQualitiesMetrics.SOFTWARE_QUALITY_RELIABILITY_RATING, Rating.B);
+      .assertThatValueIs(SoftwareQualitiesMetrics.SOFTWARE_QUALITY_RELIABILITY_RATING, Rating.B);
+
+    with(
+      newImpactGroup(RELIABILITY, INFO, 1))
+      .assertThatValueIs(SoftwareQualitiesMetrics.SOFTWARE_QUALITY_RELIABILITY_RATING, Rating.A);
   }
 
   @Test
@@ -651,18 +673,32 @@ class MeasureUpdateFormulaFactoryImplTest {
     withNoIssues()
       .assertThatLeakValueIs(SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_RELIABILITY_RATING, Rating.A);
 
+    with(
+      newImpactGroup(RELIABILITY, BLOCKER, 1, true))
+      .assertThatLeakValueIs(SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_RELIABILITY_RATING, Rating.E);
+
+    with(
+      newImpactGroup(RELIABILITY, HIGH, 1, true),
+      newImpactGroup(RELIABILITY, BLOCKER, 1, false))
+      .assertThatLeakValueIs(SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_RELIABILITY_RATING, Rating.D);
+
     with(
       newImpactGroup(MAINTAINABILITY, HIGH, 1, true),
       newImpactGroup(RELIABILITY, MEDIUM, 1, true),
       newImpactGroup(RELIABILITY, LOW, 1, true),
       newImpactGroup(RELIABILITY, HIGH, 1, false),
       newImpactGroup(SECURITY, HIGH, 1, true))
-        .assertThatLeakValueIs(SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_RELIABILITY_RATING, Rating.C);
+      .assertThatLeakValueIs(SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_RELIABILITY_RATING, Rating.C);
 
     with(
       newImpactGroup(RELIABILITY, LOW, 1, true),
       newImpactGroup(RELIABILITY, MEDIUM, 1, false))
-        .assertThatLeakValueIs(SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_RELIABILITY_RATING, Rating.B);
+      .assertThatLeakValueIs(SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_RELIABILITY_RATING, Rating.B);
+
+    with(
+      newImpactGroup(RELIABILITY, INFO, 1, true),
+      newImpactGroup(RELIABILITY, MEDIUM, 1, false))
+      .assertThatLeakValueIs(SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_RELIABILITY_RATING, Rating.A);
   }
 
   @Test
@@ -675,14 +711,14 @@ class MeasureUpdateFormulaFactoryImplTest {
       newGroup(RuleType.VULNERABILITY).setSeverity(Severity.MINOR).setCount(5),
       // excluded, not a vulnerability
       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.BLOCKER).setCount(3))
-        // highest severity of vulnerabilities is CRITICAL --> D
-        .assertThatValueIs(CoreMetrics.SECURITY_RATING, Rating.D);
+      // highest severity of vulnerabilities is CRITICAL --> D
+      .assertThatValueIs(CoreMetrics.SECURITY_RATING, Rating.D);
 
     with(
       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.MAJOR).setCount(3),
       newGroup(RuleType.BUG).setSeverity(Severity.CRITICAL).setCount(5))
-        // no vulnerabilities --> A
-        .assertThatValueIs(CoreMetrics.SECURITY_RATING, Rating.A);
+      // no vulnerabilities --> A
+      .assertThatValueIs(CoreMetrics.SECURITY_RATING, Rating.A);
   }
 
   @Test
@@ -690,16 +726,28 @@ class MeasureUpdateFormulaFactoryImplTest {
     withNoIssues()
       .assertThatValueIs(SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_RATING, Rating.A);
 
+    with(
+      newImpactGroup(SECURITY, BLOCKER, 1))
+      .assertThatValueIs(SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_RATING, Rating.E);
+
+    with(
+      newImpactGroup(SECURITY, HIGH, 1))
+      .assertThatValueIs(SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_RATING, Rating.D);
+
     with(
       newImpactGroup(MAINTAINABILITY, HIGH, 1),
       newImpactGroup(SECURITY, MEDIUM, 1),
       newImpactGroup(SECURITY, LOW, 1),
       newImpactGroup(RELIABILITY, MEDIUM, 1))
-        .assertThatValueIs(SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_RATING, Rating.C);
+      .assertThatValueIs(SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_RATING, Rating.C);
 
     with(
       newImpactGroup(SECURITY, LOW, 1))
-        .assertThatValueIs(SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_RATING, Rating.B);
+      .assertThatValueIs(SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_RATING, Rating.B);
+
+    with(
+      newImpactGroup(SECURITY, INFO, 2))
+      .assertThatValueIs(SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_RATING, Rating.A);
   }
 
   @Test
@@ -707,18 +755,32 @@ class MeasureUpdateFormulaFactoryImplTest {
     withNoIssues()
       .assertThatLeakValueIs(SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_SECURITY_RATING, Rating.A);
 
+    with(
+      newImpactGroup(SECURITY, BLOCKER, 1, true))
+      .assertThatLeakValueIs(SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_SECURITY_RATING, Rating.E);
+
+    with(
+      newImpactGroup(SECURITY, HIGH, 1, true),
+      newImpactGroup(SECURITY, BLOCKER, 1, false))
+      .assertThatLeakValueIs(SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_SECURITY_RATING, Rating.D);
+
     with(
       newImpactGroup(MAINTAINABILITY, HIGH, 1, true),
       newImpactGroup(SECURITY, MEDIUM, 1, true),
       newImpactGroup(SECURITY, LOW, 1, true),
       newImpactGroup(SECURITY, HIGH, 1, false),
       newImpactGroup(RELIABILITY, HIGH, 1, true))
-        .assertThatLeakValueIs(SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_SECURITY_RATING, Rating.C);
+      .assertThatLeakValueIs(SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_SECURITY_RATING, Rating.C);
 
     with(
       newImpactGroup(SECURITY, LOW, 1, true),
       newImpactGroup(SECURITY, MEDIUM, 1, false))
-        .assertThatLeakValueIs(SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_SECURITY_RATING, Rating.B);
+      .assertThatLeakValueIs(SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_SECURITY_RATING, Rating.B);
+
+    with(
+      newImpactGroup(SECURITY, INFO, 1, true),
+      newImpactGroup(SECURITY, MEDIUM, 1, false))
+      .assertThatLeakValueIs(SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_SECURITY_RATING, Rating.A);
   }
 
   @Test
@@ -732,7 +794,7 @@ class MeasureUpdateFormulaFactoryImplTest {
       // not bugs
       newGroup(RuleType.CODE_SMELL).setInLeak(true).setCount(9),
       newGroup(RuleType.VULNERABILITY).setInLeak(true).setCount(11))
-        .assertThatLeakValueIs(CoreMetrics.NEW_BUGS, 5 + 7);
+      .assertThatLeakValueIs(CoreMetrics.NEW_BUGS, 5 + 7);
 
   }
 
@@ -747,7 +809,7 @@ class MeasureUpdateFormulaFactoryImplTest {
       // not code smells
       newGroup(RuleType.BUG).setInLeak(true).setCount(9),
       newGroup(RuleType.VULNERABILITY).setInLeak(true).setCount(11))
-        .assertThatLeakValueIs(CoreMetrics.NEW_CODE_SMELLS, 5 + 7);
+      .assertThatLeakValueIs(CoreMetrics.NEW_CODE_SMELLS, 5 + 7);
   }
 
   @Test
@@ -761,7 +823,7 @@ class MeasureUpdateFormulaFactoryImplTest {
       // not vulnerabilities
       newGroup(RuleType.BUG).setInLeak(true).setCount(9),
       newGroup(RuleType.CODE_SMELL).setInLeak(true).setCount(11))
-        .assertThatLeakValueIs(CoreMetrics.NEW_VULNERABILITIES, 5 + 7);
+      .assertThatLeakValueIs(CoreMetrics.NEW_VULNERABILITIES, 5 + 7);
   }
 
   @Test
@@ -775,7 +837,7 @@ class MeasureUpdateFormulaFactoryImplTest {
       // not hotspots
       newGroup(RuleType.BUG).setInLeak(true).setCount(9),
       newGroup(RuleType.CODE_SMELL).setInLeak(true).setCount(11))
-        .assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_HOTSPOTS, 5 + 7);
+      .assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_HOTSPOTS, 5 + 7);
   }
 
   @Test
@@ -790,7 +852,7 @@ class MeasureUpdateFormulaFactoryImplTest {
       newGroup(RuleType.BUG).setInLeak(false).setCount(11),
       newGroup(RuleType.CODE_SMELL).setInLeak(false).setCount(13),
       newGroup(RuleType.VULNERABILITY).setInLeak(false).setCount(17))
-        .assertThatLeakValueIs(CoreMetrics.NEW_VIOLATIONS, 5 + 7 + 9);
+      .assertThatLeakValueIs(CoreMetrics.NEW_VIOLATIONS, 5 + 7 + 9);
   }
 
   @Test
@@ -807,7 +869,7 @@ class MeasureUpdateFormulaFactoryImplTest {
       // not in leak
       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.BLOCKER).setInLeak(false).setCount(11),
       newGroup(RuleType.BUG).setSeverity(Severity.BLOCKER).setInLeak(false).setCount(13))
-        .assertThatLeakValueIs(CoreMetrics.NEW_BLOCKER_VIOLATIONS, 3 + 5 + 7);
+      .assertThatLeakValueIs(CoreMetrics.NEW_BLOCKER_VIOLATIONS, 3 + 5 + 7);
   }
 
   @Test
@@ -824,7 +886,7 @@ class MeasureUpdateFormulaFactoryImplTest {
       // not in leak
       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.CRITICAL).setInLeak(false).setCount(11),
       newGroup(RuleType.BUG).setSeverity(Severity.CRITICAL).setInLeak(false).setCount(13))
-        .assertThatLeakValueIs(CoreMetrics.NEW_CRITICAL_VIOLATIONS, 3 + 5 + 7);
+      .assertThatLeakValueIs(CoreMetrics.NEW_CRITICAL_VIOLATIONS, 3 + 5 + 7);
   }
 
   @Test
@@ -841,7 +903,7 @@ class MeasureUpdateFormulaFactoryImplTest {
       // not in leak
       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.MAJOR).setInLeak(false).setCount(11),
       newGroup(RuleType.BUG).setSeverity(Severity.MAJOR).setInLeak(false).setCount(13))
-        .assertThatLeakValueIs(CoreMetrics.NEW_MAJOR_VIOLATIONS, 3 + 5 + 7);
+      .assertThatLeakValueIs(CoreMetrics.NEW_MAJOR_VIOLATIONS, 3 + 5 + 7);
   }
 
   @Test
@@ -858,7 +920,7 @@ class MeasureUpdateFormulaFactoryImplTest {
       // not in leak
       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.MINOR).setInLeak(false).setCount(11),
       newGroup(RuleType.BUG).setSeverity(Severity.MINOR).setInLeak(false).setCount(13))
-        .assertThatLeakValueIs(CoreMetrics.NEW_MINOR_VIOLATIONS, 3 + 5 + 7);
+      .assertThatLeakValueIs(CoreMetrics.NEW_MINOR_VIOLATIONS, 3 + 5 + 7);
   }
 
   @Test
@@ -875,7 +937,7 @@ class MeasureUpdateFormulaFactoryImplTest {
       // not in leak
       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.INFO).setInLeak(false).setCount(11),
       newGroup(RuleType.BUG).setSeverity(Severity.INFO).setInLeak(false).setCount(13))
-        .assertThatLeakValueIs(CoreMetrics.NEW_INFO_VIOLATIONS, 3 + 5 + 7);
+      .assertThatLeakValueIs(CoreMetrics.NEW_INFO_VIOLATIONS, 3 + 5 + 7);
   }
 
   @Test
@@ -891,7 +953,7 @@ class MeasureUpdateFormulaFactoryImplTest {
       // not in leak
       newGroup(RuleType.CODE_SMELL).setResolution(Issue.RESOLUTION_WONT_FIX).setInLeak(false).setCount(5),
       newGroup(RuleType.BUG).setResolution(Issue.RESOLUTION_WONT_FIX).setInLeak(false).setCount(50))
-        .assertThatLeakValueIs(CoreMetrics.NEW_ACCEPTED_ISSUES, 4 + 40);
+      .assertThatLeakValueIs(CoreMetrics.NEW_ACCEPTED_ISSUES, 4 + 40);
   }
 
   @Test
@@ -907,7 +969,7 @@ class MeasureUpdateFormulaFactoryImplTest {
       newGroup(RuleType.BUG).setEffort(7.0).setInLeak(true),
       // exclude resolved
       newResolvedGroup(RuleType.CODE_SMELL).setEffort(17.0).setInLeak(true))
-        .assertThatLeakValueIs(CoreMetrics.NEW_TECHNICAL_DEBT, 3.0);
+      .assertThatLeakValueIs(CoreMetrics.NEW_TECHNICAL_DEBT, 3.0);
   }
 
   @Test
@@ -922,7 +984,7 @@ class MeasureUpdateFormulaFactoryImplTest {
       newGroup(RuleType.CODE_SMELL).setEffort(7.0).setInLeak(true),
       // exclude resolved
       newResolvedGroup(RuleType.BUG).setEffort(17.0).setInLeak(true))
-        .assertThatLeakValueIs(CoreMetrics.NEW_RELIABILITY_REMEDIATION_EFFORT, 3.0);
+      .assertThatLeakValueIs(CoreMetrics.NEW_RELIABILITY_REMEDIATION_EFFORT, 3.0);
   }
 
   @Test
@@ -937,7 +999,7 @@ class MeasureUpdateFormulaFactoryImplTest {
       newGroup(RuleType.CODE_SMELL).setEffort(7.0).setInLeak(true),
       // exclude resolved
       newResolvedGroup(RuleType.VULNERABILITY).setEffort(17.0).setInLeak(true))
-        .assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_REMEDIATION_EFFORT, 3.0);
+      .assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_REMEDIATION_EFFORT, 3.0);
   }
 
   @Test
@@ -953,8 +1015,8 @@ class MeasureUpdateFormulaFactoryImplTest {
       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.BLOCKER).setInLeak(true),
       // exclude resolved
       newResolvedGroup(RuleType.BUG).setSeverity(Severity.BLOCKER).setInLeak(true))
-        // highest severity of bugs on leak period is minor -> B
-        .assertThatLeakValueIs(CoreMetrics.NEW_RELIABILITY_RATING, Rating.B);
+      // highest severity of bugs on leak period is minor -> B
+      .assertThatLeakValueIs(CoreMetrics.NEW_RELIABILITY_RATING, Rating.B);
   }
 
   @Test
@@ -970,8 +1032,8 @@ class MeasureUpdateFormulaFactoryImplTest {
       newGroup(RuleType.CODE_SMELL).setSeverity(Severity.BLOCKER).setInLeak(true),
       // exclude resolved
       newResolvedGroup(RuleType.VULNERABILITY).setSeverity(Severity.BLOCKER).setInLeak(true))
-        // highest severity of bugs on leak period is minor -> B
-        .assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_RATING, Rating.B);
+      // highest severity of bugs on leak period is minor -> B
+      .assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_RATING, Rating.B);
   }
 
   @Test
@@ -981,20 +1043,17 @@ class MeasureUpdateFormulaFactoryImplTest {
       newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW).setCount(1).setInLeak(true),
       // not in leak
       newGroup(RuleType.SECURITY_HOTSPOT).setSeverity(Issue.STATUS_TO_REVIEW).setInLeak(false))
-        .assertThatLeakValueIs(NEW_SECURITY_REVIEW_RATING, Rating.B)
-        .assertThatLeakValueIs(SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_SECURITY_REVIEW_RATING, Rating.B);
+      .assertThatLeakValueIs(NEW_SECURITY_REVIEW_RATING, Rating.B);
 
     withNoIssues()
-      .assertThatLeakValueIs(NEW_SECURITY_REVIEW_RATING, Rating.A)
-      .assertThatLeakValueIs(SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_SECURITY_REVIEW_RATING, Rating.A);
+      .assertThatLeakValueIs(NEW_SECURITY_REVIEW_RATING, Rating.A);
 
     with(
       newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW).setCount(3).setInLeak(true),
       newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW).setCount(1).setInLeak(true),
       // not in leak
       newGroup(RuleType.SECURITY_HOTSPOT).setSeverity(Issue.STATUS_TO_REVIEW).setInLeak(false))
-        .assertThatLeakValueIs(NEW_SECURITY_REVIEW_RATING, Rating.E)
-        .assertThatLeakValueIs(SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_SECURITY_REVIEW_RATING, Rating.D);
+      .assertThatLeakValueIs(NEW_SECURITY_REVIEW_RATING, Rating.E);
   }
 
   @Test
@@ -1004,7 +1063,7 @@ class MeasureUpdateFormulaFactoryImplTest {
       newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW).setCount(1).setInLeak(true),
       // not in leak
       newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW).setCount(5).setInLeak(false))
-        .assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_HOTSPOTS_REVIEWED, 75.0);
+      .assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_HOTSPOTS_REVIEWED, 75.0);
 
     withNoIssues()
       .assertNoLeakValue(CoreMetrics.NEW_SECURITY_HOTSPOTS_REVIEWED);
@@ -1017,7 +1076,7 @@ class MeasureUpdateFormulaFactoryImplTest {
       newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW).setCount(1).setInLeak(true),
       // not in leak
       newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW).setCount(5).setInLeak(false))
-        .assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_HOTSPOTS_REVIEWED_STATUS, 3.0);
+      .assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_HOTSPOTS_REVIEWED_STATUS, 3.0);
 
     withNoIssues()
       .assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_HOTSPOTS_REVIEWED_STATUS, 0.0);
@@ -1030,7 +1089,7 @@ class MeasureUpdateFormulaFactoryImplTest {
       newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW).setCount(1).setInLeak(true),
       // not in leak
       newGroup(RuleType.SECURITY_HOTSPOT).setStatus(Issue.STATUS_TO_REVIEW).setCount(5).setInLeak(false))
-        .assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_HOTSPOTS_TO_REVIEW_STATUS, 1.0);
+      .assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_HOTSPOTS_TO_REVIEW_STATUS, 1.0);
 
     withNoIssues()
       .assertThatLeakValueIs(CoreMetrics.NEW_SECURITY_HOTSPOTS_TO_REVIEW_STATUS, 0.0);
@@ -1039,13 +1098,15 @@ class MeasureUpdateFormulaFactoryImplTest {
   private static Stream<Arguments> newMaintainabilityMetrics() {
     return Stream.of(
       arguments(CoreMetrics.NEW_TECHNICAL_DEBT, CoreMetrics.NEW_SQALE_DEBT_RATIO, CoreMetrics.NEW_MAINTAINABILITY_RATING),
-      arguments(SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_MAINTAINABILITY_REMEDIATION_EFFORT, SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_MAINTAINABILITY_DEBT_RATIO,
+      arguments(SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_MAINTAINABILITY_REMEDIATION_EFFORT,
+        SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_MAINTAINABILITY_DEBT_RATIO,
         SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_MAINTAINABILITY_RATING));
   }
 
   @ParameterizedTest
   @MethodSource("newMaintainabilityMetrics")
-  void test_new_sqale_debt_ratio_and_new_maintainability_rating(Metric<?> newMaintainabilityRemediationEffortMetric, Metric<?> newMaintainabilityDebtRatioMetric,
+  void test_new_sqale_debt_ratio_and_new_maintainability_rating(Metric<?> newMaintainabilityRemediationEffortMetric,
+    Metric<?> newMaintainabilityDebtRatioMetric,
     Metric<?> newMaintainabilityRatingMetric) {
     withNoIssues()
       .assertThatLeakValueIs(newMaintainabilityDebtRatioMetric, 0)
@@ -1082,8 +1143,8 @@ class MeasureUpdateFormulaFactoryImplTest {
       .and(CoreMetrics.NEW_DEVELOPMENT_COST, 10.0D)
       .assertThatLeakValueIs(newMaintainabilityDebtRatioMetric, 200.0);
     switch (newMaintainabilityRatingMetric.key()) {
-      case NEW_MAINTAINABILITY_RATING_KEY -> verifier.assertThatLeakValueIs(newMaintainabilityRatingMetric, Rating.E);
-      case SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_MAINTAINABILITY_RATING_KEY -> verifier.assertThatLeakValueIs(newMaintainabilityRatingMetric, Rating.D);
+      case NEW_MAINTAINABILITY_RATING_KEY, SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_MAINTAINABILITY_RATING_KEY ->
+        verifier.assertThatLeakValueIs(newMaintainabilityRatingMetric, Rating.E);
       default -> throw new IllegalArgumentException("Unexpected metric: " + newMaintainabilityRatingMetric.key());
     }
 
@@ -1135,7 +1196,7 @@ class MeasureUpdateFormulaFactoryImplTest {
       newImpactGroup(SECURITY, LOW, Issue.STATUS_RESOLVED, Issue.RESOLUTION_WONT_FIX, 6),
       newImpactGroup(SECURITY, HIGH, Issue.STATUS_RESOLVED, Issue.RESOLUTION_FALSE_POSITIVE, 7),
       newImpactGroup(RELIABILITY, HIGH, Issue.STATUS_RESOLVED, Issue.RESOLUTION_WONT_FIX, 8))
-        .assertThatValueIs(CoreMetrics.HIGH_IMPACT_ACCEPTED_ISSUES, 4 + 8);
+      .assertThatValueIs(CoreMetrics.HIGH_IMPACT_ACCEPTED_ISSUES, 4 + 8);
   }
 
   @Test
@@ -1157,12 +1218,12 @@ class MeasureUpdateFormulaFactoryImplTest {
       newImpactGroup(SECURITY, HIGH, Issue.STATUS_RESOLVED, Issue.RESOLUTION_WONT_FIX, 4, 1d, false),
       newImpactGroup(RELIABILITY, HIGH, Issue.STATUS_RESOLVED, Issue.RESOLUTION_WONT_FIX, 8, 1d, false),
       newImpactGroup(MAINTAINABILITY, MEDIUM, Issue.STATUS_RESOLVED, Issue.RESOLUTION_WONT_FIX, 8, 1d, false))
-        .assertThatValueIs(SoftwareQualitiesMetrics.SOFTWARE_QUALITY_MAINTAINABILITY_REMEDIATION_EFFORT, 1d + 2d)
-        .assertThatValueIs(SoftwareQualitiesMetrics.SOFTWARE_QUALITY_RELIABILITY_REMEDIATION_EFFORT, 1d + 2d)
-        .assertThatValueIs(SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_REMEDIATION_EFFORT, 1d)
-        .assertThatLeakValueIs(SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_MAINTAINABILITY_REMEDIATION_EFFORT, 2d)
-        .assertThatLeakValueIs(SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_RELIABILITY_REMEDIATION_EFFORT, 2d)
-        .assertThatLeakValueIs(SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_SECURITY_REMEDIATION_EFFORT, 0d);
+      .assertThatValueIs(SoftwareQualitiesMetrics.SOFTWARE_QUALITY_MAINTAINABILITY_REMEDIATION_EFFORT, 1d + 2d)
+      .assertThatValueIs(SoftwareQualitiesMetrics.SOFTWARE_QUALITY_RELIABILITY_REMEDIATION_EFFORT, 1d + 2d)
+      .assertThatValueIs(SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_REMEDIATION_EFFORT, 1d)
+      .assertThatLeakValueIs(SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_MAINTAINABILITY_REMEDIATION_EFFORT, 2d)
+      .assertThatLeakValueIs(SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_RELIABILITY_REMEDIATION_EFFORT, 2d)
+      .assertThatLeakValueIs(SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_SECURITY_REMEDIATION_EFFORT, 0d);
   }
 
   @Test
@@ -1186,9 +1247,9 @@ class MeasureUpdateFormulaFactoryImplTest {
       newImpactGroup(MAINTAINABILITY, MEDIUM, 10),
       newImpactGroup(MAINTAINABILITY, LOW, 11),
       newImpactGroup(SECURITY, HIGH, 3))
-        .assertThatJsonValueIs(CoreMetrics.RELIABILITY_ISSUES, impactMeasureToJson(8, 3, 4, 1))
-        .assertThatJsonValueIs(CoreMetrics.MAINTAINABILITY_ISSUES, impactMeasureToJson(21, 0, 10, 11))
-        .assertThatJsonValueIs(CoreMetrics.SECURITY_ISSUES, impactMeasureToJson(3, 3, 0, 0));
+      .assertThatJsonValueIs(CoreMetrics.RELIABILITY_ISSUES, impactMeasureToJson(8, 3, 4, 1))
+      .assertThatJsonValueIs(CoreMetrics.MAINTAINABILITY_ISSUES, impactMeasureToJson(21, 0, 10, 11))
+      .assertThatJsonValueIs(CoreMetrics.SECURITY_ISSUES, impactMeasureToJson(3, 3, 0, 0));
   }
 
   @Test
@@ -1347,19 +1408,23 @@ class MeasureUpdateFormulaFactoryImplTest {
     return newImpactGroup(softwareQuality, severity, status, resolution, count, 0, false);
   }
 
-  private static IssueImpactGroupDto newImpactGroup(SoftwareQuality softwareQuality, org.sonar.api.issue.impact.Severity severity, long count) {
+  private static IssueImpactGroupDto newImpactGroup(SoftwareQuality softwareQuality, org.sonar.api.issue.impact.Severity severity,
+    long count) {
     return newImpactGroup(softwareQuality, severity, Issue.STATUS_OPEN, null, count, 0, false);
   }
 
-  private static IssueImpactGroupDto newImpactGroup(SoftwareQuality softwareQuality, org.sonar.api.issue.impact.Severity severity, long count, boolean inLeak) {
+  private static IssueImpactGroupDto newImpactGroup(SoftwareQuality softwareQuality, org.sonar.api.issue.impact.Severity severity,
+    long count, boolean inLeak) {
     return newImpactGroup(softwareQuality, severity, Issue.STATUS_OPEN, null, count, 0, inLeak);
   }
 
-  private static IssueImpactGroupDto newImpactGroup(SoftwareQuality softwareQuality, org.sonar.api.issue.impact.Severity severity, long count, double effort) {
+  private static IssueImpactGroupDto newImpactGroup(SoftwareQuality softwareQuality, org.sonar.api.issue.impact.Severity severity,
+    long count, double effort) {
     return newImpactGroup(softwareQuality, severity, Issue.STATUS_OPEN, null, count, effort, false);
   }
 
-  private static IssueImpactGroupDto newImpactGroup(SoftwareQuality softwareQuality, org.sonar.api.issue.impact.Severity severity, long count, double effort, boolean inLeak) {
+  private static IssueImpactGroupDto newImpactGroup(SoftwareQuality softwareQuality, org.sonar.api.issue.impact.Severity severity,
+    long count, double effort, boolean inLeak) {
     return newImpactGroup(softwareQuality, severity, Issue.STATUS_OPEN, null, count, effort, inLeak);
   }
 
@@ -1420,7 +1485,7 @@ class MeasureUpdateFormulaFactoryImplTest {
 
     @Override
     public DebtRatingGrid getDebtRatingGrid() {
-      return new DebtRatingGrid(new double[] {0.05, 0.1, 0.2, 0.5});
+      return new DebtRatingGrid(new double[]{0.05, 0.1, 0.2, 0.5});
     }
 
     @Override
@@ -1491,7 +1556,8 @@ class MeasureUpdateFormulaFactoryImplTest {
       return this;
     }
 
-    public HierarchyTester withChildrenHotspotsCounts(long childrenHotspotsReviewed, long childrenNewHotspotsReviewed, long childrenHotspotsToReview,
+    public HierarchyTester withChildrenHotspotsCounts(long childrenHotspotsReviewed, long childrenNewHotspotsReviewed,
+      long childrenHotspotsToReview,
       long childrenNewHotspotsToReview) {
       this.initialValues.childrenHotspotsReviewed = childrenHotspotsReviewed;
       this.initialValues.childrenNewHotspotsReviewed = childrenNewHotspotsReviewed;
index 51d90e14b6177f7bfc4d53fee3a981f78e09459e..1463035e7f1efbe818dda36a8a5a18e0747aadb6 100644 (file)
@@ -52,10 +52,10 @@ public class MeasuresWsModuleTest {
   public void getNewMetricsInSonarQube107_shouldReturnExactString() {
     String actual = MeasuresWsModule.getNewMetricsInSonarQube107();
     assertThat(actual).isEqualTo("'software_quality_maintainability_debt_ratio', 'software_quality_maintainability_rating', 'software_quality_reliability_rating', " +
-      "'software_quality_security_rating', 'software_quality_security_review_rating', 'software_quality_maintainability_remediation_effort', " +
+      "'software_quality_security_rating', 'software_quality_maintainability_remediation_effort', " +
       "'software_quality_reliability_remediation_effort', 'software_quality_security_remediation_effort', 'effort_to_reach_software_quality_maintainability_rating_a', " +
       "'new_software_quality_maintainability_debt_ratio', 'new_software_quality_maintainability_rating', 'new_software_quality_reliability_rating', " +
-      "'new_software_quality_security_rating', 'new_software_quality_security_review_rating', 'new_software_quality_maintainability_remediation_effort'," +
+      "'new_software_quality_security_rating', 'new_software_quality_maintainability_remediation_effort'," +
       " 'new_software_quality_reliability_remediation_effort', 'new_software_quality_security_remediation_effort'");
   }
 }
index 5738de6bdf6e525a58f8ea413abf94611b6bcc5c..ddac90bfbc41a322f99668c65cf929107dd5c070 100644 (file)
@@ -26,7 +26,6 @@ import org.sonar.api.measures.Metrics;
 import static org.sonar.api.measures.CoreMetrics.DOMAIN_MAINTAINABILITY;
 import static org.sonar.api.measures.CoreMetrics.DOMAIN_RELIABILITY;
 import static org.sonar.api.measures.CoreMetrics.DOMAIN_SECURITY;
-import static org.sonar.api.measures.CoreMetrics.DOMAIN_SECURITY_REVIEW;
 
 public class SoftwareQualitiesMetrics implements Metrics {
 
@@ -39,7 +38,7 @@ public class SoftwareQualitiesMetrics implements Metrics {
       .setDirection(Metric.DIRECTION_WORST)
       .setQualitative(true)
       .setBestValue(1.0)
-      .setWorstValue(4.0)
+      .setWorstValue(5.0)
       .create();
 
   public static final String NEW_SOFTWARE_QUALITY_MAINTAINABILITY_RATING_KEY = "new_software_quality_maintainability_rating";
@@ -54,7 +53,7 @@ public class SoftwareQualitiesMetrics implements Metrics {
       .setOptimizedBestValue(true)
       .setQualitative(true)
       .setBestValue(1.0)
-      .setWorstValue(4.0)
+      .setWorstValue(5.0)
       .create();
 
   public static final String SOFTWARE_QUALITY_RELIABILITY_RATING_KEY = "software_quality_reliability_rating";
@@ -66,7 +65,7 @@ public class SoftwareQualitiesMetrics implements Metrics {
     .setDirection(Metric.DIRECTION_WORST)
     .setQualitative(true)
     .setBestValue(1.0)
-    .setWorstValue(4.0)
+    .setWorstValue(5.0)
     .create();
 
   public static final String NEW_SOFTWARE_QUALITY_RELIABILITY_RATING_KEY = "new_software_quality_reliability_rating";
@@ -81,7 +80,7 @@ public class SoftwareQualitiesMetrics implements Metrics {
     .setOptimizedBestValue(true)
     .setQualitative(true)
     .setBestValue(1.0)
-    .setWorstValue(4.0)
+    .setWorstValue(5.0)
     .create();
 
   public static final String SOFTWARE_QUALITY_SECURITY_RATING_KEY = "software_quality_security_rating";
@@ -93,7 +92,7 @@ public class SoftwareQualitiesMetrics implements Metrics {
     .setDirection(Metric.DIRECTION_WORST)
     .setQualitative(true)
     .setBestValue(1.0)
-    .setWorstValue(4.0)
+    .setWorstValue(5.0)
     .create();
 
   public static final String NEW_SOFTWARE_QUALITY_SECURITY_RATING_KEY = "new_software_quality_security_rating";
@@ -107,36 +106,9 @@ public class SoftwareQualitiesMetrics implements Metrics {
     .setOptimizedBestValue(true)
     .setQualitative(true)
     .setBestValue(1.0)
-    .setWorstValue(4.0)
+    .setWorstValue(5.0)
     .create();
 
-  public static final String SOFTWARE_QUALITY_SECURITY_REVIEW_RATING_KEY = "software_quality_security_review_rating";
-
-  public static final Metric<Integer> SOFTWARE_QUALITY_SECURITY_REVIEW_RATING =
-    new Metric.Builder(SOFTWARE_QUALITY_SECURITY_REVIEW_RATING_KEY, "Software Quality Security Review Rating", Metric.ValueType.RATING)
-      .setDescription("Software quality security review rating")
-      .setDomain(DOMAIN_SECURITY_REVIEW)
-      .setDirection(Metric.DIRECTION_WORST)
-      .setQualitative(true)
-      .setBestValue(1.0)
-      .setWorstValue(4.0)
-      .create();
-
-  public static final String NEW_SOFTWARE_QUALITY_SECURITY_REVIEW_RATING_KEY = "new_software_quality_security_review_rating";
-
-  public static final Metric<Integer> NEW_SOFTWARE_QUALITY_SECURITY_REVIEW_RATING =
-    new Metric.Builder(NEW_SOFTWARE_QUALITY_SECURITY_REVIEW_RATING_KEY, "Software Quality Security Review Rating on New Code",
-      Metric.ValueType.RATING)
-      .setDescription("Software quality security review rating on new code")
-      .setDomain(DOMAIN_SECURITY_REVIEW)
-      .setDirection(Metric.DIRECTION_WORST)
-      .setDeleteHistoricalData(true)
-      .setOptimizedBestValue(true)
-      .setQualitative(true)
-      .setBestValue(1.0)
-      .setWorstValue(4.0)
-      .create();
-
   public static final String EFFORT_TO_REACH_SOFTWARE_QUALITY_MAINTAINABILITY_RATING_A_KEY =
     "effort_to_reach_software_quality_maintainability_rating_a";
 
@@ -271,8 +243,6 @@ public class SoftwareQualitiesMetrics implements Metrics {
       NEW_SOFTWARE_QUALITY_RELIABILITY_RATING,
       SOFTWARE_QUALITY_SECURITY_RATING,
       NEW_SOFTWARE_QUALITY_SECURITY_RATING,
-      SOFTWARE_QUALITY_SECURITY_REVIEW_RATING,
-      NEW_SOFTWARE_QUALITY_SECURITY_REVIEW_RATING,
       EFFORT_TO_REACH_SOFTWARE_QUALITY_MAINTAINABILITY_RATING_A,
       SOFTWARE_QUALITY_MAINTAINABILITY_REMEDIATION_EFFORT,
       NEW_SOFTWARE_QUALITY_MAINTAINABILITY_REMEDIATION_EFFORT,
index e849eba4b3c901b28ce1933c50e0405eb20e908f..522f4dd84f581dfd3828c47a0dd3ed924b745879 100644 (file)
@@ -222,7 +222,8 @@ public class ProtobufJsonFormat {
         writer.value((String) value);
         break;
       case ENUM:
-        writer.value(((Descriptors.EnumValueDescriptor) value).getName());
+        String enumValue = formatEnumValue(fieldDescriptor, (Descriptors.EnumValueDescriptor) value);
+        writer.value(enumValue);
         break;
       case MESSAGE:
         writeMessageValue((Message) value, writer);
@@ -232,6 +233,20 @@ public class ProtobufJsonFormat {
     }
   }
 
+  /**
+   * As a limitation from protobuf, there can't be the same enum value defined twice in proto, even if they belong to different enums.
+   * To remove this constraint, we let the possibility to have the enum name as the prefix of the enum value to make it unique.
+   * The class will make sure to remove it when converted to JSON.
+   *  @see <a href="https://github.com/protocolbuffers/protobuf/issues/5425">https://github.com/protocolbuffers/protobuf/issues/5425</a>
+   */
+  private static String formatEnumValue(Descriptors.FieldDescriptor fieldDescriptor, Descriptors.EnumValueDescriptor value) {
+    String enumValue = value.getName();
+    if (enumValue.startsWith(fieldDescriptor.getEnumType().getName())) {
+      enumValue = enumValue.substring(fieldDescriptor.getEnumType().getName().length() + 1);
+    }
+    return enumValue;
+  }
+
   private static void writeMessageValue(Message message, JsonWriter writer) {
     MessageType messageType = MessageType.of(message);
     if (messageType.doesWrapRepeated) {
index 890d4875c16847df5f820127f0c128ea15ee96d8..7604ab16d9f9f4340423ccf8af67227b549beacf 100644 (file)
@@ -1244,8 +1244,6 @@ issues.facet.createdAt.last_year=Last year
 issues.facet.createdAt.bar_description={0} issues from {1} to {2}
 issues.facet.authors=Author
 issues.facet.impactSeverities=Severity
-issues.facet.impactSeverities.help.line1=Severities are now directly tied to the software quality impacted. This means that one software quality impacted has one severity.
-issues.facet.impactSeverities.help.line2=There are only three levels: high, medium, and low.
 issues.facet.issues=Issue Key
 issues.facet.mode=Display Mode
 issues.facet.mode.count=Issues
@@ -1328,24 +1326,38 @@ projects.limited_set_of_projects=Displayed project set limited to the top {0} pr
 projects.facets.quality_gate=Quality Gate
 projects.facets.quality_gate.warning_help=Warning status is deprecated. This filter will disappear when no Warning Quality Gate remains.
 projects.facets.rating_x={0} rating
-projects.facets.rating_option.reliability.1=0 issues
+projects.facets.rating_option.reliability.legacy.1=≥ 0 info issues
+projects.facets.rating_option.reliability.legacy.2=≥ 1 minor issue
+projects.facets.rating_option.reliability.legacy.3=≥ 1 major issue
+projects.facets.rating_option.reliability.legacy.4=≥ 1 critical issue
+projects.facets.rating_option.reliability.legacy.5=≥ 1 blocker issue
+projects.facets.rating_option.reliability.1=≥ 0 info issues
 projects.facets.rating_option.reliability.2=≥ 1 low issue
 projects.facets.rating_option.reliability.3=≥ 1 medium issue
 projects.facets.rating_option.reliability.4=≥ 1 high issue
-projects.facets.rating_option.security.1=0 issues
+projects.facets.rating_option.reliability.5=≥ 1 blocker issue
+projects.facets.rating_option.security.legacy.1=≥ 0 info issues
+projects.facets.rating_option.security.legacy.2=≥ 1 minor issue
+projects.facets.rating_option.security.legacy.3=≥ 1 major issue
+projects.facets.rating_option.security.legacy.4=≥ 1 critical issue
+projects.facets.rating_option.security.legacy.5=≥ 1 blocker issue
+projects.facets.rating_option.security.1=≥ 0 info issues
 projects.facets.rating_option.security.2=≥ 1 low issue
 projects.facets.rating_option.security.3=≥ 1 medium issue
 projects.facets.rating_option.security.4=≥ 1 high issue
+projects.facets.rating_option.security.5=≥ 1 blocker issue
 projects.facets.rating_option.maintainability.1=≤ 5% to 0%
 projects.facets.rating_option.maintainability.2=≥ 5% to <10%
 projects.facets.rating_option.maintainability.3=≥ 10% to <20%
-projects.facets.rating_option.maintainability.4=≥ 20%
-projects.facets.rating_option.security_review.1== 100%
-projects.facets.rating_option.security_review.2=≥ 70% to <100%
+projects.facets.rating_option.maintainability.4=≥ 20% to <50%
+projects.facets.rating_option.maintainability.5=≥ 50%
+projects.facets.rating_option.security_review.1=≥ 80%
+projects.facets.rating_option.security_review.2=≥ 70% to <80%
 projects.facets.rating_option.security_review.3=≥ 50% to <70%
-projects.facets.rating_option.security_review.4=< 50%
+projects.facets.rating_option.security_review.4=≥ 30% to <50%
+projects.facets.rating_option.security_review.5=< 30%
 projects.facets.security_review.description=The percentage of reviewed (fixed or safe) security hotspots
-projects.facets.maintainability.description=Ratio of the size of the project to the estimated time needed to fix all outstanding maintainability issues
+projects.facets.maintainability.description=Ratio of the estimated time needed to fix all outstanding maintainability issues to the size of the project
 projects.facets.languages=Languages
 projects.facets.search.languages=Search for languages
 projects.facets.new_lines=New Lines
@@ -2938,12 +2950,20 @@ severity.INFO=Info
 severity.INFO.description=Neither a bug nor a quality flaw. Just a finding.
 
 # New severities
-severity.HIGH=High
-severity.HIGH.description=Must be fixed immediately.
-severity.MEDIUM=Medium
-severity.MEDIUM.description=High potential for significant to moderate impact.
-severity.LOW=Low
-severity.LOW.description=Potential for moderate to minor impact.
+severity_impact.title=Severity of impact
+severity_impact.levels=Severity levels
+severity_impact.BLOCKER=Blocker
+severity_impact.BLOCKER.description=Must be fixed immediately.
+severity_impact.HIGH=High
+severity_impact.HIGH.description=Must be reviewed immediately and fixed soon.
+severity_impact.MEDIUM=Medium
+severity_impact.MEDIUM.description=High potential for significant to moderate impact.
+severity_impact.LOW=Low
+severity_impact.LOW.description=Potential for moderate to minor impact.
+severity_impact.INFO=Info
+severity_impact.INFO.description=Neither a bug nor a quality flaw. Just a finding.
+severity_impact.help.line1=Severities are now directly tied to the software quality impacted. This means that one software quality impacted has one severity.
+severity_impact.help.line2=There are five levels of severity: blocker, high, medium, low and info.
 
 
 #------------------------------------------------------------------------------
@@ -3309,9 +3329,6 @@ metric.new_software_quality_security_remediation_effort.extra_short_name=Remedia
 metric.new_security_review_rating.description=Security Review Rating on New Code
 metric.new_security_review_rating.name=Security Review Rating on New Code
 metric.new_security_review_rating.extra_short_name=Rating
-metric.new_software_quality_security_review_rating.description=Security Review Rating on New Code
-metric.new_software_quality_security_review_rating.name=Security Review Rating on New Code
-metric.new_software_quality_security_review_rating.extra_short_name=Rating
 metric.new_sqale_debt_ratio.description=Technical Debt Ratio of new/changed code.
 metric.new_sqale_debt_ratio.name=Technical Debt Ratio on New Code
 metric.new_sqale_debt_ratio.short_name=Debt Ratio on new code
@@ -3413,11 +3430,11 @@ metric.software_quality_releasability_rating.name=Releasability Rating
 metric.software_quality_reliability_rating.description=Reliability rating
 metric.software_quality_reliability_rating.name=Reliability Rating
 metric.software_quality_reliability_rating.extra_short_name=Rating
-metric.software_quality_reliability_rating.tooltip.A=Reliability rating is A when there are no reliability issues.
+metric.software_quality_reliability_rating.tooltip.A=Reliability rating is A when there are no reliability issues above info severity.
 metric.software_quality_reliability_rating.tooltip.B=Reliability rating is B when there is at least one low reliability issue.
 metric.software_quality_reliability_rating.tooltip.C=Reliability rating is C when there is at least one medium reliability issue.
 metric.software_quality_reliability_rating.tooltip.D=Reliability rating is D when there is at least one high reliability issue.
-metric.software_quality_reliability_rating.tooltip.E=Reliability rating is E when there is at least one high reliability issue.
+metric.software_quality_reliability_rating.tooltip.E=Reliability rating is E when there is at least one blocker reliability issue.
 metric.reliability_remediation_effort.description=Reliability Remediation Effort
 metric.reliability_remediation_effort.name=Reliability Remediation Effort
 metric.reliability_remediation_effort.extra_short_name=Remediation Effort
@@ -3452,11 +3469,11 @@ metric.security_rating.tooltip.E=Security rating is E when there is at least one
 metric.software_quality_security_rating.description=Security rating
 metric.software_quality_security_rating.name=Security Rating
 metric.software_quality_security_rating.extra_short_name=Rating
-metric.software_quality_security_rating.tooltip.A=Security rating is A when there are no security issues.
+metric.software_quality_security_rating.tooltip.A=Security rating is A when there are no security issues above info severity.
 metric.software_quality_security_rating.tooltip.B=Security rating is B when there is at least one low security issue.
 metric.software_quality_security_rating.tooltip.C=Security rating is C when there is at least one medium security issue.
 metric.software_quality_security_rating.tooltip.D=Security rating is D when there is at least one high security issue.
-metric.software_quality_security_rating.tooltip.E=Security rating is E when there is at least one high security issue.
+metric.software_quality_security_rating.tooltip.E=Security rating is E when there is at least one blocker security issue.
 metric.security_remediation_effort.description=Security remediation effort
 metric.security_remediation_effort.name=Security Remediation Effort
 metric.security_remediation_effort.extra_short_name=Remediation Effort
@@ -3466,19 +3483,11 @@ metric.software_quality_security_remediation_effort.extra_short_name=Remediation
 metric.security_review_rating.description=Security Review Rating
 metric.security_review_rating.name=Security Review Rating
 metric.security_review_rating.extra_short_name=Rating
-metric.software_quality_security_review_rating.description=Security Review Rating
-metric.software_quality_security_review_rating.name=Security Review Rating
-metric.software_quality_security_review_rating.extra_short_name=Rating
 metric.security_review_rating.tooltip.A=Security Review rating is A when at least 80% of Security Hotspots are reviewed.
 metric.security_review_rating.tooltip.B=Security Review rating is B when less than 80% of Security Hotspots are reviewed.
 metric.security_review_rating.tooltip.C=Security Review rating is C when less than 70% of Security Hotspots are reviewed.
 metric.security_review_rating.tooltip.D=Security Review rating is D when less than 50% of Security Hotspots are reviewed.
 metric.security_review_rating.tooltip.E=Security Review rating is E when less than 30% of Security Hotspots are reviewed.
-metric.software_quality_security_review_rating.tooltip.A=Security Review rating is A when all Security Hotspots are reviewed.
-metric.software_quality_security_review_rating.tooltip.B=Security Review rating is B when less than 100% of Security Hotspots are reviewed.
-metric.software_quality_security_review_rating.tooltip.C=Security Review rating is C when less than 70% of Security Hotspots are reviewed.
-metric.software_quality_security_review_rating.tooltip.D=Security Review rating is D when less than 50% of Security Hotspots are reviewed.
-metric.software_quality_security_review_rating.tooltip.E=Security Review rating is E when less than 30% of Security Hotspots are reviewed.
 metric.skipped_tests.description=Number of skipped unit tests
 metric.skipped_tests.name=Skipped Unit Tests
 metric.skipped_tests.short_name=Skipped
@@ -4221,7 +4230,7 @@ overview.measures.software_impact.severity.LOW=L
 overview.measures.software_impact.severity.LOW.tooltip=Low Impact
 overview.measures.software_impact.severity.LOW.improve_tooltip=low
 overview.measures.software_impact.improve_rating_tooltip=The {softwareQuality} rating is {ratingLabel} when there is at least one issue with {severity} impact on the {_softwareQuality} of your software.
-overview.measures.software_impact.improve_rating_tooltip.A=The {softwareQuality} rating is {ratingLabel} when there are no issues with impact on the {_softwareQuality} of your software.
+overview.measures.software_impact.improve_rating_tooltip.A=The {softwareQuality} rating is {ratingLabel} when there are no issues above info severity with impact on the {_softwareQuality} of your software.
 overview.measures.software_impact.improve_rating_tooltip.MAINTAINABILITY=The Maintainability rating is {ratingLabel} if the code has a relatively higher level of technical debt when compared to the size of the codebase.
 overview.measures.software_impact.improve_rating_tooltip.MAINTAINABILITY.A=The Maintainability rating is A if the code has a relatively lower level of technical debt when compared to the size of the codebase.
 
index 1f46975d3c474976b3729f47cb8e45a11ac94562..a230b33a6d5a8ae154066d7e22842aad12bdc74b 100644 (file)
@@ -34,8 +34,6 @@ class SoftwareQualitiesMetricsTest {
         SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_RELIABILITY_RATING,
         SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_RATING,
         SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_SECURITY_RATING,
-        SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_REVIEW_RATING,
-        SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_SECURITY_REVIEW_RATING,
         SoftwareQualitiesMetrics.EFFORT_TO_REACH_SOFTWARE_QUALITY_MAINTAINABILITY_RATING_A,
         SoftwareQualitiesMetrics.SOFTWARE_QUALITY_MAINTAINABILITY_REMEDIATION_EFFORT,
         SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_MAINTAINABILITY_REMEDIATION_EFFORT,
index 6dd4375a170913a5e4a64fb5900a8283e75d220e..d5721ebdcb5c56fef464775db420ed82545bb230 100644 (file)
@@ -58,6 +58,21 @@ public class ProtobufJsonFormatTest {
       "{\"stringField\":\"foo\",\"intField\":10,\"longField\":100,\"doubleField\":3.14,\"booleanField\":true,\"enumField\":\"GREEN\"}");
   }
 
+  @Test
+  public void toJson_whenPrefixedEnum_shouldConvertToExpectedEnumValue() {
+    PrimitiveTypeMsg protobuf = PrimitiveTypeMsg.newBuilder()
+      .setStringField("foo")
+      .setIntField(10)
+      .setLongField(100L)
+      .setDoubleField(3.14)
+      .setBooleanField(true)
+      .setEnumField(org.sonar.core.test.Test.FakeEnum.FakeEnum_YELLOW)
+      .build();
+
+    assertThat(toJson(protobuf)).isEqualTo(
+      "{\"stringField\":\"foo\",\"intField\":10,\"longField\":100,\"doubleField\":3.14,\"booleanField\":true,\"enumField\":\"YELLOW\"}");
+  }
+
   @Test
   public void bytes_field_can_not_be_converted() {
     assertThatThrownBy(() -> {
index e81ca6cffbe696094635741f780cf9f538517941..46e4ad106ea827e35f15bb21051fa620163a4175 100644 (file)
@@ -32,6 +32,7 @@ enum FakeEnum {
   BLUE = 0;
   RED = 1;
   GREEN = 2;
+  FakeEnum_YELLOW = 3;
 }
 
 message PrimitiveTypeMsg {
index bdbd28fcdaaf5658b11dfe19c65f7af1b1d5351b..e8ff234901d60b3e14ea66e84a73404ca1ad0718 100644 (file)
@@ -46,6 +46,7 @@ import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;
 import static org.apache.commons.lang3.RandomStringUtils.randomAlphabetic;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.sonar.api.issue.impact.Severity.BLOCKER;
 import static org.sonar.api.issue.impact.Severity.HIGH;
 import static org.sonar.api.issue.impact.Severity.LOW;
 import static org.sonar.api.issue.impact.SoftwareQuality.MAINTAINABILITY;
@@ -109,7 +110,7 @@ public class ExternalIssueImporterTest {
     ExternalIssue output = context.allExternalIssues().iterator().next();
     assertThat(output.engineId()).isEqualTo(RULE_ENGINE_ID);
     assertThat(output.ruleId()).isEqualTo(RULE_ID);
-    assertThat(output.severity()).isEqualTo(Severity.CRITICAL); //backmapped
+    assertThat(output.severity()).isEqualTo(Severity.BLOCKER); // backmapped
     assertThat(output.type()).isEqualTo(RuleType.VULNERABILITY); //backmapped
     assertThat(output.remediationEffort()).isNull();
     assertThat(logs.logs(Level.INFO)).contains("Imported 1 issue in 1 file");
@@ -119,10 +120,10 @@ public class ExternalIssueImporterTest {
     assertThat(output1.ruleId()).isEqualTo(RULE_ID);
     assertThat(output1.name()).isEqualTo(RULE_NAME);
     assertThat(output1.engineId()).isEqualTo(RULE_ENGINE_ID);
-    assertThat(output1.severity()).isEqualTo(Severity.CRITICAL); //backmapped
+    assertThat(output1.severity()).isEqualTo(Severity.BLOCKER); // backmapped
     assertThat(output1.type()).isEqualTo(RuleType.VULNERABILITY); //backmapped
     assertThat(output1.cleanCodeAttribute()).isEqualTo(RULE_ATTRIBUTE);
-    assertThat(output1.defaultImpacts()).containsExactlyInAnyOrderEntriesOf(Map.of(SECURITY, HIGH, MAINTAINABILITY, LOW));
+    assertThat(output1.defaultImpacts()).containsExactlyInAnyOrderEntriesOf(Map.of(SECURITY, BLOCKER, MAINTAINABILITY, LOW));
   }
 
   @Test
@@ -333,7 +334,7 @@ public class ExternalIssueImporterTest {
 
 
   private static ExternalIssueReport.Rule createRule() {
-    return createRule(RULE_ATTRIBUTE.name(), SECURITY.name(), HIGH.name());
+    return createRule(RULE_ATTRIBUTE.name(), SECURITY.name(), BLOCKER.name());
   }
 
   private static ExternalIssueReport.Rule createRule(String cleanCodeAttribute, String softwareQuality, String impactSeverity) {
index 7fa09b59828db9a6312554897ca7b426a8565e52..e999f837afd9bc8b3c68a7498f4624b793dd266d 100644 (file)
@@ -121,6 +121,9 @@ enum ImpactSeverity {
   LOW = 1;
   MEDIUM = 2;
   HIGH = 3;
+  // INFO and BLOCKER conflicts with Severity enum, so we use different values prefixed with enum name
+  ImpactSeverity_INFO = 4;
+  ImpactSeverity_BLOCKER = 5;
 }
 
 // Lines start at 1 and line offsets start at 0