浏览代码

SONAR-20607 Add isCaycCondition attribute in 'api/qualitygates/show' endpoint

tags/10.3.0.82913
Zipeng WU 8 个月前
父节点
当前提交
664aa30312

+ 46
- 19
server/sonar-webserver-webapi/src/it/java/org/sonar/server/qualitygate/QualityGateCaycCheckerIT.java 查看文件

package org.sonar.server.qualitygate; package org.sonar.server.qualitygate;


import java.util.List; import java.util.List;
import java.util.stream.IntStream;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.sonar.api.measures.Metric; import org.sonar.api.measures.Metric;
import org.sonar.db.qualitygate.QualityGateConditionDto; import org.sonar.db.qualitygate.QualityGateConditionDto;


import static org.junit.Assert.assertEquals; 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.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.LINE_COVERAGE;
import static org.sonar.api.measures.CoreMetrics.NEW_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_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_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.QualityGateCaycChecker.CAYC_METRICS;
import static org.sonar.server.qualitygate.QualityGateCaycStatus.COMPLIANT; import static org.sonar.server.qualitygate.QualityGateCaycStatus.COMPLIANT;
import static org.sonar.server.qualitygate.QualityGateCaycStatus.NON_COMPLIANT; import static org.sonar.server.qualitygate.QualityGateCaycStatus.NON_COMPLIANT;
QualityGateCaycChecker underTest = new QualityGateCaycChecker(db.getDbClient()); QualityGateCaycChecker underTest = new QualityGateCaycChecker(db.getDbClient());


@Test @Test
public void checkCaycCompliant() {
public void checkCaycCompliant_when_contains_all_and_only_complicant_conditions_should_return_compliant() {
String qualityGateUuid = "abcd"; String qualityGateUuid = "abcd";
CAYC_METRICS.forEach(metric -> insertCondition(insertMetric(metric), qualityGateUuid, metric.getBestValue())); CAYC_METRICS.forEach(metric -> insertCondition(insertMetric(metric), qualityGateUuid, metric.getBestValue()));
assertEquals(COMPLIANT, underTest.checkCaycCompliant(db.getSession(), qualityGateUuid)); assertEquals(COMPLIANT, underTest.checkCaycCompliant(db.getSession(), qualityGateUuid));
} }


@Test @Test
public void check_Cayc_non_compliant_with_extra_conditions() {
public void checkCaycCompliant_when_extra_conditions_should_return_over_compliant() {
String qualityGateUuid = "abcd"; String qualityGateUuid = "abcd";
CAYC_METRICS.forEach(metric -> insertCondition(insertMetric(metric), qualityGateUuid, metric.getBestValue())); CAYC_METRICS.forEach(metric -> insertCondition(insertMetric(metric), qualityGateUuid, metric.getBestValue()));


// extra conditions outside of CAYC requirements // 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)); assertEquals(OVER_COMPLIANT, underTest.checkCaycCompliant(db.getSession(), qualityGateUuid));
} }


@Test @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(); 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 @Test
public void check_Cayc_NonCompliant_with_missing_metric() {
public void checkCaycCompliant_when_missing_compliant_condition_should_return_non_compliant() {
String qualityGateUuid = "abcd"; 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())); .forEach(metric -> insertCondition(insertMetric(metric), qualityGateUuid, metric.getBestValue()));
assertEquals(NON_COMPLIANT, underTest.checkCaycCompliant(db.getSession(), qualityGateUuid)); assertEquals(NON_COMPLIANT, underTest.checkCaycCompliant(db.getSession(), qualityGateUuid));
} }
@Test @Test
public void existency_requirements_check_only_existency() { public void existency_requirements_check_only_existency() {
String qualityGateUuid = "abcd"; 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())); .forEach(metric -> insertCondition(insertMetric(metric), qualityGateUuid, metric.getBestValue()));
List.of(NEW_COVERAGE, NEW_DUPLICATED_LINES_DENSITY) List.of(NEW_COVERAGE, NEW_DUPLICATED_LINES_DENSITY)
.forEach(metric -> insertCondition(insertMetric(metric), qualityGateUuid, metric.getWorstValue())); .forEach(metric -> insertCondition(insertMetric(metric), qualityGateUuid, metric.getWorstValue()));
.setValueType(metric.getType().name()) .setValueType(metric.getType().name())
.setHidden(false) .setHidden(false)
.setBestValue(metric.getBestValue()) .setBestValue(metric.getBestValue())
.setBestValue(metric.getWorstValue())
.setWorstValue(metric.getWorstValue())
.setDirection(metric.getDirection())); .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());
}
} }

+ 5
- 3
server/sonar-webserver-webapi/src/it/java/org/sonar/server/qualitygate/ws/CreateActionIT.java 查看文件

import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.sonar.api.measures.CoreMetrics;
import org.sonar.api.measures.Metric; import org.sonar.api.measures.Metric;
import org.sonar.api.server.ws.WebService; import org.sonar.api.server.ws.WebService;
import org.sonar.core.util.UuidFactoryFast; import org.sonar.core.util.UuidFactoryFast;


private final DbClient dbClient = db.getDbClient(); private final DbClient dbClient = db.getDbClient();
private final DbSession dbSession = db.getSession(); 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)); new QualityGateConditionsUpdater(dbClient));
private final WsActionTester ws = new WsActionTester(underTest); private final WsActionTester ws = new WsActionTester(underTest);


