]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-19521 Delete redundant Failed Alerts from Applications that were introduced...
authorDimitris Kavvathas <dimitris.kavvathas@sonarsource.com>
Wed, 1 Nov 2023 15:30:08 +0000 (16:30 +0100)
committersonartech <sonartech@sonarsource.com>
Tue, 7 Nov 2023 20:02:50 +0000 (20:02 +0000)
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/MigrationConfigurationModule.java
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v104/DbVersion104.java [new file with mode: 0644]
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v104/DeleteRedundantFailedAlertsForApplications.java [new file with mode: 0644]
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v104/DbVersion104Test.java [new file with mode: 0644]
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v104/DeleteRedundantFailedAlertsForApplicationsTest.java [new file with mode: 0644]
server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v104/DeleteRedundantFailedAlertsForApplicationsTest/schema.sql [new file with mode: 0644]

index 54bb7543d300776c0f6b5943df9f102325e406e3..47d6f2d7f2035056056708e2a6a7cd7f0dcec327 100644 (file)
@@ -32,6 +32,7 @@ import org.sonar.server.platform.db.migration.version.v100.DbVersion100;
 import org.sonar.server.platform.db.migration.version.v101.DbVersion101;
 import org.sonar.server.platform.db.migration.version.v102.DbVersion102;
 import org.sonar.server.platform.db.migration.version.v103.DbVersion103;
