From 73f0b1065613ffa86f7058695e4ce872a6d7d824 Mon Sep 17 00:00:00 2001 From: Nolwenn Cadic Date: Thu, 9 Nov 2023 16:47:04 +0100 Subject: [PATCH] SONAR-20991 Copy old 'Sonar way' quality gate to 'Sonar way (legacy)' --- .../sonar/server/qualitygate/QualityGate.java | 3 + .../qualitygate/RegisterQualityGatesIT.java | 101 +++++++++++++++--- .../qualitygate/RegisterQualityGates.java | 64 ++++++++--- 3 files changed, 143 insertions(+), 25 deletions(-) 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 d4082e0288c..2f15f0e2cfb 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 @@ -30,6 +30,9 @@ import static java.util.Objects.requireNonNull; public class QualityGate { public static final String BUILTIN_QUALITY_GATE_NAME = "Sonar way"; + + public static final String SONAR_WAY_LEGACY_QUALITY_GATE_NAME = "Sonar way (legacy)"; + private final String id; private final String name; private final Set conditions; diff --git a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/qualitygate/RegisterQualityGatesIT.java b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/qualitygate/RegisterQualityGatesIT.java index 1dad0d2b0a1..7edbfb9e155 100644 --- a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/qualitygate/RegisterQualityGatesIT.java +++ b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/qualitygate/RegisterQualityGatesIT.java @@ -19,12 +19,16 @@ */ package org.sonar.server.qualitygate; +import com.tngtech.java.junit.dataprovider.DataProvider; +import com.tngtech.java.junit.dataprovider.DataProviderRunner; +import com.tngtech.java.junit.dataprovider.UseDataProvider; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Random; import org.junit.Rule; import org.junit.Test; +import org.junit.runner.RunWith; import org.slf4j.event.Level; import org.sonar.api.testfixtures.log.LogTester; import org.sonar.api.utils.System2; @@ -54,16 +58,16 @@ import static org.sonar.api.measures.Metric.ValueType.PERCENT; import static org.sonar.db.metric.MetricTesting.newMetricDto; 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; +import static org.sonar.server.qualitygate.QualityGate.SONAR_WAY_LEGACY_QUALITY_GATE_NAME; +@RunWith(DataProviderRunner.class) public class RegisterQualityGatesIT { @Rule public DbTester db = DbTester.create(); @Rule public LogTester logTester = new LogTester(); - - private static final String BUILT_IN_NAME = "Sonar way"; - private final DbClient dbClient = db.getDbClient(); private final DbSession dbSession = db.getSession(); @@ -114,7 +118,6 @@ public class RegisterQualityGatesIT { underTest.start(); - assertThat(db.countRowsOfTable("quality_gates")).isOne(); verifyCorrectBuiltInQualityGate(); assertThat( logTester.logs(Level.INFO)).contains("Built-in quality gate's conditions of [Sonar way] has been updated"); @@ -131,7 +134,6 @@ public class RegisterQualityGatesIT { underTest.start(); - assertThat(db.countRowsOfTable("quality_gates")).isOne(); verifyCorrectBuiltInQualityGate(); assertThat( logTester.logs(Level.INFO)).contains("Built-in quality gate's conditions of [Sonar way] has been updated"); @@ -149,7 +151,6 @@ public class RegisterQualityGatesIT { underTest.start(); - assertThat(db.countRowsOfTable("quality_gates")).isOne(); verifyCorrectBuiltInQualityGate(); assertThat( logTester.logs(Level.INFO)).contains("Built-in quality gate's conditions of [Sonar way] has been updated"); @@ -169,7 +170,6 @@ public class RegisterQualityGatesIT { underTest.start(); - assertThat(db.countRowsOfTable("quality_gates")).isOne(); verifyCorrectBuiltInQualityGate(); assertThat( logTester.logs(Level.INFO)).contains("Quality gate [Sonar way] has been set as built-in"); @@ -184,7 +184,6 @@ public class RegisterQualityGatesIT { underTest.start(); - assertThat(db.countRowsOfTable("quality_gates")).isOne(); verifyCorrectBuiltInQualityGate(); // Log must not be present assertThat( @@ -208,9 +207,8 @@ public class RegisterQualityGatesIT { QualityGateDto oldQualityGate = qualityGateDao.selectByName(dbSession, qualityGateName); assertThat(oldQualityGate).isNotNull(); assertThat(oldQualityGate.isBuiltIn()).isFalse(); - assertThat(db.select("select name as \"name\" from quality_gates where is_built_in is true")) - .extracting(column -> column.get("name")) - .containsExactly(BUILT_IN_NAME); + var qualityGateDto = qualityGateDao.selectByName(dbSession, BUILTIN_QUALITY_GATE_NAME); + assertThat(qualityGateDto).isNotNull(); assertThat( logTester.logs(Level.INFO)).contains("Built-in quality gate [Sonar way] has been created"); assertThat( @@ -237,6 +235,62 @@ public class RegisterQualityGatesIT { logTester.logs(Level.INFO)).contains("Built-in quality gate's conditions of [Sonar way] has been updated"); } + @Test + public void register_sonar_way_legacy_qg_if_not_exists_and_existing_instance() { + insertMetrics(); + QualityGateDto builtin = new QualityGateDto().setName(BUILTIN_QUALITY_GATE_NAME).setBuiltIn(true).setUuid(Uuids.createFast()); + qualityGateDao.insert(dbSession, builtin); + createBuiltInConditions(builtin); + dbSession.commit(); + + underTest.start(); + + var qualityGateDto = qualityGateDao.selectByName(dbSession, SONAR_WAY_LEGACY_QUALITY_GATE_NAME); + assertThat(qualityGateDto).isNotNull(); + + verifyCorrectSonarWayLegacyQualityGate(); + assertThat( + logTester.logs(Level.INFO)).contains("Sonar way (legacy) quality gate has been created"); + + } + + @Test + @UseDataProvider("data") + public void do_not_register_sonar_way_legacy_qg(boolean isNewInstance, boolean hasSonarWayLegacyQG) { + insertMetrics(); + QualityGateDto builtin = new QualityGateDto().setName(BUILTIN_QUALITY_GATE_NAME).setBuiltIn(true).setUuid(Uuids.createFast()); + qualityGateDao.insert(dbSession, builtin); + if(!isNewInstance) { + createBuiltInConditions(builtin); + } + if(hasSonarWayLegacyQG) { + QualityGateDto sonarWayLegacy = new QualityGateDto().setName(SONAR_WAY_LEGACY_QUALITY_GATE_NAME).setBuiltIn(true).setUuid(Uuids.createFast()); + qualityGateDao.insert(dbSession, sonarWayLegacy); + } + dbSession.commit(); + + underTest.start(); + + var qualityGateDto = qualityGateDao.selectByName(dbSession, SONAR_WAY_LEGACY_QUALITY_GATE_NAME); + if(hasSonarWayLegacyQG) { + assertThat(qualityGateDto).isNotNull(); + } else { + assertThat(qualityGateDto).isNull(); + } + + assertThat( + logTester.logs(Level.INFO)).doesNotContain("Sonar way (legacy) quality gate has been created"); + } + + @DataProvider + public static Object[][] data() { + return new Object[][] { + {false, true}, + {true, true}, + {true, false} + }; + } + private void insertMetrics() { dbClient.metricDao().insert(dbSession, newMetricDto().setKey(NEW_RELIABILITY_RATING_KEY).setValueType(INT.name()).setHidden(false).setDirection(0)); @@ -261,7 +315,7 @@ public class RegisterQualityGatesIT { MetricDto newDuplication = metricDao.selectByKey(dbSession, NEW_DUPLICATED_LINES_DENSITY_KEY); MetricDto newSecurityHotspots = metricDao.selectByKey(dbSession, NEW_SECURITY_HOTSPOTS_REVIEWED_KEY); - QualityGateDto qualityGateDto = qualityGateDao.selectByName(dbSession, BUILT_IN_NAME); + QualityGateDto qualityGateDto = qualityGateDao.selectByName(dbSession, BUILTIN_QUALITY_GATE_NAME); assertThat(qualityGateDto).isNotNull(); assertThat(qualityGateDto.getCreatedAt()).isNotNull(); assertThat(qualityGateDto.isBuiltIn()).isTrue(); @@ -275,6 +329,29 @@ public class RegisterQualityGatesIT { tuple(newSecurityHotspots.getUuid(), OPERATOR_LESS_THAN, "100")); } + private void verifyCorrectSonarWayLegacyQualityGate() { + MetricDto newReliability = metricDao.selectByKey(dbSession, NEW_RELIABILITY_RATING_KEY); + MetricDto newSecurity = metricDao.selectByKey(dbSession, NEW_SECURITY_RATING_KEY); + MetricDto newMaintainability = metricDao.selectByKey(dbSession, NEW_MAINTAINABILITY_RATING_KEY); + MetricDto newCoverage = metricDao.selectByKey(dbSession, NEW_COVERAGE_KEY); + MetricDto newDuplication = metricDao.selectByKey(dbSession, NEW_DUPLICATED_LINES_DENSITY_KEY); + MetricDto newSecurityHotspots = metricDao.selectByKey(dbSession, NEW_SECURITY_HOTSPOTS_REVIEWED_KEY); + QualityGateDto qualityGateDto = qualityGateDao.selectByName(dbSession, SONAR_WAY_LEGACY_QUALITY_GATE_NAME); + assertThat(qualityGateDto).isNotNull(); + assertThat(qualityGateDto.getCreatedAt()).isNotNull(); + assertThat(qualityGateDto.isBuiltIn()).isFalse(); + assertThat(gateConditionDao.selectForQualityGate(dbSession, qualityGateDto.getUuid())) + .extracting(QualityGateConditionDto::getMetricUuid, QualityGateConditionDto::getOperator, + QualityGateConditionDto::getErrorThreshold) + .containsExactlyInAnyOrder( + tuple(newReliability.getUuid(), OPERATOR_GREATER_THAN, "1"), + tuple(newSecurity.getUuid(), OPERATOR_GREATER_THAN, "1"), + tuple(newMaintainability.getUuid(), OPERATOR_GREATER_THAN, "1"), + tuple(newCoverage.getUuid(), OPERATOR_LESS_THAN, "80"), + tuple(newDuplication.getUuid(), OPERATOR_GREATER_THAN, "3"), + tuple(newSecurityHotspots.getUuid(), OPERATOR_LESS_THAN, "100")); + } + private List createBuiltInConditions(QualityGateDto qg) { List conditions = new ArrayList<>(); 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 2184186b809..21946646519 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 @@ -41,27 +41,42 @@ import org.sonar.db.qualitygate.QualityGateConditionDao; import org.sonar.db.qualitygate.QualityGateConditionDto; import org.sonar.db.qualitygate.QualityGateDao; import org.sonar.db.qualitygate.QualityGateDto; +import org.sonar.server.measure.Rating; import static com.google.common.collect.ImmutableList.toImmutableList; import static java.util.Arrays.asList; import static java.util.stream.Collectors.toMap; import static org.sonar.api.measures.CoreMetrics.NEW_COVERAGE_KEY; import static org.sonar.api.measures.CoreMetrics.NEW_DUPLICATED_LINES_DENSITY_KEY; +import static org.sonar.api.measures.CoreMetrics.NEW_MAINTAINABILITY_RATING_KEY; +import static org.sonar.api.measures.CoreMetrics.NEW_RELIABILITY_RATING_KEY; import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_HOTSPOTS_REVIEWED_KEY; +import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_RATING_KEY; 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; +import static org.sonar.server.qualitygate.QualityGate.SONAR_WAY_LEGACY_QUALITY_GATE_NAME; public class RegisterQualityGates implements Startable { private static final Logger LOGGER = LoggerFactory.getLogger(RegisterQualityGates.class); - private static final List QUALITY_GATE_CONDITIONS = asList( + + private static final String A_RATING = Integer.toString(Rating.A.getIndex()); + private static final List BUILT_IN_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"), new QualityGateCondition().setMetricKey(NEW_DUPLICATED_LINES_DENSITY_KEY).setOperator(OPERATOR_GREATER_THAN).setErrorThreshold("3"), new QualityGateCondition().setMetricKey(NEW_SECURITY_HOTSPOTS_REVIEWED_KEY).setOperator(OPERATOR_LESS_THAN).setErrorThreshold("100")); + private static final List SONAR_WAY_LEGACY_QUALITY_GATE_CONDITIONS = asList( + new QualityGateCondition().setMetricKey(NEW_SECURITY_RATING_KEY).setOperator(OPERATOR_GREATER_THAN).setErrorThreshold(A_RATING), + new QualityGateCondition().setMetricKey(NEW_RELIABILITY_RATING_KEY).setOperator(OPERATOR_GREATER_THAN).setErrorThreshold(A_RATING), + new QualityGateCondition().setMetricKey(NEW_MAINTAINABILITY_RATING_KEY).setOperator(OPERATOR_GREATER_THAN).setErrorThreshold(A_RATING), + new QualityGateCondition().setMetricKey(NEW_COVERAGE_KEY).setOperator(OPERATOR_LESS_THAN).setErrorThreshold("80"), + new QualityGateCondition().setMetricKey(NEW_DUPLICATED_LINES_DENSITY_KEY).setOperator(OPERATOR_GREATER_THAN).setErrorThreshold("3"), + new QualityGateCondition().setMetricKey(NEW_SECURITY_HOTSPOTS_REVIEWED_KEY).setOperator(OPERATOR_LESS_THAN).setErrorThreshold("100")); + private final DbClient dbClient; private final QualityGateConditionsUpdater qualityGateConditionsUpdater; private final QualityGateDao qualityGateDao; @@ -86,9 +101,17 @@ public class RegisterQualityGates implements Startable { // Create builtinQualityGate if not present if (builtinQualityGate == null) { LOGGER.info("Built-in quality gate [{}] has been created", BUILTIN_QUALITY_GATE_NAME); - builtinQualityGate = createQualityGate(dbSession, BUILTIN_QUALITY_GATE_NAME); + builtinQualityGate = createQualityGate(dbSession, BUILTIN_QUALITY_GATE_NAME, true); + } + List builtInQualityGateConditions = getQualityGateConditions(dbSession, builtinQualityGate); + + // Create sonar way (legacy) only if it is not a new instance (a new instance has a Sonar way QG and no conditions) and if it is + // not already present + if (!builtInQualityGateConditions.isEmpty()) { + createSonarWayLegacyQualityGateIfMissing(dbSession); } + // Set builtinQualityGate if missing if (!builtinQualityGate.isBuiltIn()) { builtinQualityGate.setBuiltIn(true); @@ -96,7 +119,7 @@ public class RegisterQualityGates implements Startable { LOGGER.info("Quality gate [{}] has been set as built-in", BUILTIN_QUALITY_GATE_NAME); } - updateQualityConditionsIfRequired(dbSession, builtinQualityGate); + updateQualityConditionsIfRequired(dbSession, builtinQualityGate, builtInQualityGateConditions); qualityGateDao.ensureOneBuiltInQualityGate(dbSession, BUILTIN_QUALITY_GATE_NAME); @@ -104,8 +127,18 @@ public class RegisterQualityGates implements Startable { } } - private void updateQualityConditionsIfRequired(DbSession dbSession, QualityGateDto builtinQualityGate) { - List qualityGateConditions = getQualityGateConditions(dbSession, builtinQualityGate); + private void createSonarWayLegacyQualityGateIfMissing(DbSession dbSession) { + QualityGateDto sonarWayLegacyQualityGate = qualityGateDao.selectByName(dbSession, SONAR_WAY_LEGACY_QUALITY_GATE_NAME); + LOGGER.info("Sonar way legacy Gate: {} ", sonarWayLegacyQualityGate); + if (sonarWayLegacyQualityGate == null) { + sonarWayLegacyQualityGate = createQualityGate(dbSession, SONAR_WAY_LEGACY_QUALITY_GATE_NAME, false); + addConditionsToQualityGate(dbSession, sonarWayLegacyQualityGate, SONAR_WAY_LEGACY_QUALITY_GATE_CONDITIONS); + LOGGER.info("Sonar way (legacy) quality gate has been created"); + } + } + + private void updateQualityConditionsIfRequired(DbSession dbSession, QualityGateDto builtinQualityGate, + List qualityGateConditions) { List qgConditionsDeleted = removeExtraConditions(dbSession, builtinQualityGate, qualityGateConditions); qgConditionsDeleted.addAll(removeDuplicatedConditions(dbSession, builtinQualityGate, qualityGateConditions)); @@ -131,7 +164,7 @@ public class RegisterQualityGates implements Startable { // Find all conditions that are not present in QUALITY_GATE_CONDITIONS // Those conditions must be deleted List qgConditionsToBeDeleted = new ArrayList<>(qualityGateConditions); - qgConditionsToBeDeleted.removeAll(QUALITY_GATE_CONDITIONS); + qgConditionsToBeDeleted.removeAll(BUILT_IN_QUALITY_GATE_CONDITIONS); qgConditionsToBeDeleted .forEach(qgc -> qualityGateConditionDao.delete(qgc.toQualityGateDto(builtinQualityGate.getUuid()), dbSession)); return qgConditionsToBeDeleted; @@ -149,26 +182,31 @@ public class RegisterQualityGates implements Startable { return qgConditionsDuplicated; } - private List addMissingConditions(DbSession dbSession, QualityGateDto builtinQualityGate, List qualityGateConditions) { + private List addMissingConditions(DbSession dbSession, QualityGateDto builtinQualityGate, + List qualityGateConditions) { // Find all conditions that are not present in qualityGateConditions // Those conditions must be added to the built-in quality gate - List qgConditionsToBeAdded = new ArrayList<>(QUALITY_GATE_CONDITIONS); + List qgConditionsToBeAdded = new ArrayList<>(BUILT_IN_QUALITY_GATE_CONDITIONS); qgConditionsToBeAdded.removeAll(qualityGateConditions); - qgConditionsToBeAdded - .forEach(qgc -> qualityGateConditionsUpdater.createCondition(dbSession, builtinQualityGate, qgc.getMetricKey(), qgc.getOperator(), - qgc.getErrorThreshold())); + addConditionsToQualityGate(dbSession, builtinQualityGate, qgConditionsToBeAdded); return qgConditionsToBeAdded; } + private void addConditionsToQualityGate(DbSession dbSession, QualityGateDto qualityGate, List conditions) { + conditions.forEach(condition -> qualityGateConditionsUpdater.createCondition(dbSession, qualityGate, condition.getMetricKey(), + condition.getOperator(), + condition.getErrorThreshold())); + } + @Override public void stop() { // do nothing } - private QualityGateDto createQualityGate(DbSession dbSession, String name) { + private QualityGateDto createQualityGate(DbSession dbSession, String name, boolean isBuiltIn) { QualityGateDto qualityGate = new QualityGateDto() .setName(name) - .setBuiltIn(true) + .setBuiltIn(isBuiltIn) .setUuid(uuidFactory.create()) .setCreatedAt(new Date(system2.now())); return dbClient.qualityGateDao().insert(dbSession, qualityGate); -- 2.39.5