]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-20607 Add isCaycCondition attribute in 'api/qualitygates/show' endpoint
authorZipeng WU <zipeng.wu@sonarsource.com>
Tue, 3 Oct 2023 09:05:06 +0000 (11:05 +0200)
committersonartech <sonartech@sonarsource.com>
Thu, 12 Oct 2023 20:02:51 +0000 (20:02 +0000)
server/sonar-webserver-webapi/src/it/java/org/sonar/server/qualitygate/QualityGateCaycCheckerIT.java
server/sonar-webserver-webapi/src/it/java/org/sonar/server/qualitygate/ws/CreateActionIT.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/QualityGateCaycChecker.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/ws/CreateAction.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/ws/ShowAction.java
sonar-ws/src/main/protobuf/ws-qualitygates.proto

index e9e9e712f3e9f45827819dbc255cc5e3c0f119c7..d5d151227bc2e31eed087e102ae9abd2a1b6b617 100644 (file)
@@ -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());
+  }
 }
index 9fe1185766b5424cabe674d2fc8c53a65dce96a6..9e8fa9ea172a7064a97cc90685df968683c910bd 100644 (file)
@@ -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},
       {""},
       {"  "}
index 58322389702e87e4457c10f5c2c0956a8680fe0b..f2c82248ad1a2b91134b96ee9e4fbb2124d033b8 100644 (file)
 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<Metric<? extends Serializable>> CAYC_METRICS = List.of(
-    NEW_MAINTAINABILITY_RATING,
-    NEW_RELIABILITY_RATING,
+  static final Map<String, Double> BEST_VALUE_REQUIREMENTS = Stream.of(
+    NEW_VIOLATIONS,
+    NEW_SECURITY_HOTSPOTS_REVIEWED
+  ).collect(toMap(Metric::getKey, Metric::getBestValue));
+  static final Set<String> EXISTENCY_REQUIREMENTS = Set.of(
+    NEW_DUPLICATED_LINES_DENSITY_KEY,
+    NEW_COVERAGE_KEY
+  );
+  public static final Set<Metric<? extends Serializable>> CAYC_METRICS = Set.of(
+    NEW_VIOLATIONS,
     NEW_SECURITY_HOTSPOTS_REVIEWED,
-    NEW_SECURITY_RATING,
     NEW_DUPLICATED_LINES_DENSITY,
     NEW_COVERAGE
   );
 
-  private static final Set<String> EXISTENCY_REQUIREMENTS = Set.of(
-    NEW_DUPLICATED_LINES_DENSITY_KEY,
-    NEW_COVERAGE_KEY
-  );
-
-  private static final Map<String, Double> 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;
index 371756e40d2c46725f986f4d07f3076807686d29..1fe80f49324000ae070d3e9a7a9d6e972695f8c8 100644 (file)
@@ -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)))
     );
   }
 
index 69eafd56a7003f91dce1e80cb3e4ce61c6fefeea..8eae80f94459539c7a88369f1d1df6e9fd654917 100644 (file)
@@ -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<QualityGateConditionDto> conditions,
-    Map<String, MetricDto> metricsByUuid, QualityGateCaycStatus caycStatus) {
+  private ShowWsResponse buildResponse(DbSession dbSession, QualityGateDto qualityGate, QualityGateDto defaultQualityGate,
+    Collection<QualityGateConditionDto> conditions, Map<String, MetricDto> 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<QualityGateConditionDto, ShowWsResponse.Condition> toWsCondition(Map<String, MetricDto> metricsByUuid) {
+  private Function<QualityGateConditionDto, ShowWsResponse.Condition> toWsCondition(Map<String, MetricDto> 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();
index 8327e2ae0dc39d35ed38934e002ec9c5446eac7d..261f82f1b7911e9e55475d97a545c43368bed5d5 100644 (file)
@@ -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;
   }
 }