+import org.sonar.server.platform.db.migration.version.v104.DbVersion104;
 
 public class MigrationConfigurationModule extends Module {
   @Override
@@ -44,6 +45,7 @@ public class MigrationConfigurationModule extends Module {
       DbVersion101.class,
       DbVersion102.class,
       DbVersion103.class,
+      DbVersion104.class,
 
       // migration steps
       MigrationStepRegistryImpl.class,
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v104/DbVersion104.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v104/DbVersion104.java
new file mode 100644 (file)
index 0000000..9ea5eb0
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 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.v104;
+
+import org.sonar.server.platform.db.migration.step.MigrationStepRegistry;
+import org.sonar.server.platform.db.migration.version.DbVersion;
+
+// ignoring bad number formatting, as it's indented that we align the migration numbers to SQ versions
+@SuppressWarnings("java:S3937")
+public class DbVersion104 implements DbVersion {
+
+  /**
+   * We use the start of the 10.X cycle as an opportunity to align migration numbers with the SQ version number.
+   * Please follow this pattern:
+   * 10_0_000
+   * 10_0_001
+   * 10_0_002
+   * 10_1_000
+   * 10_1_001
+   * 10_1_002
+   * 10_2_000
+   */
+
+  @Override
+  public void addSteps(MigrationStepRegistry registry) {
+    registry
+      .add(10_4_000, "Delete redundant Failed Alerts for Applications", DeleteRedundantFailedAlertsForApplications.class);
+  }
+}
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v104/DeleteRedundantFailedAlertsForApplications.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v104/DeleteRedundantFailedAlertsForApplications.java
new file mode 100644 (file)
index 0000000..484b48d
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 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.v104;
+
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.server.platform.db.migration.step.DataChange;
+import org.sonar.server.platform.db.migration.step.MassUpdate;
+
+public class DeleteRedundantFailedAlertsForApplications extends DataChange {
+
+  private static final String SELECT_QUERY = """
+    SELECT E.uuid
+      FROM events E
+      JOIN components C ON E.component_uuid = C.uuid
+      WHERE E.name = 'Failed'
+        AND E.category = 'Alert'
+        AND E.event_data = '{ stillFailing: false, status: "ERROR" }'
+        AND C.qualifier = 'APP'""";
+
+  private static final String DELETE_EVENTS_STATEMENT = "DELETE FROM events WHERE uuid = ?";
+  private static final String DELETE_EVENTS_COMPONENT_CHANGES_STATEMENT = "DELETE FROM event_component_changes WHERE event_uuid = ?";
+
+  public DeleteRedundantFailedAlertsForApplications(Database db) {
+    super(db);
+  }
+
+  @Override
+  public void execute(Context context) throws SQLException {
+    MassUpdate massUpdate = context.prepareMassUpdate();
+    var select = massUpdate.select(SELECT_QUERY);
+    var deleteEventComponentChanges = massUpdate.update(DELETE_EVENTS_COMPONENT_CHANGES_STATEMENT);
+    var deleteEvents = massUpdate.update(DELETE_EVENTS_STATEMENT);
+    try (select; deleteEventComponentChanges; deleteEvents) {
+      massUpdate.execute((row, delete, index) -> {
+        // both updates use the same select, so no need to differentiate for the 2 update indexes
+        String uuid = row.getString(1);
+        delete.setString(1, uuid);
+        return true;
+      });
+    }
+  }
+}
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v104/DbVersion104Test.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v104/DbVersion104Test.java
new file mode 100644 (file)
index 0000000..39fa506
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 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.v104;
+
+import org.junit.Test;
+
+import static org.sonar.server.platform.db.migration.version.DbVersionTestUtils.verifyMigrationNotEmpty;
+import static org.sonar.server.platform.db.migration.version.DbVersionTestUtils.verifyMinimumMigrationNumber;
+
+public class DbVersion104Test {
+
+  private final DbVersion104 underTest = new DbVersion104();
+
+  @Test
+  public void migrationNumber_starts_at_10_4_000() {
+    verifyMinimumMigrationNumber(underTest, 10_4_000);
+  }
+
+  @Test
+  public void verify_migration_is_not_empty() {
+    verifyMigrationNotEmpty(underTest);
+  }
+
+}
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v104/DeleteRedundantFailedAlertsForApplicationsTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v104/DeleteRedundantFailedAlertsForApplicationsTest.java
new file mode 100644 (file)
index 0000000..3c5f925
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 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.v104;
+
+import java.sql.SQLException;
+import java.util.Map;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.db.CoreDbTester;
+import org.sonar.server.platform.db.migration.step.DataChange;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class DeleteRedundantFailedAlertsForApplicationsTest {
+
+  @Rule
+  public final CoreDbTester db = CoreDbTester.createForSchema(DeleteRedundantFailedAlertsForApplicationsTest.class, "schema.sql");
+  private final DataChange underTest = new DeleteRedundantFailedAlertsForApplications(db.database());
+
+  @Before
+  public void setUp() {
+    // cleanup db
+    db.executeUpdateSql("truncate table events");
+    db.executeUpdateSql("truncate table event_component_changes");
+    db.executeUpdateSql("truncate table components");
+  }
+
+  @Test
+  public void givenFailedAlertsForApplication_whenExecuted_thenFailedAlertsAreDeleted() throws SQLException {
+    // given
+    insertComponent("app1", "appUuid1", "appUuid1", "APP");
+
+    // event that should be deleted
+    insertEvent("eventUuid1", "appUuid1", "Failed", "Alert", "{ stillFailing: false, status: \"ERROR\" }");
+    insertEventChanges("eventChangeUuid1", "eventUuid1", "appUuid1");
+    insertEventChanges("eventChangeUuid2", "eventUuid1", "appUuid1");
+
+    // events that should not be deleted
+    insertEvent("eventUuid2", "appUuid1", "Passed", "Alert", "{ stillFailing: false, status: \"ERROR\" }");
+    insertEventChanges("eventChangeUuid3", "eventUuid2", "appUuid1");
+    insertEvent("eventUuid3", "appUuid1", "Failed", "Alert", "{ stillFailing: false, status: \"PASSED\" }");
+    insertEventChanges("eventChangeUuid4", "eventUuid3", "appUuid1");
+
+    // when
+    underTest.execute();
+
+    // then
+    assertThat(db.countRowsOfTable("events")).isEqualTo(2);
+    assertThat(db.countSql("select count(1) from events where uuid = 'eventUuid1'")).isZero();
+
+    assertThat(db.countRowsOfTable("event_component_changes")).isEqualTo(2);
+    assertThat(db.countSql("select count(1) from event_component_changes where uuid = 'eventUuid1'")).isZero();
+  }
+
+  @Test
+  public void givenFailedAlertsForProject_whenExecute_thenTheEventsAreNotDeleted() throws SQLException {
+    // given
+    insertComponent("project1", "projectUuid1", "projectUuid1", "TRK");
+
+    // event that should not be deleted
+    insertEvent("eventUuid1", "projectUuid1", "Failed", "Alert", "{ stillFailing: false, status: \"ERROR\" }");
+    insertEventChanges("eventChangeUuid1", "eventUuid1", "projectUuid1");
+    insertEventChanges("eventChangeUuid2", "eventUuid1", "projectUuid1");
+
+    // when
+    underTest.execute();
+
+    // then
+    assertThat(db.countSql("select count(1) from events where uuid = 'eventUuid1'")).isEqualTo(1);
+    assertThat(db.countSql("select count(1) from event_component_changes where event_uuid = 'eventUuid1'")).isEqualTo(2);
+  }
+
+  @Test
+  public void givenMigration_whenExecutedMoreThanOnce_thenNoError() throws SQLException {
+    // given
+    insertComponent("app1", "appUuid1", "appUuid1", "APP");
+
+    // event that should be deleted
+    insertEvent("eventUuid1", "appUuid1", "Failed", "Alert", "{ stillFailing: false, status: \"ERROR\" }");
+    insertEventChanges("eventChangeUuid1", "eventUuid1", "appUuid1");
+    insertEventChanges("eventChangeUuid2", "eventUuid1", "appUuid1");
+
+    // when
+    underTest.execute();
+    underTest.execute();
+
+    // then
+    assertThat(db.countSql("select count(1) from events where uuid = 'eventUuid1'")).isZero();
+    assertThat(db.countSql("select count(1) from event_component_changes where uuid = 'eventUuid1'")).isZero();
+  }
+
+  private void insertComponent(String key, String uuid, String branchUuid, String qualifier) {
+    Map<String, Object> map = Map.ofEntries(
+      Map.entry("UUID", uuid),
+      Map.entry("KEE", key),
+      Map.entry("BRANCH_UUID", branchUuid),
+      Map.entry("UUID_PATH", "." + uuid + "."),
+      Map.entry("QUALIFIER", qualifier),
+      Map.entry("ENABLED", true),
+      Map.entry("PRIVATE", true)
+    );
+
+    db.executeInsert("components", map);
+  }
+
+  private void insertEvent(String uuid, String componentUuid, String name, String category, String eventData) {
+    Map<String, Object> map = Map.ofEntries(
+      Map.entry("UUID", uuid),
+      Map.entry("NAME", name),
+      Map.entry("ANALYSIS_UUID", "analysisUuid"),
+      Map.entry("CATEGORY", category),
+      Map.entry("CREATED_AT", 1_500_000_000_000L),
+      Map.entry("EVENT_DATE", 1_500_000_000_000L),
+      Map.entry("COMPONENT_UUID", componentUuid),
+      Map.entry("EVENT_DATA", eventData)
+    );
+
+    db.executeInsert("events", map);
+  }
+
+  private void insertEventChanges(String uuid, String eventUuid, String componentUuid) {
+    Map<String, Object> map = Map.ofEntries(
+      Map.entry("UUID", uuid),
+      Map.entry("EVENT_UUID", eventUuid),
+      Map.entry("EVENT_COMPONENT_UUID", componentUuid),
+      Map.entry("EVENT_ANALYSIS_UUID", "analysisUuid"),
+      Map.entry("CHANGE_CATEGORY", "FAILED_QG"),
+      Map.entry("COMPONENT_UUID", uuid),
+      Map.entry("COMPONENT_KEY", "app"),
+      Map.entry("COMPONENT_NAME", "app"),
+      Map.entry("COMPONENT_BRANCH_KEY", 1_500_000_000_000L),
+      Map.entry("CREATED_AT", 1_500_000_000_000L)
+    );
+
+    db.executeInsert("event_component_changes", map);
+  }
+
+
+}
diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v104/DeleteRedundantFailedAlertsForApplicationsTest/schema.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v104/DeleteRedundantFailedAlertsForApplicationsTest/schema.sql
new file mode 100644 (file)
index 0000000..20cd163
--- /dev/null
@@ -0,0 +1,64 @@
+CREATE TABLE "COMPONENTS"(
+    "UUID" CHARACTER VARYING(50) NOT NULL,
+    "KEE" CHARACTER VARYING(1000),
+    "DEPRECATED_KEE" CHARACTER VARYING(400),
+    "NAME" CHARACTER VARYING(2000),
+    "LONG_NAME" CHARACTER VARYING(2000),
+    "DESCRIPTION" CHARACTER VARYING(2000),
+    "ENABLED" BOOLEAN DEFAULT TRUE NOT NULL,
+    "SCOPE" CHARACTER VARYING(3),
+    "QUALIFIER" CHARACTER VARYING(10),
+    "PRIVATE" BOOLEAN NOT NULL,
+    "LANGUAGE" CHARACTER VARYING(20),
+    "COPY_COMPONENT_UUID" CHARACTER VARYING(50),
+    "PATH" CHARACTER VARYING(2000),
+    "UUID_PATH" CHARACTER VARYING(1500) NOT NULL,
+    "BRANCH_UUID" CHARACTER VARYING(50) NOT NULL,
+    "B_CHANGED" BOOLEAN,
+    "B_NAME" CHARACTER VARYING(500),
+    "B_LONG_NAME" CHARACTER VARYING(500),
+    "B_DESCRIPTION" CHARACTER VARYING(2000),
+    "B_ENABLED" BOOLEAN,
+    "B_QUALIFIER" CHARACTER VARYING(10),
+    "B_LANGUAGE" CHARACTER VARYING(20),
+    "B_COPY_COMPONENT_UUID" CHARACTER VARYING(50),
+    "B_PATH" CHARACTER VARYING(2000),
+    "B_UUID_PATH" CHARACTER VARYING(1500),
+    "CREATED_AT" TIMESTAMP
+);
+CREATE INDEX "PROJECTS_QUALIFIER" ON "COMPONENTS"("QUALIFIER" NULLS FIRST);
+CREATE UNIQUE NULLS NOT DISTINCT INDEX "COMPONENTS_UUID" ON "COMPONENTS"("UUID" NULLS FIRST);
+CREATE INDEX "COMPONENTS_BRANCH_UUID" ON "COMPONENTS"("BRANCH_UUID" NULLS FIRST);
+CREATE UNIQUE NULLS NOT DISTINCT INDEX "COMPONENTS_KEE_BRANCH_UUID" ON "COMPONENTS"("KEE" NULLS FIRST, "BRANCH_UUID" NULLS FIRST);
+
+CREATE TABLE "EVENT_COMPONENT_CHANGES"(
+    "UUID" CHARACTER VARYING(40) NOT NULL,
+    "EVENT_UUID" CHARACTER VARYING(40) NOT NULL,
+    "EVENT_COMPONENT_UUID" CHARACTER VARYING(50) NOT NULL,
+    "EVENT_ANALYSIS_UUID" CHARACTER VARYING(50) NOT NULL,
+    "CHANGE_CATEGORY" CHARACTER VARYING(12) NOT NULL,
+    "COMPONENT_UUID" CHARACTER VARYING(50) NOT NULL,
+    "COMPONENT_KEY" CHARACTER VARYING(400) NOT NULL,
+    "COMPONENT_NAME" CHARACTER VARYING(2000) NOT NULL,
+    "COMPONENT_BRANCH_KEY" CHARACTER VARYING(255),
+    "CREATED_AT" BIGINT NOT NULL
+);
+ALTER TABLE "EVENT_COMPONENT_CHANGES" ADD CONSTRAINT "PK_EVENT_COMPONENT_CHANGES" PRIMARY KEY("UUID");
+CREATE UNIQUE NULLS NOT DISTINCT INDEX "EVENT_COMPONENT_CHANGES_UNIQUE" ON "EVENT_COMPONENT_CHANGES"("EVENT_UUID" NULLS FIRST, "CHANGE_CATEGORY" NULLS FIRST, "COMPONENT_UUID" NULLS FIRST);
+CREATE INDEX "EVENT_CPNT_CHANGES_CPNT" ON "EVENT_COMPONENT_CHANGES"("EVENT_COMPONENT_UUID" NULLS FIRST);
+CREATE INDEX "EVENT_CPNT_CHANGES_ANALYSIS" ON "EVENT_COMPONENT_CHANGES"("EVENT_ANALYSIS_UUID" NULLS FIRST);
+
+CREATE TABLE "EVENTS"(
+    "UUID" CHARACTER VARYING(40) NOT NULL,
+    "ANALYSIS_UUID" CHARACTER VARYING(50) NOT NULL,
+    "NAME" CHARACTER VARYING(400),
+    "CATEGORY" CHARACTER VARYING(50),
+    "DESCRIPTION" CHARACTER VARYING(4000),
+    "EVENT_DATA" CHARACTER VARYING(4000),
+    "EVENT_DATE" BIGINT NOT NULL,
+    "CREATED_AT" BIGINT NOT NULL,
+    "COMPONENT_UUID" CHARACTER VARYING(50) NOT NULL
+);
+ALTER TABLE "EVENTS" ADD CONSTRAINT "PK_EVENTS" PRIMARY KEY("UUID");
+CREATE INDEX "EVENTS_ANALYSIS" ON "EVENTS"("ANALYSIS_UUID" NULLS FIRST);
+CREATE INDEX "EVENTS_COMPONENT_UUID" ON "EVENTS"("COMPONENT_UUID" NULLS FIRST);