diff options
author | Julien Lancelot <julien.lancelot@sonarsource.com> | 2016-10-25 19:08:35 +0200 |
---|---|---|
committer | Julien Lancelot <julien.lancelot@sonarsource.com> | 2016-10-26 11:55:16 +0200 |
commit | 331558e106bc9bda51dfa39cc9a5874aa62c236c (patch) | |
tree | 31b012f1259748fdcf430c243babbc4a445f4544 /sonar-db/src/main/java/org/sonar | |
parent | 1609c61c56b527f9a2f73e1419aa5a57d9a6a06d (diff) | |
download | sonarqube-331558e106bc9bda51dfa39cc9a5874aa62c236c.tar.gz sonarqube-331558e106bc9bda51dfa39cc9a5874aa62c236c.zip |
SONAR-8283 Migrate quality gates after removall of IT/overall coverage metrics
Diffstat (limited to 'sonar-db/src/main/java/org/sonar')
3 files changed, 217 insertions, 3 deletions
diff --git a/sonar-db/src/main/java/org/sonar/db/version/DatabaseVersion.java b/sonar-db/src/main/java/org/sonar/db/version/DatabaseVersion.java index 22be5a76f91..86bd8c05b1b 100644 --- a/sonar-db/src/main/java/org/sonar/db/version/DatabaseVersion.java +++ b/sonar-db/src/main/java/org/sonar/db/version/DatabaseVersion.java @@ -30,7 +30,7 @@ import org.sonar.db.MyBatis; public class DatabaseVersion { - public static final int LAST_VERSION = 1_418; + public static final int LAST_VERSION = 1_419; /** * The minimum supported version which can be upgraded. Lower diff --git a/sonar-db/src/main/java/org/sonar/db/version/MigrationStepModule.java b/sonar-db/src/main/java/org/sonar/db/version/MigrationStepModule.java index d2b9d190a08..adf927f7a26 100644 --- a/sonar-db/src/main/java/org/sonar/db/version/MigrationStepModule.java +++ b/sonar-db/src/main/java/org/sonar/db/version/MigrationStepModule.java @@ -177,6 +177,7 @@ import org.sonar.db.version.v62.PopulateOrganizationUuidOfGroupRoles; import org.sonar.db.version.v62.PopulateOrganizationUuidOfGroups; import org.sonar.db.version.v62.PopulateOrganizationUuidOfPermissionTemplates; import org.sonar.db.version.v62.PopulateOrganizationUuidOfUserRoles; +import org.sonar.db.version.v62.UpdateQualityGateConditionsOnCoverage; public class MigrationStepModule extends Module { @Override @@ -374,7 +375,7 @@ public class MigrationStepModule extends Module { MakeOrganizationUuidNotNullOnPermissionTemplates.class, AddOrganizationUuidToGroupRoles.class, PopulateOrganizationUuidOfGroupRoles.class, - MakeOrganizationUuidNotNullOnGroupRoles.class - ); + MakeOrganizationUuidNotNullOnGroupRoles.class, + UpdateQualityGateConditionsOnCoverage.class); } } diff --git a/sonar-db/src/main/java/org/sonar/db/version/v62/UpdateQualityGateConditionsOnCoverage.java b/sonar-db/src/main/java/org/sonar/db/version/v62/UpdateQualityGateConditionsOnCoverage.java new file mode 100644 index 00000000000..0f65516e65f --- /dev/null +++ b/sonar-db/src/main/java/org/sonar/db/version/v62/UpdateQualityGateConditionsOnCoverage.java @@ -0,0 +1,213 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.db.version.v62; + +import com.google.common.collect.ImmutableList; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.Function; +import java.util.stream.Collectors; +import org.sonar.api.measures.CoreMetrics; +import org.sonar.db.Database; +import org.sonar.db.version.BaseDataChange; +import org.sonar.db.version.Select; + +import static java.util.Objects.requireNonNull; +import static org.sonar.core.util.stream.Collectors.uniqueIndex; +import static org.sonar.db.DatabaseUtils.repeatCondition; + +/** + * Migrate every quality gates that have conditions on related coverage metrics. + * + * If there's a condition on {@link CoreMetrics#OVERALL_COVERAGE}, it will be updated to {@link CoreMetrics#COVERAGE}, + * conditions on {@link CoreMetrics#IT_COVERAGE} are removed. + * Else if there's condition on {@link CoreMetrics#COVERAGE}, it will be kept and conditions on {@link CoreMetrics#IT_COVERAGE} are removed. + * Then If there's condition on {@link CoreMetrics#IT_COVERAGE}, it will be updated to {@link CoreMetrics#COVERAGE} + * + * Same strategy is applied on new_XXX, (it_|overall_)lines_to_cover, (it_|overall_)uncovered_lines, etc. related coverage metrics. + */ +public class UpdateQualityGateConditionsOnCoverage extends BaseDataChange { + + private static final List<String> COVERAGE_METRIC_KEYS = ImmutableList.of( + "coverage", "lines_to_cover", "uncovered_lines", "line_coverage", "conditions_to_cover", "uncovered_conditions", "branch_coverage"); + + private static final String OVERALL_PREFIX = "overall_"; + private static final String IT_PREFIX = "it_"; + private static final String NEW_PREFIX = "new_"; + + public UpdateQualityGateConditionsOnCoverage(Database db) { + super(db); + } + + @Override + public void execute(Context context) throws SQLException { + List<Metric> metrics = selectMetrics(context); + if (metrics.isEmpty()) { + return; + } + List<Long> qualityGateIds = context.prepareSelect("select id from quality_gates").list(Select.LONG_READER); + if (qualityGateIds.isEmpty()) { + return; + } + new Migration(context, metrics, qualityGateIds).execute(); + } + + private static class Migration { + private final Context context; + private final Map<String, Metric> metricsByMetricKeys; + private final List<Long> metricIds; + private final List<Long> qualityGateIds; + + Migration(Context context, List<Metric> metrics, List<Long> qualityGateIds) { + this.context = context; + this.metricsByMetricKeys = metrics.stream().collect(uniqueIndex(Metric::getKey, Function.identity())); + this.metricIds = metrics.stream().map(Metric::getId).collect(Collectors.toList()); + this.qualityGateIds = qualityGateIds; + } + + public void execute() { + qualityGateIds.forEach(this::processQualityGate); + } + + private void processQualityGate(long qualityGateId) { + List<QualityGateCondition> qualityGateConditions = selectQualityGateConditions(qualityGateId, metricIds); + Map<Long, QualityGateCondition> qualityGateConditionsByMetricId = qualityGateConditions.stream() + .collect(uniqueIndex(QualityGateCondition::getMetricId, Function.identity())); + COVERAGE_METRIC_KEYS.forEach(metric -> { + processConditions(metric, OVERALL_PREFIX + metric, IT_PREFIX + metric, qualityGateConditionsByMetricId, qualityGateId); + processConditions(NEW_PREFIX + metric, NEW_PREFIX + OVERALL_PREFIX + metric, NEW_PREFIX + IT_PREFIX + metric, qualityGateConditionsByMetricId, qualityGateId); + }); + } + + private void processConditions(String coverageMetricKey, String overallMetricKey, String itMetricKey, Map<Long, QualityGateCondition> qualityGateConditionsByMetricId, + long qualityGateId) { + try { + Optional<QualityGateCondition> conditionOnCoverage = getConditionByMetricKey(coverageMetricKey, qualityGateConditionsByMetricId); + Optional<QualityGateCondition> conditionOnOverallCoverage = getConditionByMetricKey(overallMetricKey, qualityGateConditionsByMetricId); + Optional<QualityGateCondition> conditionOnItCoverage = getConditionByMetricKey(itMetricKey, qualityGateConditionsByMetricId); + if (!conditionOnCoverage.isPresent() && !conditionOnOverallCoverage.isPresent() && !conditionOnItCoverage.isPresent()) { + return; + } + if (conditionOnOverallCoverage.isPresent()) { + removeQualityGateCondition(conditionOnCoverage); + removeQualityGateCondition(conditionOnItCoverage); + updateQualityGateCondition(conditionOnOverallCoverage.get().getId(), coverageMetricKey); + } else if (conditionOnCoverage.isPresent()) { + removeQualityGateCondition(conditionOnItCoverage); + } else { + updateQualityGateCondition(conditionOnItCoverage.get().getId(), coverageMetricKey); + } + } catch (SQLException e) { + throw new IllegalStateException(String.format("Fail to update quality gate conditions of quality gate %s", qualityGateId), e); + } + } + + private Optional<QualityGateCondition> getConditionByMetricKey(String metricKey, Map<Long, QualityGateCondition> qualityGateConditionsByMetricId) { + Metric metric = metricsByMetricKeys.get(metricKey); + if (metric == null) { + return Optional.empty(); + } + return Optional.ofNullable(qualityGateConditionsByMetricId.get(metric.getId())); + } + + private List<QualityGateCondition> selectQualityGateConditions(long qualityGateId, List<Long> metricIds) { + try { + Select select = context.prepareSelect("select qgc.id, qgc.metric_id " + + "from quality_gate_conditions qgc " + + "where qgc.qgate_id=? and qgc.metric_id in (" + repeatCondition("?", metricIds.size(), ",") + ")") + .setLong(1, qualityGateId); + for (int i = 0; i < metricIds.size(); i++) { + select.setLong(i + 2, metricIds.get(i)); + } + return select.list(QualityGateCondition::new); + } catch (SQLException e) { + throw new IllegalStateException(String.format("Fail to select quality gate conditions of quality gate %s", qualityGateId), e); + } + } + + private void updateQualityGateCondition(long id, String metricKey) throws SQLException { + context.prepareUpsert("update quality_gate_conditions set metric_id=? where id=?") + .setLong(1, metricsByMetricKeys.get(metricKey).getId()) + .setLong(2, id) + .execute() + .commit(); + } + + private void removeQualityGateCondition(Optional<QualityGateCondition> condition) throws SQLException { + if (!condition.isPresent()) { + return; + } + context.prepareUpsert("delete from quality_gate_conditions where id=?").setLong(1, condition.get().getId()) + .execute() + .commit(); + } + } + + private static List<Metric> selectMetrics(Context context) throws SQLException { + List<String> metricKeys = new ArrayList<>(COVERAGE_METRIC_KEYS); + metricKeys.addAll(COVERAGE_METRIC_KEYS.stream().map(metricKey -> IT_PREFIX + metricKey).collect(Collectors.toList())); + metricKeys.addAll(COVERAGE_METRIC_KEYS.stream().map(metricKey -> OVERALL_PREFIX + metricKey).collect(Collectors.toList())); + metricKeys.addAll(metricKeys.stream().map(metricKey -> NEW_PREFIX + metricKey).collect(Collectors.toList())); + Select select = context.prepareSelect("select id, name from metrics where name in (" + repeatCondition("?", metricKeys.size(), ",") + ")"); + for (int i = 0; i < metricKeys.size(); i++) { + select.setString(i + 1, metricKeys.get(i)); + } + return select.list(Metric::new); + } + + private static class QualityGateCondition { + private final long id; + private final long metricId; + + QualityGateCondition(Select.Row row) throws SQLException { + this.id = requireNonNull(row.getLong(1)); + this.metricId = requireNonNull(row.getLong(2)); + } + + long getId() { + return id; + } + + long getMetricId() { + return metricId; + } + } + + private static class Metric { + private final long id; + private final String key; + + Metric(Select.Row row) throws SQLException { + this.id = requireNonNull(row.getLong(1)); + this.key = requireNonNull(row.getString(2)); + } + + long getId() { + return id; + } + + String getKey() { + return key; + } + } +} |