]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-23363 Database Migration for manual_severity flag
authorOrlovAlexander <alexander.orlov@sonarsource.com>
Tue, 5 Nov 2024 15:48:04 +0000 (16:48 +0100)
committersonartech <sonartech@sonarsource.com>
Mon, 11 Nov 2024 20:02:43 +0000 (20:02 +0000)
server/sonar-db-dao/src/it/java/org/sonar/db/issue/IssueDaoIT.java
server/sonar-db-dao/src/main/java/org/sonar/db/issue/ImpactDto.java
server/sonar-db-dao/src/main/resources/org/sonar/db/issue/IssueMapper.xml
server/sonar-db-dao/src/schema/schema-sq.ddl
server/sonar-db-migration/src/it/java/org/sonar/server/platform/db/migration/version/v108/AddManualSeverityColumnInIssuesImpactsTableTest.java [new file with mode: 0644]
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v108/AddManualSeverityColumnInIssuesImpactsTable.java [new file with mode: 0644]
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v108/DbVersion108.java

index ef3a44abbb1e50c703cb88cc49771a0bf84f1ef6..15758d0f49b8bc89ec6959511687b9a17a307e88 100644 (file)
@@ -20,7 +20,6 @@
 package org.sonar.db.issue;
 
 import java.security.SecureRandom;
-import java.sql.SQLException;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Date;
@@ -184,10 +183,10 @@ class IssueDaoIT {
     assertThat(issue.getEffectiveCleanCodeAttribute()).isEqualTo(RULE.getCleanCodeAttribute());
     assertThat(issue.parseLocations()).isNull();
     assertThat(issue.getImpacts())
-      .extracting(ImpactDto::getSeverity, ImpactDto::getSoftwareQuality)
+      .extracting(ImpactDto::getSeverity, ImpactDto::getSoftwareQuality, ImpactDto::isManualSeverity)
       .containsExactlyInAnyOrder(
-        tuple(MEDIUM, RELIABILITY),
-        tuple(LOW, SECURITY));
+        tuple(MEDIUM, RELIABILITY, false),
+        tuple(LOW, SECURITY, false));
     assertThat(issue.getRuleDefaultImpacts())
       .extracting(ImpactDto::getSeverity, ImpactDto::getSoftwareQuality)
       .containsExactlyInAnyOrder(tuple(HIGH, MAINTAINABILITY));
@@ -238,7 +237,7 @@ class IssueDaoIT {
   }
 
   @Test