var conditions = getConditions(dbSession, qualityGateDto); var conditions = getConditions(dbSession, qualityGateDto);


CAYC_METRICS.stream() CAYC_METRICS.stream()
.map(m -> dbClient.metricDao().selectByKey(dbSession, m.getKey()))
.map(metric -> dbClient.metricDao().selectByKey(dbSession, metric.getKey()))
.forEach(metricDto -> assertThat(conditions) .forEach(metricDto -> assertThat(conditions)
.anyMatch(c -> metricDto.getUuid().equals(c.getMetricUuid()) && c.getErrorThreshold().equals(String.valueOf(getDefaultCaycValue(metricDto))))); .anyMatch(c -> metricDto.getUuid().equals(c.getMetricUuid()) && c.getErrorThreshold().equals(String.valueOf(getDefaultCaycValue(metricDto)))));
} }


@DataProvider @DataProvider
public static Object[][] nullOrEmpty() { public static Object[][] nullOrEmpty() {
return new Object[][] {
return new Object[][]{
{null}, {null},
{""}, {""},
{" "} {" "}

+ 17
- 18
server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/QualityGateCaycChecker.java 查看文件

package org.sonar.server.qualitygate; package org.sonar.server.qualitygate;


import java.io.Serializable; import java.io.Serializable;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.sonar.api.measures.Metric; import org.sonar.api.measures.Metric;
import org.sonar.db.DbClient; import org.sonar.db.DbClient;
import org.sonar.db.DbSession; import org.sonar.db.DbSession;
import org.sonar.db.metric.MetricDto; import org.sonar.db.metric.MetricDto;
import org.sonar.db.qualitygate.QualityGateConditionDto; 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;
import static org.sonar.api.measures.CoreMetrics.NEW_COVERAGE_KEY; 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;
import static org.sonar.api.measures.CoreMetrics.NEW_DUPLICATED_LINES_DENSITY_KEY; 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_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.COMPLIANT;
import static org.sonar.server.qualitygate.QualityGateCaycStatus.NON_COMPLIANT; import static org.sonar.server.qualitygate.QualityGateCaycStatus.NON_COMPLIANT;
import static org.sonar.server.qualitygate.QualityGateCaycStatus.OVER_COMPLIANT; import static org.sonar.server.qualitygate.QualityGateCaycStatus.OVER_COMPLIANT;


public class QualityGateCaycChecker { 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_HOTSPOTS_REVIEWED,
NEW_SECURITY_RATING,
NEW_DUPLICATED_LINES_DENSITY, NEW_DUPLICATED_LINES_DENSITY,
NEW_COVERAGE 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; private final DbClient dbClient;


public QualityGateCaycChecker(DbClient dbClient) { public QualityGateCaycChecker(DbClient dbClient) {
.orElse(NON_COMPLIANT); .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) { private static boolean checkMetricCaycCompliant(QualityGateConditionDto condition, MetricDto metric) {
if (EXISTENCY_REQUIREMENTS.contains(metric.getKey())) { if (EXISTENCY_REQUIREMENTS.contains(metric.getKey())) {
return true; return true;

+ 3
- 3
server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/ws/CreateAction.java 查看文件

} }


private void addCaycConditions(DbSession dbSession, QualityGateDto newQualityGate) { 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)))
); );
} }



+ 4
- 3
server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/ws/ShowAction.java 查看文件

return dbClient.metricDao().selectByUuids(dbSession, metricUuids).stream().filter(MetricDto::isEnabled).collect(Collectors.toMap(MetricDto::getUuid, Function.identity())); 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() return ShowWsResponse.newBuilder()
.setName(qualityGate.getName()) .setName(qualityGate.getName())
.setIsBuiltIn(qualityGate.isBuiltIn()) .setIsBuiltIn(qualityGate.isBuiltIn())
.build(); .build();
} }


private static Function<QualityGateConditionDto, ShowWsResponse.Condition> toWsCondition(Map<String, MetricDto> metricsByUuid) {
private Function<QualityGateConditionDto, ShowWsResponse.Condition> toWsCondition(Map<String, MetricDto> metricsByUuid) {
return condition -> { return condition -> {
String metricUuid = condition.getMetricUuid(); String metricUuid = condition.getMetricUuid();
MetricDto metric = metricsByUuid.get(metricUuid); MetricDto metric = metricsByUuid.get(metricUuid);
ShowWsResponse.Condition.Builder builder = ShowWsResponse.Condition.newBuilder() ShowWsResponse.Condition.Builder builder = ShowWsResponse.Condition.newBuilder()
.setId(condition.getUuid()) .setId(condition.getUuid())
.setMetric(metric.getKey()) .setMetric(metric.getKey())
.setIsCaycCondition(qualityGateCaycChecker.isCaycCondition(metric))
.setOp(condition.getOperator()); .setOp(condition.getOperator());
ofNullable(condition.getErrorThreshold()).ifPresent(builder::setError); ofNullable(condition.getErrorThreshold()).ifPresent(builder::setError);
return builder.build(); return builder.build();

+ 4
- 3
sonar-ws/src/main/protobuf/ws-qualitygates.proto 查看文件

optional string caycStatus = 6; optional string caycStatus = 6;


message Condition { 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; optional string error = 6;
required bool isCaycCondition = 7;
} }
} }



正在加载...
取消
保存