]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-23998 Add migration of deprecated json metrics to measures migration
authorLéo Geoffroy <leo.geoffroy@sonarsource.com>
Tue, 29 Oct 2024 07:32:06 +0000 (08:32 +0100)
committersonartech <sonartech@sonarsource.com>
Tue, 5 Nov 2024 20:03:02 +0000 (20:03 +0000)
fixup! SONAR-23998 Add migration of deprecated json metrics to measures migration

SONAR-23998 Add a unit test to check on cenversion failure

server/sonar-db-migration/src/it/java/org/sonar/server/platform/db/migration/version/v108/MigrateBranchesLiveMeasuresToMeasuresIT.java
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v108/AbstractMigrateLiveMeasuresToMeasures.java
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v108/MeasureMigration.java [new file with mode: 0644]

index a3ff63478f3ec2c0cf758949f05cab2fe3886709..74813eea032f90a3e77585d4e3d1c9f8d29279eb 100644 (file)
@@ -19,6 +19,7 @@
  */
 package org.sonar.server.platform.db.migration.version.v108;
 
+import com.google.gson.Gson;
 import java.nio.charset.StandardCharsets;
 import java.sql.SQLException;
 import java.util.HashMap;
@@ -29,8 +30,10 @@ import java.util.stream.Collectors;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.RegisterExtension;
 import org.slf4j.event.Level;
+import org.sonar.api.measures.CoreMetrics;
 import org.sonar.api.testfixtures.log.LogTesterJUnit5;
 import org.sonar.api.utils.System2;
+import org.sonar.core.metric.SoftwareQualitiesMetrics;
 import org.sonar.core.util.SequenceUuidFactory;
 import org.sonar.db.MigrationDbTester;
 import org.sonar.server.platform.db.migration.step.DataChange;
@@ -190,6 +193,82 @@ class MigrateBranchesLiveMeasuresToMeasuresIT {
       .isEmpty();
   }
 
