From 2659be50a7dee7cc410106fe861b0385340889b0 Mon Sep 17 00:00:00 2001 From: OrlovAlexander Date: Tue, 5 Nov 2024 16:48:04 +0100 Subject: [PATCH] SONAR-23363 Database Migration for manual_severity flag --- .../java/org/sonar/db/issue/IssueDaoIT.java | 65 +++++++++++++++---- .../java/org/sonar/db/issue/ImpactDto.java | 22 ++++++- .../org/sonar/db/issue/IssueMapper.xml | 8 ++- server/sonar-db-dao/src/schema/schema-sq.ddl | 3 +- ...everityColumnInIssuesImpactsTableTest.java | 54 +++++++++++++++ ...ualSeverityColumnInIssuesImpactsTable.java | 56 ++++++++++++++++ .../migration/version/v108/DbVersion108.java | 3 +- 7 files changed, 190 insertions(+), 21 deletions(-) create mode 100644 server/sonar-db-migration/src/it/java/org/sonar/server/platform/db/migration/version/v108/AddManualSeverityColumnInIssuesImpactsTableTest.java create mode 100644 server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v108/AddManualSeverityColumnInIssuesImpactsTable.java diff --git a/server/sonar-db-dao/src/it/java/org/sonar/db/issue/IssueDaoIT.java b/server/sonar-db-dao/src/it/java/org/sonar/db/issue/IssueDaoIT.java index ef3a44abbb1..15758d0f49b 100644 --- a/server/sonar-db-dao/src/it/java/org/sonar/db/issue/IssueDaoIT.java +++ b/server/sonar-db-dao/src/it/java/org/sonar/db/issue/IssueDaoIT.java @@ -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 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); } diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/issue/ImpactDto.java b/server/sonar-db-dao/src/main/java/org/sonar/db/issue/ImpactDto.java index 4cf2ec95e1a..389c549649b 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/issue/ImpactDto.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/issue/ImpactDto.java @@ -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); } } diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/issue/IssueMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/issue/IssueMapper.xml index 0dbe81a387c..1515c039681 100644 --- a/server/sonar-db-dao/src/main/resources/org/sonar/db/issue/IssueMapper.xml +++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/issue/IssueMapper.xml @@ -119,6 +119,7 @@ ii.software_quality as "ii_softwareQuality", ii.severity as "ii_severity", + ii.manual_severity as "ii_manualSeverity", @@ -137,6 +138,7 @@ javaType="java.util.Set" ofType="Impact"> + @@ -185,11 +187,12 @@ - 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}) @@ -343,6 +346,7 @@ javaType="java.util.Set" ofType="Impact"> + diff --git a/server/sonar-db-dao/src/schema/schema-sq.ddl b/server/sonar-db-dao/src/schema/schema-sq.ddl index 272b1c156a1..b6a46b9bc40 100644 --- a/server/sonar-db-dao/src/schema/schema-sq.ddl +++ b/server/sonar-db-dao/src/schema/schema-sq.ddl @@ -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 index 00000000000..451f2b696b8 --- /dev/null +++ b/server/sonar-db-migration/src/it/java/org/sonar/server/platform/db/migration/version/v108/AddManualSeverityColumnInIssuesImpactsTableTest.java @@ -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 index 00000000000..3d614a4d689 --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v108/AddManualSeverityColumnInIssuesImpactsTable.java @@ -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()); + } + } + } +} diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v108/DbVersion108.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v108/DbVersion108.java index 20686209202..2c493a67fc7 100644 --- a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v108/DbVersion108.java +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v108/DbVersion108.java @@ -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); } } -- 2.39.5