diff options
author | Nolwenn Cadic <98824442+Nolwenn-cadic-sonarsource@users.noreply.github.com> | 2023-10-26 14:30:36 +0200 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2023-10-26 20:02:58 +0000 |
commit | 2eaf4da9b14cc5bfa73719b660c5db706672d182 (patch) | |
tree | 0070c5710f6f0c90f5aa333bc373c1a5d29c81a3 | |
parent | c49df4a6f7ee55283c891139d79418a482538769 (diff) | |
download | sonarqube-2eaf4da9b14cc5bfa73719b660c5db706672d182.tar.gz sonarqube-2eaf4da9b14cc5bfa73719b660c5db706672d182.zip |
SONAR-20787 Add telemetry about quality gate conditions and sonar way (#9743)
13 files changed, 200 insertions, 27 deletions
diff --git a/server/sonar-db-dao/src/it/java/org/sonar/db/qualitygate/QualityGateConditionDaoIT.java b/server/sonar-db-dao/src/it/java/org/sonar/db/qualitygate/QualityGateConditionDaoIT.java index 6a46ce044d4..aa5e1c27a5e 100644 --- a/server/sonar-db-dao/src/it/java/org/sonar/db/qualitygate/QualityGateConditionDaoIT.java +++ b/server/sonar-db-dao/src/it/java/org/sonar/db/qualitygate/QualityGateConditionDaoIT.java @@ -33,6 +33,7 @@ import org.sonar.db.metric.MetricDto; import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.AssertionsForClassTypes.tuple; public class QualityGateConditionDaoIT { @@ -82,6 +83,20 @@ public class QualityGateConditionDaoIT { } @Test + public void selectAll() { + MetricDto metric = dbTester.measures().insertMetric(t -> t.setEnabled(true)); + QualityGateConditionDto condition1 = insertQGCondition("uuid1", metric.getUuid()); + QualityGateConditionDto condition2 = insertQGCondition("uuid2", metric.getUuid()); + QualityGateConditionDto condition3 = insertQGCondition("uuid3", metric.getUuid()); + + assertThat(underTest.selectAll(dbSession)) + .extracting(QualityGateConditionDto::getUuid, QualityGateConditionDto::getMetricUuid) + .containsOnly(tuple(condition1.getUuid(), condition1.getMetricUuid()), + tuple(condition2.getUuid(), condition2.getMetricUuid()), + tuple(condition3.getUuid(), condition3.getMetricUuid())); + } + + @Test public void testSelectByUuid() { QualityGateConditionDto condition = insertQGCondition("1", "2", "GT", "20"); diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/QualityGateConditionDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/QualityGateConditionDao.java index 7187d444538..2c412ff9bb3 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/QualityGateConditionDao.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/QualityGateConditionDao.java @@ -34,6 +34,10 @@ public class QualityGateConditionDao implements Dao { return mapper(session).selectForQualityGate(qGateUuid); } + public Collection<QualityGateConditionDto> selectAll(DbSession session) { + return mapper(session).selectAll(); + } + public QualityGateConditionDto selectByUuid(String uuid, DbSession session) { return mapper(session).selectByUuid(uuid); } diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/QualityGateConditionMapper.java b/server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/QualityGateConditionMapper.java index 723bd22e96d..8b9efc7fc93 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/QualityGateConditionMapper.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/qualitygate/QualityGateConditionMapper.java @@ -27,6 +27,8 @@ public interface QualityGateConditionMapper { List<QualityGateConditionDto> selectForQualityGate(String qGateUuid); + List<QualityGateConditionDto> selectAll(); + void update(QualityGateConditionDto newCondition); QualityGateConditionDto selectByUuid(String uuid); diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/qualitygate/QualityGateConditionMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/qualitygate/QualityGateConditionMapper.xml index 2c57a73d936..0efc2c5d5b5 100644 --- a/server/sonar-db-dao/src/main/resources/org/sonar/db/qualitygate/QualityGateConditionMapper.xml +++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/qualitygate/QualityGateConditionMapper.xml @@ -20,6 +20,13 @@ order by created_at asc </select> + <select id="selectAll" resultType="QualityGateCondition"> + select + <include refid="conditionColumns"/> + from quality_gate_conditions + order by qgate_uuid asc + </select> + <select id="selectByUuid" parameterType="String" resultType="QualityGateCondition"> select <include refid="conditionColumns"/> diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/qualitygate/QualityGate.java b/server/sonar-server-common/src/main/java/org/sonar/server/qualitygate/QualityGate.java index c03a0d8f6e6..d4082e0288c 100644 --- a/server/sonar-server-common/src/main/java/org/sonar/server/qualitygate/QualityGate.java +++ b/server/sonar-server-common/src/main/java/org/sonar/server/qualitygate/QualityGate.java @@ -28,6 +28,8 @@ import static java.util.Objects.requireNonNull; @Immutable public class QualityGate { + + public static final String BUILTIN_QUALITY_GATE_NAME = "Sonar way"; private final String id; private final String name; private final Set<Condition> conditions; diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/qualitygate/QualityGateFinder.java b/server/sonar-server-common/src/main/java/org/sonar/server/qualitygate/QualityGateFinder.java index 32dad6ce43c..1bc482d9145 100644 --- a/server/sonar-server-common/src/main/java/org/sonar/server/qualitygate/QualityGateFinder.java +++ b/server/sonar-server-common/src/main/java/org/sonar/server/qualitygate/QualityGateFinder.java @@ -25,6 +25,9 @@ import org.sonar.db.DbSession; import org.sonar.db.project.ProjectDto; import org.sonar.db.qualitygate.QualityGateDto; +import static java.lang.String.format; +import static org.sonar.server.qualitygate.QualityGate.BUILTIN_QUALITY_GATE_NAME; + public class QualityGateFinder { private final DbClient dbClient; @@ -55,6 +58,11 @@ public class QualityGateFinder { return Optional.ofNullable(dbClient.qualityGateDao().selectDefault(dbSession)).orElseThrow(() -> new IllegalStateException("Default quality gate is missing")); } + public QualityGateDto getSonarWay(DbSession dbSession) { + return Optional.ofNullable(dbClient.qualityGateDao().selectByName(dbSession, BUILTIN_QUALITY_GATE_NAME)).orElseThrow(() -> + new IllegalStateException(format("%s quality gate is missing", BUILTIN_QUALITY_GATE_NAME))); + } + public static class QualityGateData { private final String uuid; private final String name; diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/telemetry/TelemetryData.java b/server/sonar-server-common/src/main/java/org/sonar/server/telemetry/TelemetryData.java index 6ec247b38b7..18e545d6ae5 100644 --- a/server/sonar-server-common/src/main/java/org/sonar/server/telemetry/TelemetryData.java +++ b/server/sonar-server-common/src/main/java/org/sonar/server/telemetry/TelemetryData.java @@ -31,6 +31,7 @@ import org.sonar.core.platform.EditionProvider; import org.sonar.core.platform.EditionProvider.Edition; import org.sonar.db.project.CreationMethod; import org.sonar.db.user.UserTelemetryDto; +import org.sonar.server.qualitygate.Condition; import static java.util.Objects.requireNonNullElse; import static org.sonar.db.newcodeperiod.NewCodePeriodType.PREVIOUS_VERSION; @@ -43,6 +44,7 @@ public class TelemetryData { private final Database database; private final EditionProvider.Edition edition; private final String defaultQualityGate; + private final String sonarWayQualityGate; private final Long installationDate; private final String installationVersion; private final boolean inContainer; @@ -68,6 +70,7 @@ public class TelemetryData { database = builder.database; edition = builder.edition; defaultQualityGate = builder.defaultQualityGate; + sonarWayQualityGate = builder.sonarWayQualityGate; installationDate = builder.installationDate; installationVersion = builder.installationVersion; inContainer = builder.inContainer; @@ -114,6 +117,10 @@ public class TelemetryData { return defaultQualityGate; } + public String getSonarWayQualityGate() { + return sonarWayQualityGate; + } + public Long getInstallationDate() { return installationDate; } @@ -190,6 +197,8 @@ public class TelemetryData { private Database database; private Edition edition; private String defaultQualityGate; + + private String sonarWayQualityGate; private Long installationDate; private String installationVersion; private boolean inContainer = false; @@ -246,6 +255,11 @@ public class TelemetryData { return this; } + Builder setSonarWayQualityGate(String sonarWayQualityGate) { + this.sonarWayQualityGate = sonarWayQualityGate; + return this; + } + Builder setInstallationDate(@Nullable Long installationDate) { this.installationDate = installationDate; return this; @@ -360,7 +374,7 @@ public class TelemetryData { record Project(String projectUuid, Long lastAnalysis, String language, String qualityProfile, Long loc) { } - record QualityGate(String uuid, String caycStatus) { + record QualityGate(String uuid, String caycStatus, List<Condition> conditions) { } public record QualityProfile(String uuid, @Nullable String parentUuid, String language, boolean isDefault, diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/telemetry/TelemetryDataJsonWriter.java b/server/sonar-server-common/src/main/java/org/sonar/server/telemetry/TelemetryDataJsonWriter.java index 94315180324..b3148e9ddb3 100644 --- a/server/sonar-server-common/src/main/java/org/sonar/server/telemetry/TelemetryDataJsonWriter.java +++ b/server/sonar-server-common/src/main/java/org/sonar/server/telemetry/TelemetryDataJsonWriter.java @@ -63,6 +63,7 @@ public class TelemetryDataJsonWriter { json.prop(NCD_ID, telemetryData.getNcdId()); telemetryData.getEdition().ifPresent(e -> json.prop("edition", e.name().toLowerCase(Locale.ENGLISH))); json.prop("defaultQualityGate", telemetryData.getDefaultQualityGate()); + json.prop("sonarway_quality_gate_uuid", telemetryData.getSonarWayQualityGate()); json.name("database"); json.beginObject(); json.prop("name", telemetryData.getDatabase().name()); @@ -222,6 +223,16 @@ public class TelemetryDataJsonWriter { json.beginObject(); json.prop("uuid", qualityGate.uuid()); json.prop("caycStatus", qualityGate.caycStatus()); + json.name("conditions"); + json.beginArray(); + qualityGate.conditions().forEach(condition -> { + json.beginObject(); + json.prop("metric", condition.getMetricKey()); + json.prop("comparison_operator", condition.getOperator().getDbValue()); + json.prop("error_value", condition.getErrorThreshold()); + json.endObject(); + }); + json.endArray(); json.endObject(); }); json.endArray(); diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/qualitygate/ConditionTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/qualitygate/ConditionTest.java index f3f24f7a822..947a8e29537 100644 --- a/server/sonar-server-common/src/test/java/org/sonar/server/qualitygate/ConditionTest.java +++ b/server/sonar-server-common/src/test/java/org/sonar/server/qualitygate/ConditionTest.java @@ -40,7 +40,7 @@ public class ConditionTest { } @Test - public void constructor_throws_NPE_if_operator_is_null() { + public void constructor_throws_NPE_if_operator_operator_is_null() { assertThatThrownBy(() -> new Condition(METRIC_KEY, null, ERROR_THRESHOLD)) .isInstanceOf(NullPointerException.class) .hasMessage("operator can't be null"); diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/telemetry/TelemetryDataJsonWriterTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/telemetry/TelemetryDataJsonWriterTest.java index bc98e99ad77..b523f5b0d4a 100644 --- a/server/sonar-server-common/src/test/java/org/sonar/server/telemetry/TelemetryDataJsonWriterTest.java +++ b/server/sonar-server-common/src/test/java/org/sonar/server/telemetry/TelemetryDataJsonWriterTest.java @@ -42,6 +42,7 @@ import org.sonar.core.platform.EditionProvider; import org.sonar.core.telemetry.TelemetryExtension; import org.sonar.db.project.CreationMethod; import org.sonar.db.user.UserTelemetryDto; +import org.sonar.server.qualitygate.Condition; import static java.util.stream.Collectors.joining; import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic; @@ -50,6 +51,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import static org.sonar.db.newcodeperiod.NewCodePeriodType.NUMBER_OF_DAYS; import static org.sonar.db.newcodeperiod.NewCodePeriodType.PREVIOUS_VERSION; +import static org.sonar.server.qualitygate.Condition.Operator.fromDbValue; import static org.sonar.test.JsonAssert.assertJson; @RunWith(DataProviderRunner.class) @@ -121,6 +123,20 @@ public class TelemetryDataJsonWriterTest { } @Test + public void writes_sonarWay_qg() { + TelemetryData data = telemetryBuilder() + .setSonarWayQualityGate("sonarWayUUID") + .build(); + + String json = writeTelemetryData(data); + assertJson(json).isSimilarTo(""" + { + "sonarway_quality_gate_uuid": "%s" + } + """.formatted(data.getSonarWayQualityGate())); + } + + @Test public void writes_database() { String name = randomAlphabetic(12); String version = randomAlphabetic(10); @@ -517,20 +533,56 @@ public class TelemetryDataJsonWriterTest { assertJson(json).isSimilarTo(""" { "quality-gates": [ - { - "uuid": "uuid-0", - "caycStatus": "non-compliant" - }, - { - "uuid": "uuid-1", - "caycStatus": "compliant" - }, - { - "uuid": "uuid-2", - "caycStatus": "over-compliant" - } - ] - } + { + "uuid": "uuid-0", + "caycStatus": "non-compliant", + "conditions": [ + { + "metric": "new_coverage", + "comparison_operator": "LT", + "error_value": "80" + }, + { + "metric": "new_duplicated_lines_density", + "comparison_operator": "GT", + "error_value": "3" + } + ] + }, + { + "uuid": "uuid-1", + "caycStatus": "compliant", + "conditions": [ + { + "metric": "new_coverage", + "comparison_operator": "LT", + "error_value": "80" + }, + { + "metric": "new_duplicated_lines_density", + "comparison_operator": "GT", + "error_value": "3" + } + ] + }, + { + "uuid": "uuid-2", + "caycStatus": "over-compliant", + "conditions": [ + { + "metric": "new_coverage", + "comparison_operator": "LT", + "error_value": "80" + }, + { + "metric": "new_duplicated_lines_density", + "comparison_operator": "GT", + "error_value": "3" + } + ] + } + ] + } """); } @@ -693,9 +745,15 @@ public class TelemetryDataJsonWriterTest { } private List<TelemetryData.QualityGate> attachQualityGates() { - return List.of(new TelemetryData.QualityGate("uuid-0", "non-compliant"), - new TelemetryData.QualityGate("uuid-1", "compliant"), - new TelemetryData.QualityGate("uuid-2", "over-compliant")); + List<Condition> qualityGateConditions = attachQualityGateConditions(); + return List.of(new TelemetryData.QualityGate("uuid-0", "non-compliant", qualityGateConditions), + new TelemetryData.QualityGate("uuid-1", "compliant", qualityGateConditions), + new TelemetryData.QualityGate("uuid-2", "over-compliant", qualityGateConditions)); + } + + private List<Condition> attachQualityGateConditions() { + return List.of(new Condition("new_coverage", fromDbValue("LT"), "80"), + new Condition("new_duplicated_lines_density", fromDbValue("GT"), "3")); } private List<TelemetryData.Branch> attachBranches() { diff --git a/server/sonar-webserver-core/src/it/java/org/sonar/server/telemetry/TelemetryDataLoaderImplIT.java b/server/sonar-webserver-core/src/it/java/org/sonar/server/telemetry/TelemetryDataLoaderImplIT.java index d467e152eaf..0cb082ad3fd 100644 --- a/server/sonar-webserver-core/src/it/java/org/sonar/server/telemetry/TelemetryDataLoaderImplIT.java +++ b/server/sonar-webserver-core/src/it/java/org/sonar/server/telemetry/TelemetryDataLoaderImplIT.java @@ -26,12 +26,14 @@ import java.sql.DatabaseMetaData; import java.sql.SQLException; import java.time.ZoneId; import java.time.ZonedDateTime; +import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Optional; import java.util.Set; import java.util.function.Consumer; import java.util.function.Function; +import java.util.stream.Collectors; import java.util.stream.IntStream; import org.junit.Before; import org.junit.Rule; @@ -53,6 +55,7 @@ import org.sonar.db.metric.MetricDto; import org.sonar.db.newcodeperiod.NewCodePeriodType; import org.sonar.db.project.CreationMethod; import org.sonar.db.property.PropertyDto; +import org.sonar.db.qualitygate.QualityGateConditionDto; import org.sonar.db.qualitygate.QualityGateDto; import org.sonar.db.qualityprofile.QProfileDto; import org.sonar.db.user.UserDbTester; @@ -137,7 +140,7 @@ public class TelemetryDataLoaderImplIT { @Before public void setUpBuiltInQualityGate() { - String builtInQgName = "Sonar Way"; + String builtInQgName = "Sonar way"; builtInDefaultQualityGate = db.qualityGates().insertQualityGate(qg -> qg.setName(builtInQgName).setBuiltIn(true)); when(qualityGateCaycChecker.checkCaycCompliant(any(), any())).thenReturn(NON_COMPLIANT); db.qualityGates().setDefaultQualityGate(builtInDefaultQualityGate); @@ -219,6 +222,9 @@ public class TelemetryDataLoaderImplIT { QualityGateDto qualityGate1 = db.qualityGates().insertQualityGate(qg -> qg.setName("QG1").setBuiltIn(true)); QualityGateDto qualityGate2 = db.qualityGates().insertQualityGate(qg -> qg.setName("QG2")); + QualityGateConditionDto condition1 = db.qualityGates().addCondition(qualityGate1, vulnerabilitiesDto, c -> c.setOperator("GT").setErrorThreshold("80")); + QualityGateConditionDto condition2 = db.qualityGates().addCondition(qualityGate2, securityHotspotsDto, c -> c.setOperator("LT").setErrorThreshold("2")); + // quality profiles QProfileDto javaQP = db.qualityProfiles().insert(qProfileDto -> qProfileDto.setLanguage("java")); QProfileDto kotlinQP = db.qualityProfiles().insert(qProfileDto -> qProfileDto.setLanguage("kotlin")); @@ -245,6 +251,7 @@ public class TelemetryDataLoaderImplIT { assertThat(data.getVersion()).isEqualTo(version); assertThat(data.getEdition()).contains(DEVELOPER); assertThat(data.getDefaultQualityGate()).isEqualTo(builtInDefaultQualityGate.getUuid()); + assertThat(data.getSonarWayQualityGate()).isEqualTo(builtInDefaultQualityGate.getUuid()); assertThat(data.getNcdId()).isEqualTo(NewCodeDefinition.getInstanceDefault().hashCode()); assertThat(data.getMessageSequenceNumber()).isOne(); assertDatabaseMetadata(data.getDatabase()); @@ -294,11 +301,14 @@ public class TelemetryDataLoaderImplIT { tuple("branch", NewCodePeriodType.REFERENCE_BRANCH.name(), branch1.uuid())); assertThat(data.getQualityGates()) - .extracting(TelemetryData.QualityGate::uuid, TelemetryData.QualityGate::caycStatus) + .extracting(TelemetryData.QualityGate::uuid, TelemetryData.QualityGate::caycStatus, + qg -> qg.conditions().stream() + .map(condition -> tuple(condition.getMetricKey(), condition.getOperator().getDbValue(), condition.getErrorThreshold(), condition.isOnLeakPeriod())) + .collect(Collectors.toList())) .containsExactlyInAnyOrder( - tuple(builtInDefaultQualityGate.getUuid(), "non-compliant"), - tuple(qualityGate1.getUuid(), "non-compliant"), - tuple(qualityGate2.getUuid(), "non-compliant")); + tuple(builtInDefaultQualityGate.getUuid(), "non-compliant", Collections.emptyList()), + tuple(qualityGate1.getUuid(), "non-compliant", List.of(tuple(vulnerabilitiesDto.getKey(), condition1.getOperator(), condition1.getErrorThreshold(), false))), + tuple(qualityGate2.getUuid(), "non-compliant", List.of(tuple(securityHotspotsDto.getKey(), condition2.getOperator(), condition2.getErrorThreshold(), false)))); assertThat(data.getQualityProfiles()) .extracting(TelemetryData.QualityProfile::uuid, TelemetryData.QualityProfile::isBuiltIn) @@ -702,6 +712,8 @@ public class TelemetryDataLoaderImplIT { .setCreatedAt(1L)); } + + @DataProvider public static Set<String> getScimFeatureStatues() { HashSet<String> result = new HashSet<>(); diff --git a/server/sonar-webserver-core/src/main/java/org/sonar/server/telemetry/TelemetryDataLoaderImpl.java b/server/sonar-webserver-core/src/main/java/org/sonar/server/telemetry/TelemetryDataLoaderImpl.java index db30d30bec9..47e02b9ef27 100644 --- a/server/sonar-webserver-core/src/main/java/org/sonar/server/telemetry/TelemetryDataLoaderImpl.java +++ b/server/sonar-webserver-core/src/main/java/org/sonar/server/telemetry/TelemetryDataLoaderImpl.java @@ -57,10 +57,12 @@ import org.sonar.db.project.ProjectDto; import org.sonar.db.property.PropertyDto; import org.sonar.db.property.PropertyQuery; import org.sonar.db.qualitygate.ProjectQgateAssociationDto; +import org.sonar.db.qualitygate.QualityGateConditionDto; import org.sonar.db.qualitygate.QualityGateDto; import org.sonar.server.management.ManagedInstanceService; import org.sonar.server.platform.ContainerSupport; import org.sonar.server.property.InternalProperties; +import org.sonar.server.qualitygate.Condition; import org.sonar.server.qualitygate.QualityGateCaycChecker; import org.sonar.server.qualitygate.QualityGateFinder; import org.sonar.server.telemetry.TelemetryData.Database; @@ -86,6 +88,7 @@ import static org.sonar.core.platform.EditionProvider.Edition.ENTERPRISE; import static org.sonar.db.newcodeperiod.NewCodePeriodType.REFERENCE_BRANCH; import static org.sonar.server.metric.UnanalyzedLanguageMetrics.UNANALYZED_CPP_KEY; import static org.sonar.server.metric.UnanalyzedLanguageMetrics.UNANALYZED_C_KEY; +import static org.sonar.server.qualitygate.Condition.Operator.fromDbValue; import static org.sonar.server.telemetry.TelemetryDaemon.I_PROP_MESSAGE_SEQUENCE; @ServerSide @@ -167,9 +170,11 @@ public class TelemetryDataLoaderImpl implements TelemetryDataLoader { data.setNewCodeDefinitions(newCodeDefinitions); String defaultQualityGateUuid = qualityGateFinder.getDefault(dbSession).getUuid(); + String sonarWayQualityGateUuid = qualityGateFinder.getSonarWay(dbSession).getUuid(); List<ProjectDto> projects = dbClient.projectDao().selectProjects(dbSession); data.setDefaultQualityGate(defaultQualityGateUuid); + data.setSonarWayQualityGate(sonarWayQualityGateUuid); resolveUnanalyzedLanguageCode(data, dbSession); resolveProjectStatistics(data, dbSession, defaultQualityGateUuid, projects); resolveProjects(data, dbSession); @@ -217,7 +222,7 @@ public class TelemetryDataLoaderImpl implements TelemetryDataLoader { this.qualityProfileByProjectAndLanguage.clear(); } - private void loadNewCodeDefinitions(DbSession dbSession, List<BranchMeasuresDto> branchMeasuresDtos) { + private void loadNewCodeDefinitions(DbSession dbSession, List<BranchMeasuresDto> branchMeasuresDtos) { var branchUuidByKey = branchMeasuresDtos.stream() .collect(Collectors.toMap(dto -> createBranchUniqueKey(dto.getProjectUuid(), dto.getBranchKey()), BranchMeasuresDto::getBranchUuid)); List<NewCodePeriodDto> newCodePeriodDtos = dbClient.newCodePeriodDao().selectAll(dbSession); @@ -380,15 +385,50 @@ public class TelemetryDataLoaderImpl implements TelemetryDataLoader { private void resolveQualityGates(TelemetryData.Builder data, DbSession dbSession) { List<TelemetryData.QualityGate> qualityGates = new ArrayList<>(); Collection<QualityGateDto> qualityGateDtos = dbClient.qualityGateDao().selectAll(dbSession); + Collection<QualityGateConditionDto> qualityGateConditions = dbClient.gateConditionDao().selectAll(dbSession); + Map<String, MetricDto> metricsByUuid = getMetricsByUuid(dbSession, qualityGateConditions); + + Map<String, List<Condition>> conditionsMap = mapQualityGateConditions(qualityGateConditions, metricsByUuid); + for (QualityGateDto qualityGateDto : qualityGateDtos) { + String qualityGateUuid = qualityGateDto.getUuid(); + List<Condition> conditions = conditionsMap.getOrDefault(qualityGateUuid, Collections.emptyList()); qualityGates.add( new TelemetryData.QualityGate(qualityGateDto.getUuid(), qualityGateCaycChecker.checkCaycCompliant(dbSession, - qualityGateDto.getUuid()).toString())); + qualityGateDto.getUuid()).toString(), conditions)); } data.setQualityGates(qualityGates); } + private static Map<String, List<Condition>> mapQualityGateConditions(Collection<QualityGateConditionDto> qualityGateConditions, Map<String, MetricDto> metricsByUuid) { + Map<String, List<Condition>> conditionsMap = new HashMap<>(); + + for (QualityGateConditionDto condition : qualityGateConditions) { + String qualityGateUuid = condition.getQualityGateUuid(); + + MetricDto metricDto = metricsByUuid.get(condition.getMetricUuid()); + String metricKey = metricDto != null ? metricDto.getKey() : "Unknown Metric"; + + Condition telemetryCondition = new Condition( + metricKey, + fromDbValue(condition.getOperator()), + condition.getErrorThreshold() + ); + + conditionsMap + .computeIfAbsent(qualityGateUuid, k -> new ArrayList<>()) + .add(telemetryCondition); + } + + return conditionsMap; + } + + private Map<String, MetricDto> getMetricsByUuid(DbSession dbSession, Collection<QualityGateConditionDto> conditions) { + Set<String> metricUuids = conditions.stream().map(QualityGateConditionDto::getMetricUuid).collect(Collectors.toSet()); + return dbClient.metricDao().selectByUuids(dbSession, metricUuids).stream().filter(MetricDto::isEnabled).collect(Collectors.toMap(MetricDto::getUuid, Function.identity())); + } + private void resolveUsers(TelemetryData.Builder data, DbSession dbSession) { data.setUsers(dbClient.userDao().selectUsersForTelemetry(dbSession)); } diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/RegisterQualityGates.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/RegisterQualityGates.java index 455ab3be636..2184186b809 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/RegisterQualityGates.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualitygate/RegisterQualityGates.java @@ -51,11 +51,11 @@ import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_HOTSPOTS_REVIEWED_ import static org.sonar.api.measures.CoreMetrics.NEW_VIOLATIONS_KEY; import static org.sonar.db.qualitygate.QualityGateConditionDto.OPERATOR_GREATER_THAN; import static org.sonar.db.qualitygate.QualityGateConditionDto.OPERATOR_LESS_THAN; +import static org.sonar.server.qualitygate.QualityGate.BUILTIN_QUALITY_GATE_NAME; public class RegisterQualityGates implements Startable { private static final Logger LOGGER = LoggerFactory.getLogger(RegisterQualityGates.class); - private static final String BUILTIN_QUALITY_GATE_NAME = "Sonar way"; private static final List<QualityGateCondition> QUALITY_GATE_CONDITIONS = asList( new QualityGateCondition().setMetricKey(NEW_VIOLATIONS_KEY).setOperator(OPERATOR_GREATER_THAN).setErrorThreshold("0"), new QualityGateCondition().setMetricKey(NEW_COVERAGE_KEY).setOperator(OPERATOR_LESS_THAN).setErrorThreshold("80"), |