From 664aa30312b0e559785536630f18433228c31467 Mon Sep 17 00:00:00 2001 From: Zipeng WU Date: Tue, 3 Oct 2023 11:05:06 +0200 Subject: [PATCH] SONAR-20607 Add isCaycCondition attribute in 'api/qualitygates/show' endpoint --- .../qualitygate/QualityGateCaycCheckerIT.java | 65 +++++++++++++------ .../server/qualitygate/ws/CreateActionIT.java | 8 ++- .../qualitygate/QualityGateCaycChecker.java | 35 +++++----- .../server/qualitygate/ws/CreateAction.java | 6 +- .../server/qualitygate/ws/ShowAction.java | 7 +- .../src/main/protobuf/ws-qualitygates.proto | 7 +- 6 files changed, 79 insertions(+), 49 deletions(-) diff --git a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/qualitygate/QualityGateCaycCheckerIT.java b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/qualitygate/QualityGateCaycCheckerIT.java index e9e9e712f3e..d5d151227bc 100644 --- a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/qualitygate/QualityGateCaycCheckerIT.java +++ b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/qualitygate/QualityGateCaycCheckerIT.java @@ -20,7 +20,6 @@ package org.sonar.server.qualitygate; import java.util.List; -import java.util.stream.IntStream; import org.junit.Rule; import org.junit.Test; import org.sonar.api.measures.Metric; @@ -30,14 +29,17 @@ import org.sonar.db.metric.MetricDto; import org.sonar.db.qualitygate.QualityGateConditionDto; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.sonar.api.measures.CoreMetrics.BLOCKER_VIOLATIONS; import static org.sonar.api.measures.CoreMetrics.DUPLICATED_LINES; +import static org.sonar.api.measures.CoreMetrics.FUNCTION_COMPLEXITY; import static org.sonar.api.measures.CoreMetrics.LINE_COVERAGE; import static org.sonar.api.measures.CoreMetrics.NEW_COVERAGE; import static org.sonar.api.measures.CoreMetrics.NEW_DUPLICATED_LINES_DENSITY; -import static org.sonar.api.measures.CoreMetrics.NEW_MAINTAINABILITY_RATING; -import static org.sonar.api.measures.CoreMetrics.NEW_RELIABILITY_RATING; import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_HOTSPOTS_REVIEWED; -import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_RATING; +import static org.sonar.api.measures.CoreMetrics.NEW_VIOLATIONS; +import static org.sonar.server.qualitygate.QualityGateCaycChecker.BEST_VALUE_REQUIREMENTS; import static org.sonar.server.qualitygate.QualityGateCaycChecker.CAYC_METRICS; import static org.sonar.server.qualitygate.QualityGateCaycStatus.COMPLIANT; import static org.sonar.server.qualitygate.QualityGateCaycStatus.NON_COMPLIANT; @@ -50,41 +52,57 @@ public class QualityGateCaycCheckerIT { QualityGateCaycChecker underTest = new QualityGateCaycChecker(db.getDbClient()); @Test - public void checkCaycCompliant() { + public void checkCaycCompliant_when_contains_all_and_only_complicant_conditions_should_return_compliant() { String qualityGateUuid = "abcd"; CAYC_METRICS.forEach(metric -> insertCondition(insertMetric(metric), qualityGateUuid, metric.getBestValue())); assertEquals(COMPLIANT, underTest.checkCaycCompliant(db.getSession(), qualityGateUuid)); } @Test - public void check_Cayc_non_compliant_with_extra_conditions() { + public void checkCaycCompliant_when_extra_conditions_should_return_over_compliant() { String qualityGateUuid = "abcd"; CAYC_METRICS.forEach(metric -> insertCondition(insertMetric(metric), qualityGateUuid, metric.getBestValue())); // extra conditions outside of CAYC requirements - List.of(LINE_COVERAGE, DUPLICATED_LINES).forEach(metric -> insertCondition(insertMetric(metric), qualityGateUuid, metric.getBestValue())); + List.of(LINE_COVERAGE, DUPLICATED_LINES).forEach(metric -> insertCondition(insertMetric(metric), qualityGateUuid, + metric.getBestValue())); assertEquals(OVER_COMPLIANT, underTest.checkCaycCompliant(db.getSession(), qualityGateUuid)); } @Test - public void check_Cayc_NonCompliant_with_lesser_threshold_value() { + public void checkCaycCompliant_when_conditions_have_lesser_threshold_value_should_return_non_compliant() { var metrics = CAYC_METRICS.stream().map(this::insertMetric).toList(); - IntStream.range(0, 4).forEach(idx -> { - String qualityGateUuid = "abcd" + idx; - for (int i = 0; i < metrics.size(); i++) { - var metric = metrics.get(i); - insertCondition(metric, qualityGateUuid, idx == i ? metric.getWorstValue() : metric.getBestValue()); + String qualityGateUuid = "abcd"; + for (var metric : metrics) { + if (BEST_VALUE_REQUIREMENTS.keySet().contains(metric.getKey())) { + insertCondition(metric, qualityGateUuid, metric.getBestValue() - 1); + } else { + insertCondition(metric, qualityGateUuid, metric.getBestValue()); } - assertEquals(NON_COMPLIANT, underTest.checkCaycCompliant(db.getSession(), qualityGateUuid)); - }); + } + assertEquals(NON_COMPLIANT, underTest.checkCaycCompliant(db.getSession(), qualityGateUuid)); + } + + @Test + public void isCaycCondition_when_check_compliant_condition_should_return_true() { + CAYC_METRICS.stream().map(this::toMetricDto) + .forEach(metricDto -> assertTrue(underTest.isCaycCondition(metricDto))); + } + + @Test + public void isCaycCondition_when_check_non_compliant_condition_should_return_false() { + List.of(BLOCKER_VIOLATIONS, FUNCTION_COMPLEXITY) + .stream().map(this::toMetricDto) + .forEach(metricDto -> assertFalse(underTest.isCaycCondition(metricDto))); } + @Test - public void check_Cayc_NonCompliant_with_missing_metric() { + public void checkCaycCompliant_when_missing_compliant_condition_should_return_non_compliant() { String qualityGateUuid = "abcd"; - List.of(NEW_MAINTAINABILITY_RATING, NEW_RELIABILITY_RATING, NEW_SECURITY_HOTSPOTS_REVIEWED, NEW_DUPLICATED_LINES_DENSITY) + List.of(NEW_VIOLATIONS, NEW_SECURITY_HOTSPOTS_REVIEWED) .forEach(metric -> insertCondition(insertMetric(metric), qualityGateUuid, metric.getBestValue())); assertEquals(NON_COMPLIANT, underTest.checkCaycCompliant(db.getSession(), qualityGateUuid)); } @@ -92,7 +110,7 @@ public class QualityGateCaycCheckerIT { @Test public void existency_requirements_check_only_existency() { String qualityGateUuid = "abcd"; - List.of(NEW_MAINTAINABILITY_RATING, NEW_RELIABILITY_RATING, NEW_SECURITY_HOTSPOTS_REVIEWED, NEW_SECURITY_RATING) + List.of(NEW_VIOLATIONS, NEW_SECURITY_HOTSPOTS_REVIEWED) .forEach(metric -> insertCondition(insertMetric(metric), qualityGateUuid, metric.getBestValue())); List.of(NEW_COVERAGE, NEW_DUPLICATED_LINES_DENSITY) .forEach(metric -> insertCondition(insertMetric(metric), qualityGateUuid, metric.getWorstValue())); @@ -115,7 +133,16 @@ public class QualityGateCaycCheckerIT { .setValueType(metric.getType().name()) .setHidden(false) .setBestValue(metric.getBestValue()) - .setBestValue(metric.getWorstValue()) + .setWorstValue(metric.getWorstValue()) .setDirection(metric.getDirection())); } + + private MetricDto toMetricDto(Metric metric) { + return new MetricDto() + .setKey(metric.key()) + .setValueType(metric.getType().name()) + .setBestValue(metric.getBestValue()) + .setWorstValue(metric.getWorstValue()) + .setDirection(metric.getDirection()); + } } diff --git a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/qualitygate/ws/CreateActionIT.java b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/qualitygate/ws/CreateActionIT.java index 9fe1185766b..9e8fa9ea172 100644 --- a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/qualitygate/ws/CreateActionIT.java +++ b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/qualitygate/ws/CreateActionIT.java @@ -29,6 +29,7 @@ import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import org.sonar.api.measures.CoreMetrics; import org.sonar.api.measures.Metric; import org.sonar.api.server.ws.WebService; import org.sonar.core.util.UuidFactoryFast; @@ -64,7 +65,8 @@ public class CreateActionIT { private final DbClient dbClient = db.getDbClient(); private final DbSession dbSession = db.getSession(); - private final CreateAction underTest = new CreateAction(dbClient, userSession, new QualityGateUpdater(dbClient, UuidFactoryFast.getInstance()), + private final CreateAction underTest = new CreateAction(dbClient, userSession, new QualityGateUpdater(dbClient, + UuidFactoryFast.getInstance()), new QualityGateConditionsUpdater(dbClient)); private final WsActionTester ws = new WsActionTester(underTest); @@ -89,7 +91,7 @@ public class CreateActionIT { var conditions = getConditions(dbSession, qualityGateDto); CAYC_METRICS.stream() - .map(m -> dbClient.metricDao().selectByKey(dbSession, m.getKey())) + .map(metric -> dbClient.metricDao().selectByKey(dbSession, metric.getKey())) .forEach(metricDto -> assertThat(conditions) .anyMatch(c -> metricDto.getUuid().equals(c.getMetricUuid()) && c.getErrorThreshold().equals(String.valueOf(getDefaultCaycValue(metricDto))))); } @@ -139,7 +141,7 @@ public class CreateActionIT { @DataProvider public static Object[][] nullOrEmpty() { - return new Object[][] { + return new Object[][]{ {null}, {""}, {" "} diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/QualityGateCaycChecker.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/QualityGateCaycChecker.java index 58322389702..f2c82248ad1 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/QualityGateCaycChecker.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/QualityGateCaycChecker.java @@ -20,51 +20,46 @@ package org.sonar.server.qualitygate; import java.io.Serializable; -import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; +import java.util.stream.Stream; import org.sonar.api.measures.Metric; import org.sonar.db.DbClient; import org.sonar.db.DbSession; import org.sonar.db.metric.MetricDto; import org.sonar.db.qualitygate.QualityGateConditionDto; -import static java.util.stream.Collectors.toUnmodifiableMap; +import static java.util.stream.Collectors.toMap; import static org.sonar.api.measures.CoreMetrics.NEW_COVERAGE; import static org.sonar.api.measures.CoreMetrics.NEW_COVERAGE_KEY; import static org.sonar.api.measures.CoreMetrics.NEW_DUPLICATED_LINES_DENSITY; import static org.sonar.api.measures.CoreMetrics.NEW_DUPLICATED_LINES_DENSITY_KEY; -import static org.sonar.api.measures.CoreMetrics.NEW_MAINTAINABILITY_RATING; -import static org.sonar.api.measures.CoreMetrics.NEW_RELIABILITY_RATING; import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_HOTSPOTS_REVIEWED; -import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_RATING; +import static org.sonar.api.measures.CoreMetrics.NEW_VIOLATIONS; import static org.sonar.server.qualitygate.QualityGateCaycStatus.COMPLIANT; import static org.sonar.server.qualitygate.QualityGateCaycStatus.NON_COMPLIANT; import static org.sonar.server.qualitygate.QualityGateCaycStatus.OVER_COMPLIANT; public class QualityGateCaycChecker { - public static final List> CAYC_METRICS = List.of( - NEW_MAINTAINABILITY_RATING, - NEW_RELIABILITY_RATING, + static final Map BEST_VALUE_REQUIREMENTS = Stream.of( + NEW_VIOLATIONS, + NEW_SECURITY_HOTSPOTS_REVIEWED + ).collect(toMap(Metric::getKey, Metric::getBestValue)); + static final Set EXISTENCY_REQUIREMENTS = Set.of( + NEW_DUPLICATED_LINES_DENSITY_KEY, + NEW_COVERAGE_KEY + ); + public static final Set> CAYC_METRICS = Set.of( + NEW_VIOLATIONS, NEW_SECURITY_HOTSPOTS_REVIEWED, - NEW_SECURITY_RATING, NEW_DUPLICATED_LINES_DENSITY, NEW_COVERAGE ); - private static final Set EXISTENCY_REQUIREMENTS = Set.of( - NEW_DUPLICATED_LINES_DENSITY_KEY, - NEW_COVERAGE_KEY - ); - - private static final Map BEST_VALUE_REQUIREMENTS = CAYC_METRICS.stream() - .filter(metric -> !EXISTENCY_REQUIREMENTS.contains(metric.getKey())) - .collect(toUnmodifiableMap(Metric::getKey, Metric::getBestValue)); - private final DbClient dbClient; public QualityGateCaycChecker(DbClient dbClient) { @@ -105,6 +100,10 @@ public class QualityGateCaycChecker { .orElse(NON_COMPLIANT); } + public boolean isCaycCondition(MetricDto metric) { + return CAYC_METRICS.stream().map(Metric::getKey).anyMatch(metric.getKey()::equals); + } + private static boolean checkMetricCaycCompliant(QualityGateConditionDto condition, MetricDto metric) { if (EXISTENCY_REQUIREMENTS.contains(metric.getKey())) { return true; diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/ws/CreateAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/ws/CreateAction.java index 371756e40d2..1fe80f49324 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/ws/CreateAction.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/ws/CreateAction.java @@ -111,9 +111,9 @@ public class CreateAction implements QualityGatesWsAction { } private void addCaycConditions(DbSession dbSession, QualityGateDto newQualityGate) { - CAYC_METRICS.forEach(m -> - qualityGateConditionsUpdater.createCondition(dbSession, newQualityGate, m.getKey(), OPERATORS_BY_DIRECTION.get(m.getDirection()).getDbValue(), - String.valueOf(getDefaultCaycValue(m))) + CAYC_METRICS.forEach(metric -> + qualityGateConditionsUpdater.createCondition(dbSession, newQualityGate, metric.getKey(), OPERATORS_BY_DIRECTION.get(metric.getDirection()).getDbValue(), + String.valueOf(getDefaultCaycValue(metric))) ); } diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/ws/ShowAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/ws/ShowAction.java index 69eafd56a70..8eae80f9445 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/ws/ShowAction.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/ws/ShowAction.java @@ -105,8 +105,8 @@ public class ShowAction implements QualityGatesWsAction { return dbClient.metricDao().selectByUuids(dbSession, metricUuids).stream().filter(MetricDto::isEnabled).collect(Collectors.toMap(MetricDto::getUuid, Function.identity())); } - private ShowWsResponse buildResponse(DbSession dbSession, QualityGateDto qualityGate, QualityGateDto defaultQualityGate, Collection conditions, - Map metricsByUuid, QualityGateCaycStatus caycStatus) { + private ShowWsResponse buildResponse(DbSession dbSession, QualityGateDto qualityGate, QualityGateDto defaultQualityGate, + Collection conditions, Map metricsByUuid, QualityGateCaycStatus caycStatus) { return ShowWsResponse.newBuilder() .setName(qualityGate.getName()) .setIsBuiltIn(qualityGate.isBuiltIn()) @@ -118,7 +118,7 @@ public class ShowAction implements QualityGatesWsAction { .build(); } - private static Function toWsCondition(Map metricsByUuid) { + private Function toWsCondition(Map metricsByUuid) { return condition -> { String metricUuid = condition.getMetricUuid(); MetricDto metric = metricsByUuid.get(metricUuid); @@ -126,6 +126,7 @@ public class ShowAction implements QualityGatesWsAction { ShowWsResponse.Condition.Builder builder = ShowWsResponse.Condition.newBuilder() .setId(condition.getUuid()) .setMetric(metric.getKey()) + .setIsCaycCondition(qualityGateCaycChecker.isCaycCondition(metric)) .setOp(condition.getOperator()); ofNullable(condition.getErrorThreshold()).ifPresent(builder::setError); return builder.build(); diff --git a/sonar-ws/src/main/protobuf/ws-qualitygates.proto b/sonar-ws/src/main/protobuf/ws-qualitygates.proto index 8327e2ae0dc..261f82f1b79 100644 --- a/sonar-ws/src/main/protobuf/ws-qualitygates.proto +++ b/sonar-ws/src/main/protobuf/ws-qualitygates.proto @@ -127,10 +127,11 @@ message ShowWsResponse { optional string caycStatus = 6; message Condition { - optional string id = 1; - optional string metric = 2; - optional string op = 4; + required string id = 1; + required string metric = 2; + required string op = 4; optional string error = 6; + required bool isCaycCondition = 7; } } -- 2.39.5