+  @Test
+  void should_include_new_measures_based_on_previous_available_measures() throws SQLException {
+    Set<String> metricsToMigrate = MeasureMigration.MIGRATION_MAP.keySet().stream().map(e -> insertMetric(e, "DATA"))
+      .collect(Collectors.toSet());
+
+    String branch = "branch_4";
+    insertNotMigratedBranch(branch);
+    String component1 = uuidFactory.create();
+    metricsToMigrate.forEach(metricUuid -> insertMeasure(branch, component1, metricUuid,
+      Map.of("measure_data", "{\"LOW\":3,\"MEDIUM\":0,\"HIGH\":4,\"total\":7}")));
+
+    underTest.execute();
+
+    assertBranchMigrated(branch);
+    assertThat(db.countRowsOfTable("measures")).isEqualTo(1);
+
+    List<Map<String, Object>> measuresFromDB = db.select(format(SELECT_MEASURE, component1));
+    assertThat(measuresFromDB).hasSize(1);
+
+    Gson gson = new Gson();
+    Map<String, Object> jsonValue = gson.fromJson((String) measuresFromDB.get(0).get("json_value"), Map.class);
+
+    Map<String, Object> expectedExistingMetrics = MeasureMigration.MIGRATION_MAP.keySet().stream().collect(
+      Collectors.toMap(s -> s, s -> "{\"LOW\":3,\"MEDIUM\":0,\"HIGH\":4,\"total\":7}"));
+
+    Map<String, Object> expectedNewMetrics = MeasureMigration.MIGRATION_MAP.values().stream().collect(
+      Collectors.toMap(s -> s, s -> 7.0));
+
+    assertThat(jsonValue).containsAllEntriesOf(expectedExistingMetrics).containsAllEntriesOf(expectedNewMetrics);
+  }
+
+  @Test
+  void should_migrate_other_measures_when_there_is_an_error_converting_previous_measures() throws SQLException {
+    String nclocMetricUuid = insertMetric("ncloc", "INT");
+    String maintainabilityMetricUuid = insertMetric(CoreMetrics.MAINTAINABILITY_ISSUES_KEY, "DATA");
+    String reliabilityMetricUuid = insertMetric(CoreMetrics.RELIABILITY_ISSUES_KEY, "DATA");
+    String securityMetricUuid = insertMetric(CoreMetrics.SECURITY_ISSUES_KEY, "DATA");
+
+    String branch = "branch_4";
+    insertNotMigratedBranch(branch);
+    String component1 = uuidFactory.create();
+    insertMeasure(branch, component1, nclocMetricUuid, Map.of("value", 120));
+    // total is not a number
+    insertMeasure(branch, component1, maintainabilityMetricUuid, Map.of("measure_data", "{\"LOW\":3,\"MEDIUM\":0,\"HIGH\":4," +
+      "\"total\":\"ABC\"}"));
+    // total cannot fit in a long
+    insertMeasure(branch, component1, reliabilityMetricUuid, Map.of("measure_data", "{\"LOW\":3,\"MEDIUM\":0,\"HIGH\":4," +
+      "\"total\":98723987498723987429874928748748}"));
+    insertMeasure(branch, component1, securityMetricUuid, Map.of("measure_data", "{\"LOW\":3,\"MEDIUM\":0,\"HIGH\":4,\"total\":37}"));
+
+    logTester.setLevel(Level.DEBUG);
+    underTest.execute();
+
+    assertBranchMigrated(branch);
+    assertThat(db.countRowsOfTable("measures")).isEqualTo(1);
+
+    List<Map<String, Object>> measuresFromDB = db.select(format(SELECT_MEASURE, component1));
+    assertThat(measuresFromDB).hasSize(1);
+
+    Gson gson = new Gson();
+    Map<String, Object> jsonValue = gson.fromJson((String) measuresFromDB.get(0).get("json_value"), Map.class);
+
+    Map<String, Object> expectedExistingMetrics = Map.of(
+      "ncloc", 120.0,
+      CoreMetrics.MAINTAINABILITY_ISSUES_KEY, "{\"LOW\":3,\"MEDIUM\":0,\"HIGH\":4,\"total\":\"ABC\"}",
+      CoreMetrics.RELIABILITY_ISSUES_KEY, "{\"LOW\":3,\"MEDIUM\":0,\"HIGH\":4,\"total\":98723987498723987429874928748748}",
+      CoreMetrics.SECURITY_ISSUES_KEY, "{\"LOW\":3,\"MEDIUM\":0,\"HIGH\":4,\"total\":37}"
+    );
+
+    Map<String, Object> expectedNewMetrics = Map.of(SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_ISSUES_KEY, 37.0);
+
+    assertThat(jsonValue).hasSize(5).containsAllEntriesOf(expectedExistingMetrics).containsAllEntriesOf(expectedNewMetrics);
+    assertThat(logTester.logs(Level.DEBUG)).contains("Failed to migrate metric reliability_issues with value {\"LOW\":3,\"MEDIUM\":0," +
+      "\"HIGH\":4,\"total\":98723987498723987429874928748748}");
+  }
+
   private void assertBranchMigrated(String branch) {
     List<Map<String, Object>> result = db.select(format("select %s as \"MIGRATED\" from project_branches where uuid = '%s'", MEASURES_MIGRATED_COLUMN, branch));
     assertThat(result)
index 5ca1074bd787f043f39e35b83671fef963f3061e..3421bd0b977b5a4561e20c34d2fb2349d6c2b34d 100644 (file)
@@ -207,13 +207,24 @@ public abstract class AbstractMigrateLiveMeasuresToMeasures extends DataChange {
 
     Object metricValue = getMetricValue(data, textValue, valueType, numericValue);
     if (metricValue != null
-      && !measuresPlannedForDeletion(metricName)) {
+      && !MeasureMigration.isMetricPlannedForDeletion(metricName)) {
       measureValues.put(metricName, metricValue);
+      migrateMeasureIfNeeded(measureValues, metricName, metricValue);
     }
   }
 
-  private static boolean measuresPlannedForDeletion(String metricName) {
-    return DeleteSoftwareQualityRatingFromProjectMeasures.SOFTWARE_QUALITY_METRICS_TO_DELETE.contains(metricName);
+  private static void migrateMeasureIfNeeded(Map<String, Object> measureValues, String metricName, Object metricValue) {
+    String migratedMetricKey = MeasureMigration.getMigrationMetricKey(metricName);
+    if (migratedMetricKey != null) {
+      try {
+        Object migratedValue = MeasureMigration.migrate(metricValue);
+        if (migratedValue != null) {
+          measureValues.put(migratedMetricKey, migratedValue);
+        }
+      } catch (Exception e) {
+        LOGGER.debug("Failed to migrate metric {} with value {}", metricName, metricValue);
+      }
+    }
   }
 
   private static Object getMetricValue(@Nullable byte[] data, @Nullable String textValue, String valueType, Double numericValue) {
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v108/MeasureMigration.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v108/MeasureMigration.java
new file mode 100644 (file)
index 0000000..1ab0ec0
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2024 SonarSource SA
+ * mailto:info 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.server.platform.db.migration.version.v108;
+
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import javax.annotation.CheckForNull;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.core.metric.SoftwareQualitiesMetrics;
+
+public class MeasureMigration {
+
+  static final Pattern VALUE_EXTRACTION_PATTERN = Pattern.compile("\"total\":(\\d+)");
+
+  static final Map<String, String> MIGRATION_MAP = Map.of(
+    CoreMetrics.MAINTAINABILITY_ISSUES_KEY, SoftwareQualitiesMetrics.SOFTWARE_QUALITY_MAINTAINABILITY_ISSUES_KEY,
+    CoreMetrics.NEW_MAINTAINABILITY_ISSUES_KEY, SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_MAINTAINABILITY_ISSUES_KEY,
+    CoreMetrics.RELIABILITY_ISSUES_KEY, SoftwareQualitiesMetrics.SOFTWARE_QUALITY_RELIABILITY_ISSUES_KEY,
+    CoreMetrics.NEW_RELIABILITY_ISSUES_KEY, SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_RELIABILITY_ISSUES_KEY,
+    CoreMetrics.SECURITY_ISSUES_KEY, SoftwareQualitiesMetrics.SOFTWARE_QUALITY_SECURITY_ISSUES_KEY,
+    CoreMetrics.NEW_SECURITY_ISSUES_KEY, SoftwareQualitiesMetrics.NEW_SOFTWARE_QUALITY_SECURITY_ISSUES_KEY);
+
+  private MeasureMigration() {
+    //Only static methods
+  }
+
+  @CheckForNull
+  public static String getMigrationMetricKey(String metricKey) {
+    return MIGRATION_MAP.get(metricKey);
+  }
+
+  @CheckForNull
+  public static Object migrate(Object value) {
+    Matcher matcher = VALUE_EXTRACTION_PATTERN.matcher(value.toString());
+    if (matcher.find()) {
+      return Long.valueOf(matcher.group(1));
+    }
+    return null;
+  }
+
+  public static boolean isMetricPlannedForDeletion(String metricKey) {
+    return DeleteSoftwareQualityRatingFromProjectMeasures.SOFTWARE_QUALITY_METRICS_TO_DELETE.contains(metricKey);
+  }
+}