-  void scrollIndexationIssues_shouldReturnDto() throws SQLException {
+  void scrollIndexationIssues_shouldReturnDto() {
     ComponentDto project = db.components().insertPrivateProject().getMainBranchComponent();
     RuleDto rule = db.rules().insert(r -> r.setRepositoryKey("java").setLanguage("java")
       .replaceAllDefaultImpacts(List.of(new ImpactDto()
@@ -251,7 +250,7 @@ class IssueDaoIT {
     ComponentDto branchA = db.components().insertProjectBranch(project, b -> b.setKey("branchA"));
     ComponentDto fileA = db.components().insertComponent(newFileDto(branchA));
 
-    IntStream.range(0, 100).forEach(i -> insertBranchIssue(branchA, fileA, rule, "A" + i, STATUS_OPEN, 1_340_000_000_000L));
+    IntStream.range(0, 100).forEach(i -> insertBranchIssueWithManualSeverity(branchA, fileA, rule, "A" + i, STATUS_OPEN, 1_340_000_000_000L));
 
     Cursor<IndexedIssueDto> issues = underTest.scrollIssuesForIndexation(db.getSession(), null, null);
 
@@ -260,14 +259,15 @@ class IssueDaoIT {
     while (iterator.hasNext()) {
       IndexedIssueDto next = iterator.next();
       assertThat(next.getRuleDefaultImpacts()).hasSize(2)
-        .extracting(ImpactDto::getSoftwareQuality, ImpactDto::getSeverity)
+        .extracting(ImpactDto::getSoftwareQuality, ImpactDto::getSeverity, ImpactDto::isManualSeverity)
         .containsExactlyInAnyOrder(
-          tuple(RELIABILITY, LOW),
-          tuple(MAINTAINABILITY, MEDIUM));
+          tuple(RELIABILITY, LOW, false),
+          tuple(MAINTAINABILITY, MEDIUM, false));
       assertThat(next.getImpacts())
-        .extracting(ImpactDto::getSoftwareQuality, ImpactDto::getSeverity)
+        .extracting(ImpactDto::getSoftwareQuality, ImpactDto::getSeverity, ImpactDto::isManualSeverity)
         .containsExactlyInAnyOrder(
-          tuple(MAINTAINABILITY, HIGH));
+          tuple(MAINTAINABILITY, HIGH, true),
+          tuple(RELIABILITY, LOW, false));
       issueCount++;
     }
     assertThat(issueCount).isEqualTo(100);
@@ -792,10 +792,12 @@ class IssueDaoIT {
   void insert_shouldInsertBatchIssuesWithImpacts() {
     ImpactDto impact1 = new ImpactDto()
       .setSoftwareQuality(MAINTAINABILITY)
-      .setSeverity(HIGH);
+      .setSeverity(HIGH)
+      .setManualSeverity(false);
     ImpactDto impact2 = new ImpactDto()
       .setSoftwareQuality(SECURITY)
-      .setSeverity(LOW);
+      .setSeverity(LOW)
+      .setManualSeverity(true);
     IssueDto issue1 = createIssueWithKey(ISSUE_KEY1)
       .addImpact(impact1)
       .addImpact(impact2);
@@ -840,7 +842,8 @@ class IssueDaoIT {
       .setSeverity(HIGH);
     ImpactDto impact2 = new ImpactDto()
       .setSoftwareQuality(SECURITY)
-      .setSeverity(LOW);
+      .setSeverity(LOW)
+      .setManualSeverity(true);
     IssueDto issue1 = createIssueWithKey(ISSUE_KEY1)
       .addImpact(impact1)
       .addImpact(impact2);
@@ -1098,6 +1101,20 @@ class IssueDaoIT {
       .containsExactlyInAnyOrder(tuple(RELIABILITY, MEDIUM), tuple(SECURITY, LOW));
   }
 
+  @Test
+  void insertIssueImpacts_should_insert_all_values() {
+    IssueDto issueDto = createIssueWithKey(ISSUE_KEY1);
+    ImpactDto impactDto1 = new ImpactDto(MAINTAINABILITY, MEDIUM, true);
+    ImpactDto impactDto2 = new ImpactDto(RELIABILITY, HIGH, false);
+    issueDto.addImpact(impactDto1);
+    issueDto.addImpact(impactDto2);
+    underTest.insert(db.getSession(), issueDto);
+
+    assertThat(underTest.selectOrFailByKey(db.getSession(), ISSUE_KEY1).getImpacts()).extracting(ImpactDto::getSoftwareQuality,
+        ImpactDto::getSeverity, ImpactDto::isManualSeverity)
+      .containsExactlyInAnyOrder(tuple(MAINTAINABILITY, MEDIUM, true), tuple(RELIABILITY, HIGH, false));
+  }
+
   private static IssueDto createIssueWithKey(String issueKey) {
     return createIssueWithKey(issueKey, PROJECT_UUID, FILE_UUID);
   }
@@ -1178,6 +1195,26 @@ class IssueDaoIT {
       .setMessageFormattings(MESSAGE_FORMATTING));
   }
 
+  private void insertBranchIssueWithManualSeverity(ComponentDto branch, ComponentDto file, RuleDto rule, String id, String status,
+    Long updateAt) {
+    db.issues().insert(rule, branch, file, i -> i.setKee("issue" + id)
+      .setStatus(status)
+      .setResolution(null)
+      .setUpdatedAt(updateAt)
+      .setType(randomRuleTypeExceptHotspot())
+      .setMessage("message")
+      .setMessageFormattings(MESSAGE_FORMATTING)
+      .replaceAllImpacts(List.of(
+        new ImpactDto()
+          .setSoftwareQuality(MAINTAINABILITY)
+          .setSeverity(HIGH)
+          .setManualSeverity(true),
+        new ImpactDto()
+          .setSoftwareQuality(RELIABILITY)
+          .setSeverity(LOW)
+          .setManualSeverity(false))));
+  }
+
   private void insertBranchIssue(ComponentDto branch, ComponentDto file, RuleDto rule, String id, String status, Long updateAt) {
     insertBranchIssue(branch, file, rule, id, status, null, updateAt);
   }
index 4cf2ec95e1a5bed0c62bf6aa55e066870566ee1c..389c549649b8df4dfee3b13f072f68a71a6368a5 100644 (file)
@@ -27,14 +27,20 @@ import org.sonar.api.issue.impact.SoftwareQuality;
 public class ImpactDto implements Serializable {
   private SoftwareQuality softwareQuality;
   private Severity severity;
+  private boolean manualSeverity;
 
   public ImpactDto() {
     // nothing to do
   }
 
-  public ImpactDto(SoftwareQuality softwareQuality, Severity severity) {
+  public ImpactDto(SoftwareQuality softwareQuality, Severity severity, boolean manualSeverity) {
     this.softwareQuality = softwareQuality;
     this.severity = severity;
+    this.manualSeverity = manualSeverity;
+  }
+
+  public ImpactDto(SoftwareQuality softwareQuality, Severity severity) {
+    this(softwareQuality, severity, false);
   }
 
   public SoftwareQuality getSoftwareQuality() {
@@ -55,6 +61,15 @@ public class ImpactDto implements Serializable {
     return this;
   }
 
+  public boolean isManualSeverity() {
+    return manualSeverity;
+  }
+
+  public ImpactDto setManualSeverity(boolean manualSeverity) {
+    this.manualSeverity = manualSeverity;
+    return this;
+  }
+
   @Override
   public boolean equals(Object o) {
     if (this == o) {
@@ -67,12 +82,13 @@ public class ImpactDto implements Serializable {
 
     ImpactDto impactDto = (ImpactDto) o;
     return Objects.equals(softwareQuality, impactDto.softwareQuality)
-      && Objects.equals(severity, impactDto.severity);
+      && Objects.equals(severity, impactDto.severity)
+      && Objects.equals(manualSeverity, impactDto.manualSeverity);
   }
 
   @Override
   public int hashCode() {
-    return Objects.hash(softwareQuality, severity);
+    return Objects.hash(softwareQuality, severity, manualSeverity);
   }
 
 }
index 0dbe81a387c4f9e63843361c4a78e71d6c1c6480..1515c039681768f7eee310101c4650834853d702 100644 (file)
   <sql id="issueImpactsColumns">
     ii.software_quality as "ii_softwareQuality",
     ii.severity as "ii_severity",
+    ii.manual_severity as "ii_manualSeverity",
   </sql>
 
   <sql id="ruleDefaultImpactsColumns">
                 javaType="java.util.Set" ofType="Impact">
       <result property="softwareQuality" column="ii_softwareQuality"/>
       <result property="severity" column="ii_severity"/>
+      <result property="manualSeverity" column="ii_manualSeverity"/>
     </collection>
     <collection property="ruleDefaultImpacts" column="rdi_softwareQuality" notNullColumn="rdi_softwareQuality"
                 javaType="java.util.Set" ofType="Impact">
   </insert>
 
   <insert id="insertIssueImpact" parameterType="map" useGeneratedKeys="false">
-    INSERT INTO issues_impacts (issue_key, software_quality, severity)
+    INSERT INTO issues_impacts (issue_key, software_quality, severity, manual_severity)
     VALUES (
     #{issueKey,jdbcType=VARCHAR},
     #{dto.softwareQuality,jdbcType=VARCHAR},
-    #{dto.severity,jdbcType=VARCHAR})
+    #{dto.severity,jdbcType=VARCHAR},
+    #{dto.manualSeverity,jdbcType=BOOLEAN})
   </insert>
 
   <delete id="deleteAsNewCodeOnReferenceBranch" parameterType="String">
                 javaType="java.util.Set" ofType="Impact">
       <result property="softwareQuality" column="ii_softwareQuality"/>
       <result property="severity" column="ii_severity"/>
+      <result property="manualSeverity" column="ii_manualSeverity"/>
     </collection>
     <collection property="ruleDefaultImpacts" column="rdi_softwareQuality" notNullColumn="rdi_softwareQuality"
                 javaType="java.util.Set" ofType="Impact">
index 272b1c156a19dffad889627d27a4cf36e14ea8e2..b6a46b9bc40c96742cbdc9ad19920b85da4ea9c2 100644 (file)
@@ -511,7 +511,8 @@ ALTER TABLE "ISSUES_FIXED" ADD CONSTRAINT "PK_ISSUES_FIXED" PRIMARY KEY("PULL_RE
 CREATE TABLE "ISSUES_IMPACTS"(
     "ISSUE_KEY" CHARACTER VARYING(40) NOT NULL,
     "SOFTWARE_QUALITY" CHARACTER VARYING(40) NOT NULL,
-    "SEVERITY" CHARACTER VARYING(40) NOT NULL
+    "SEVERITY" CHARACTER VARYING(40) NOT NULL,
+    "MANUAL_SEVERITY" BOOLEAN DEFAULT FALSE NOT NULL
 );
 ALTER TABLE "ISSUES_IMPACTS" ADD CONSTRAINT "PK_ISSUES_IMPACTS" PRIMARY KEY("ISSUE_KEY", "SOFTWARE_QUALITY");
 CREATE UNIQUE NULLS NOT DISTINCT INDEX "UNIQ_ISS_KEY_SOF_QUAL" ON "ISSUES_IMPACTS"("ISSUE_KEY" NULLS FIRST, "SOFTWARE_QUALITY" NULLS FIRST);
diff --git a/server/sonar-db-migration/src/it/java/org/sonar/server/platform/db/migration/version/v108/AddManualSeverityColumnInIssuesImpactsTableTest.java b/server/sonar-db-migration/src/it/java/org/sonar/server/platform/db/migration/version/v108/AddManualSeverityColumnInIssuesImpactsTableTest.java
new file mode 100644 (file)
index 0000000..451f2b6
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * 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 org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+import org.sonar.db.MigrationDbTester;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class AddManualSeverityColumnInIssuesImpactsTableTest {
+
+  @RegisterExtension
+  public final MigrationDbTester db = MigrationDbTester.createForMigrationStep(AddManualSeverityColumnInIssuesImpactsTable.class);
+  private final AddManualSeverityColumnInIssuesImpactsTable underTest = new AddManualSeverityColumnInIssuesImpactsTable(db.database());
+
+  @Test
+  void execute_whenColumnDoesNotExist_shouldCreateColumn() throws Exception {
+    db.assertColumnDoesNotExist(AddManualSeverityColumnInIssuesImpactsTable.TABLE_NAME, AddManualSeverityColumnInIssuesImpactsTable.MANUAL_SEVERITY);
+    underTest.execute();
+    db.assertColumnDefinition(AddManualSeverityColumnInIssuesImpactsTable.TABLE_NAME, AddManualSeverityColumnInIssuesImpactsTable.MANUAL_SEVERITY, java.sql.Types.BOOLEAN, null, false);
+  }
+
+  @Test
+  void execute_whenColumnAlreadyExists_shouldNotFail() throws Exception {
+    underTest.execute();
+    assertDoesNotThrow(() -> underTest.execute());
+  }
+
+  @Test
+  void execute_is_reentrant() throws Exception {
+    underTest.execute();
+    underTest.execute();
+    db.assertColumnDefinition(AddManualSeverityColumnInIssuesImpactsTable.TABLE_NAME, AddManualSeverityColumnInIssuesImpactsTable.MANUAL_SEVERITY, java.sql.Types.BOOLEAN, null, false);
+  }
+
+}
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v108/AddManualSeverityColumnInIssuesImpactsTable.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v108/AddManualSeverityColumnInIssuesImpactsTable.java
new file mode 100644 (file)
index 0000000..3d614a4
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * 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.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.server.platform.db.migration.def.BooleanColumnDef;
+import org.sonar.server.platform.db.migration.sql.AddColumnsBuilder;
+import org.sonar.server.platform.db.migration.step.DdlChange;
+
+import static org.sonar.db.DatabaseUtils.tableColumnExists;
+
+public class AddManualSeverityColumnInIssuesImpactsTable extends DdlChange {
+
+  public static final String TABLE_NAME = "issues_impacts";
+  public static final String MANUAL_SEVERITY = "manual_severity";
+
+
+  public AddManualSeverityColumnInIssuesImpactsTable(Database db) {
+    super(db);
+  }
+
+  @Override
+  public void execute(Context context) throws SQLException {
+    try (var connection = getDatabase().getDataSource().getConnection()) {
+      if (!tableColumnExists(connection, TABLE_NAME, MANUAL_SEVERITY)) {
+        var manualSeverity = BooleanColumnDef.newBooleanColumnDefBuilder()
+          .setColumnName(MANUAL_SEVERITY)
+          .setIsNullable(false)
+          .setDefaultValue(false)
+          .build();
+
+        context.execute(new AddColumnsBuilder(getDialect(), TABLE_NAME)
+          .addColumn(manualSeverity)
+          .build());
+      }
+    }
+  }
+}
index 20686209202ae5248a4e63a09af7f1f3a66c3693..2c493a67fc7b0f1f46b24e6859db2216ca23f2be 100644 (file)
@@ -61,7 +61,8 @@ public class DbVersion108 implements DbVersion {
       .add(10_8_018, "Make columns 'published_at' and 'last_modified_at' nullable on the 'cves' table", AlterCveColumnsToNullable.class)
       .add(10_8_019, "Delete Software Quality ratings from project_measures", DeleteSoftwareQualityRatingFromProjectMeasures.class)
       .add(10_8_020, "Create new software quality metrics", CreateNewSoftwareQualityMetrics.class)
-      .add(10_8_021, "Migrate deprecated project_measures to replacement metrics", MigrateProjectMeasuresDeprecatedMetrics.class);
+      .add(10_8_021, "Migrate deprecated project_measures to replacement metrics", MigrateProjectMeasuresDeprecatedMetrics.class)
+      .add(10_8_022, "Add 'manual_severity' column in 'issues_impacts' table", AddManualSeverityColumnInIssuesImpactsTable.class);
   }
 
 }