]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-13221 replace auto-incremented ID by UUID for notifications
authorPierre <pierre.guillot@sonarsource.com>
Fri, 27 Mar 2020 11:32:22 +0000 (12:32 +0100)
committersonartech <sonartech@sonarsource.com>
Mon, 25 May 2020 20:05:18 +0000 (20:05 +0000)
29 files changed:
server/sonar-db-dao/src/main/java/org/sonar/db/notification/NotificationQueueDao.java
server/sonar-db-dao/src/main/java/org/sonar/db/notification/NotificationQueueDto.java
server/sonar-db-dao/src/main/java/org/sonar/db/notification/NotificationQueueMapper.java
server/sonar-db-dao/src/main/resources/org/sonar/db/notification/NotificationQueueMapper.xml
server/sonar-db-dao/src/schema/schema-sq.ddl
server/sonar-db-dao/src/test/java/org/sonar/db/notification/NotificationQueueDaoTest.java
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v83/DbVersion83.java
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v83/notifications/AddPrimaryKeyOnUuidColumnOfNotificationTable.java [new file with mode: 0644]
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v83/notifications/AddUuidAndCreatedAtColumnsToNotification.java [new file with mode: 0644]
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v83/notifications/DropIdColumnOfNotificationTable.java [new file with mode: 0644]
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v83/notifications/DropPrimaryKeyOnIdColumnOfNotificationTable.java [new file with mode: 0644]
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v83/notifications/MakeNotificationUuidAndCreatedAtColumnsNotNullable.java [new file with mode: 0644]
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v83/notifications/PopulateNotificationUuidAndCreatedAt.java [new file with mode: 0644]
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/DbVersionTestUtils.java
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v83/DbVersion83Test.java
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v83/notifications/AddPrimaryKeyOnUuidColumnOfNotificationTableTest.java [new file with mode: 0644]
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v83/notifications/AddUuidAndCreatedAtColumnsToNotificationTest.java [new file with mode: 0644]
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v83/notifications/DropIdColumnOfNotificationTableTest.java [new file with mode: 0644]
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v83/notifications/DropPrimaryKeyOnIdColumnOfNotificationTableTest.java [new file with mode: 0644]
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v83/notifications/MakeNotificationUuidAndCreatedAtColumnsNotNullableTest.java [new file with mode: 0644]
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v83/notifications/PopulateNotificationUuidAndCreatedAtTest.java [new file with mode: 0644]
server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v83/notifications/AddPrimaryKeyOnUuidColumnOfNotificationTableTest/schema.sql [new file with mode: 0644]
server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v83/notifications/AddUuidAndCreatedAtColumnsToNotificationTest/schema.sql [new file with mode: 0644]
server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v83/notifications/DropIdColumnOfNotificationTableTest/schema.sql [new file with mode: 0644]
server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v83/notifications/DropPrimaryKeyOnIdColumnOfNotificationTableTest/schema.sql [new file with mode: 0644]
server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v83/notifications/MakeNotificationUuidAndCreatedAtColumnsNotNullableTest/schema.sql [new file with mode: 0644]
server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v83/notifications/PopulateNotificationUuidAndCreatedAtTest/schema.sql [new file with mode: 0644]
server/sonar-server-common/src/main/java/org/sonar/server/notification/DefaultNotificationManager.java
server/sonar-server-common/src/test/java/org/sonar/server/notification/DefaultNotificationManagerTest.java

index 87cc18c2277c145f6ddf8cedba5145ff52596164..5b3a9ace6df43e04bcfeea358173493c77aad1d1 100644 (file)
@@ -21,6 +21,8 @@ package org.sonar.db.notification;
 
 import java.util.Collections;
 import java.util.List;
+import org.sonar.api.utils.System2;
+import org.sonar.core.util.UuidFactory;
 import org.sonar.db.Dao;
 import org.sonar.db.DbSession;
 import org.sonar.db.MyBatis;
@@ -28,15 +30,21 @@ import org.sonar.db.MyBatis;
 public class NotificationQueueDao implements Dao {
 
   private final MyBatis mybatis;
+  private System2 system2;
+  private UuidFactory uuidFactory;
 
-  public NotificationQueueDao(MyBatis mybatis) {
+  public NotificationQueueDao(MyBatis mybatis, System2 system2, UuidFactory uuidFactory) {
     this.mybatis = mybatis;
+    this.system2 = system2;
+    this.uuidFactory = uuidFactory;
   }
 
   public void insert(List<NotificationQueueDto> dtos) {
     try (DbSession session = mybatis.openSession(true)) {
       NotificationQueueMapper mapper = session.getMapper(NotificationQueueMapper.class);
       for (NotificationQueueDto dto : dtos) {
+        dto.setUuid(uuidFactory.create());
+        dto.setCreatedAt(system2.now());
         mapper.insert(dto);
       }
       session.commit();
@@ -47,7 +55,7 @@ public class NotificationQueueDao implements Dao {
     try (DbSession session = mybatis.openSession(true)) {
       NotificationQueueMapper mapper = session.getMapper(NotificationQueueMapper.class);
       for (NotificationQueueDto dto : dtos) {
-        mapper.delete(dto.getId());
+        mapper.delete(dto.getUuid());
       }
       session.commit();
     }
index aba0a3328fe68335e9db477112995296b39fd114..c6a8080024870a56beaaac85943de4a0ab58d34c 100644 (file)
@@ -35,15 +35,16 @@ import org.sonar.api.utils.SonarException;
  */
 public class NotificationQueueDto {
 
-  private Long id;
+  private String uuid;
   private byte[] data;
+  private long createdAt;
 
-  public Long getId() {
-    return id;
+  public String getUuid() {
+    return uuid;
   }
 
-  public NotificationQueueDto setId(Long id) {
-    this.id = id;
+  NotificationQueueDto setUuid(String uuid) {
+    this.uuid = uuid;
     return this;
   }
 
@@ -56,6 +57,15 @@ public class NotificationQueueDto {
     return this;
   }
 
+  public long getCreatedAt() {
+    return createdAt;
+  }
+
+  NotificationQueueDto setCreatedAt(long createdAt) {
+    this.createdAt = createdAt;
+    return this;
+  }
+
   @Override
   public String toString() {
     return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
index d5406ba723098905b3f36dcd9069c4697cbb5e0a..8fd5876e298740aace1b592fba4333c06a43d8f0 100644 (file)
@@ -28,7 +28,7 @@ public interface NotificationQueueMapper {
 
   void insert(NotificationQueueDto actionPlanDto);
 
-  void delete(long id);
+  void delete(String uuid);
 
   List<NotificationQueueDto> findOldest(int count);
 
index 6ee671feb629f34f1dbabee3d17a1a7ea64f28b5..bc13a32e4cf0bc1e0ef1a4acc08677a5153cafa9 100644 (file)
@@ -5,12 +5,12 @@
 <mapper namespace="org.sonar.db.notification.NotificationQueueMapper">
 
   <insert id="insert" parameterType="NotificationQueue" useGeneratedKeys="false">
-    INSERT INTO notifications (data)
-    VALUES (#{data})
+    INSERT INTO notifications (uuid,data,created_at)
+    VALUES (#{uuid},#{data},#{createdAt})
   </insert>
 
-  <delete id="delete" parameterType="long">
-    delete from notifications where id=#{id}
+  <delete id="delete" parameterType="String">
+    delete from notifications where uuid=#{uuid}
   </delete>
 
   <select id="count" resultType="long">
   </select>
 
   <select id="findOldest" parameterType="int" resultType="NotificationQueue">
-    select id, data
+    select uuid, data, created_at
     from notifications
-    order by id asc
+    order by created_at asc
     limit #{count}
   </select>
 
   <!-- SQL Server -->
   <select id="findOldest" parameterType="int" resultType="NotificationQueue" databaseId="mssql">
-    select top (#{count}) id, data
+    select top (#{count}) uuid, data, created_at
     from notifications
-    order by id asc
+    order by created_at asc
   </select>
 
   <!-- Oracle -->
   <select id="findOldest" parameterType="int" resultType="NotificationQueue" databaseId="oracle">
     select * from (select
-    id, data
+    uuid, data, created_at
     from notifications
-    order by id asc
+    order by created_at asc
     )
     where rownum &lt;= #{count}
   </select>
index 5ef948d76ba9db47ea8d628add68c5c6b8ac2a22..0af1d35bc97007a8c43e23e715ff73d3a026b965 100644 (file)
@@ -483,10 +483,11 @@ ALTER TABLE "NEW_CODE_PERIODS" ADD CONSTRAINT "PK_NEW_CODE_PERIODS" PRIMARY KEY(
 CREATE UNIQUE INDEX "UNIQ_NEW_CODE_PERIODS" ON "NEW_CODE_PERIODS"("PROJECT_UUID", "BRANCH_UUID");
 
 CREATE TABLE "NOTIFICATIONS"(
-    "ID" INTEGER NOT NULL AUTO_INCREMENT (1,1),
-    "DATA" BLOB
+    "DATA" BLOB,
+    "UUID" VARCHAR(40) NOT NULL,
+    "CREATED_AT" BIGINT NOT NULL
 );
-ALTER TABLE "NOTIFICATIONS" ADD CONSTRAINT "PK_NOTIFICATIONS" PRIMARY KEY("ID");
+ALTER TABLE "NOTIFICATIONS" ADD CONSTRAINT "PK_NOTIFICATIONS" PRIMARY KEY("UUID");
 
 CREATE TABLE "ORG_QPROFILES"(
     "UUID" VARCHAR(255) NOT NULL,
index f6306aadfc889b9acd9e9bdf1a67fa7c53669875..97043ee749103c254dc8651e0cb7582dde50f27d 100644 (file)
@@ -21,22 +21,27 @@ package org.sonar.db.notification;
 
 import java.util.Arrays;
 import java.util.List;
-import java.util.Random;
 import java.util.stream.IntStream;
 import org.junit.Rule;
 import org.junit.Test;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
 import org.sonar.api.notifications.Notification;
 import org.sonar.api.utils.System2;
 import org.sonar.db.DbTester;
 
 import static java.util.stream.Collectors.toList;
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
 import static org.sonar.db.notification.NotificationQueueDto.toNotificationQueueDto;
 
 public class NotificationQueueDaoTest {
 
+  private final System2 system2 = mock(System2.class);
+
   @Rule
-  public DbTester db = DbTester.create(System2.INSTANCE);
+  public DbTester db = DbTester.create(system2);
 
   private NotificationQueueDao dao = db.getDbClient().notificationQueueDao();
 
@@ -63,41 +68,45 @@ public class NotificationQueueDaoTest {
 
   @Test
   public void should_delete_notification() {
-    List<NotificationQueueDto> notifs = IntStream.range(0, 30 + new Random().nextInt(20))
+    List<NotificationQueueDto> notifs = IntStream.range(0, 30)
       .mapToObj(i -> toNotificationQueueDto(new Notification("foo_" + i)))
       .collect(toList());
     dao.insert(notifs);
     db.commit();
 
-    List<Long> ids = selectAllIds();
+    List<String> uuids = selectAllUuid();
 
-    dao.delete(ids.stream().limit(10).map(id -> new NotificationQueueDto().setId(id)).collect(toList()));
+    dao.delete(uuids.stream().limit(10).map(uuid -> new NotificationQueueDto().setUuid(uuid)).collect(toList()));
 
-    assertThat(selectAllIds()).containsOnly(ids.stream().skip(10).toArray(Long[]::new));
+    assertThat(selectAllUuid()).containsOnly(uuids.stream().skip(10).toArray(String[]::new));
   }
 
   @Test
   public void should_findOldest() {
-    List<NotificationQueueDto> notifs = IntStream.range(0, 20)
+    when(system2.now()).thenAnswer(new Answer<Long>() {
+      private long counter;
+
+      @Override
+      public Long answer(InvocationOnMock invocationOnMock) {
+        counter++;
+        return counter;
+      }
+    });
+
+    List<NotificationQueueDto> notifs = IntStream.range(0, 5)
       .mapToObj(i -> toNotificationQueueDto(new Notification("foo_" + i)))
       .collect(toList());
     dao.insert(notifs);
     db.commit();
 
-    List<Long> ids = selectAllIds();
-
     assertThat(dao.selectOldest(3))
-      .extracting(NotificationQueueDto::getId)
-      .containsOnly(ids.stream().limit(3).toArray(Long[]::new));
-
-    assertThat(dao.selectOldest(22))
-      .extracting(NotificationQueueDto::getId)
-      .containsOnly(ids.toArray(new Long[0]));
+      .extracting(NotificationQueueDto::getUuid)
+      .containsExactlyElementsOf(Arrays.asList("1", "2", "3"));
   }
 
-  private List<Long> selectAllIds() {
-    return db.select("select id as \"ID\" from notifications").stream()
-      .map(t -> (Long) t.get("ID"))
+  private List<String> selectAllUuid() {
+    return db.select("select uuid as \"UUID\" from notifications order by created_at asc").stream()
+      .map(t -> (String) t.get("UUID"))
       .collect(toList());
   }
 }
index 258e92cfae82b75d321a1a3f17d4b1688f4522dd..b5758075011650dfbd82bdb5ec5f70815b779239 100644 (file)
@@ -21,6 +21,12 @@ package org.sonar.server.platform.db.migration.version.v83;
 
 import org.sonar.server.platform.db.migration.step.MigrationStepRegistry;
 import org.sonar.server.platform.db.migration.version.DbVersion;
+import org.sonar.server.platform.db.migration.version.v83.notifications.AddPrimaryKeyOnUuidColumnOfNotificationTable;
+import org.sonar.server.platform.db.migration.version.v83.notifications.AddUuidAndCreatedAtColumnsToNotification;
+import org.sonar.server.platform.db.migration.version.v83.notifications.DropIdColumnOfNotificationTable;
+import org.sonar.server.platform.db.migration.version.v83.notifications.DropPrimaryKeyOnIdColumnOfNotificationTable;
+import org.sonar.server.platform.db.migration.version.v83.notifications.MakeNotificationUuidAndCreatedAtColumnsNotNullable;
+import org.sonar.server.platform.db.migration.version.v83.notifications.PopulateNotificationUuidAndCreatedAt;
 
 public class DbVersion83 implements DbVersion {
   @Override
@@ -42,7 +48,15 @@ public class DbVersion83 implements DbVersion {
       // Migration on EVENTS table
       .add(3400, "Drop primary key on 'ID' column of 'EVENTS' table", DropPrimaryKeyOnIdColumnOfEventsTable.class)
       .add(3401, "Add primary key on 'UUID' column of 'EVENTS' table", AddPrimaryKeyOnUuidColumnOfEventsTable.class)
-      .add(3402, "Drop column 'ID' of 'EVENTS' table", DropIdColumnOfEventsTable.class);
+      .add(3402, "Drop column 'ID' of 'EVENTS' table", DropIdColumnOfEventsTable.class)
+
+      // Migrations of NOTIFICATIONS table
+      .add(3403, "Add 'uuid' and 'createdAt' columns for notifications", AddUuidAndCreatedAtColumnsToNotification.class)
+      .add(3404, "Populate 'uuid' and 'createdAt columns for notifications", PopulateNotificationUuidAndCreatedAt.class)
+      .add(3405, "Make 'uuid' and 'createdAt' column not nullable for notifications", MakeNotificationUuidAndCreatedAtColumnsNotNullable.class)
+      .add(3406, "Drop primary key on 'ID' column of 'NOTIFICATIONS' table", DropPrimaryKeyOnIdColumnOfNotificationTable.class)
+      .add(3407, "Add primary key on 'UUID' column of 'NOTIFICATIONS' table", AddPrimaryKeyOnUuidColumnOfNotificationTable.class)
+      .add(3408, "Drop column 'ID' of 'NOTIFICATIONS' table", DropIdColumnOfNotificationTable.class)
 
     ;
   }
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v83/notifications/AddPrimaryKeyOnUuidColumnOfNotificationTable.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v83/notifications/AddPrimaryKeyOnUuidColumnOfNotificationTable.java
new file mode 100644 (file)
index 0000000..83a9691
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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.v83.notifications;
+
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.server.platform.db.migration.step.DdlChange;
+import org.sonar.server.platform.db.migration.version.v83.util.AddPrimaryKeyBuilder;
+
+public class AddPrimaryKeyOnUuidColumnOfNotificationTable extends DdlChange {
+
+  public AddPrimaryKeyOnUuidColumnOfNotificationTable(Database db) {
+    super(db);
+  }
+
+  @Override
+  public void execute(Context context) throws SQLException {
+    context.execute(new AddPrimaryKeyBuilder("notifications", "uuid").build());
+  }
+
+}
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v83/notifications/AddUuidAndCreatedAtColumnsToNotification.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v83/notifications/AddUuidAndCreatedAtColumnsToNotification.java
new file mode 100644 (file)
index 0000000..749d54f
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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.v83.notifications;
+
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.server.platform.db.migration.def.BigIntegerColumnDef;
+import org.sonar.server.platform.db.migration.def.VarcharColumnDef;
+import org.sonar.server.platform.db.migration.sql.AddColumnsBuilder;
+import org.sonar.server.platform.db.migration.step.DdlChange;
+
+import static org.sonar.server.platform.db.migration.def.BigIntegerColumnDef.newBigIntegerColumnDefBuilder;
+import static org.sonar.server.platform.db.migration.def.VarcharColumnDef.newVarcharColumnDefBuilder;
+
+public class AddUuidAndCreatedAtColumnsToNotification extends DdlChange {
+  private static final String TABLE = "notifications";
+
+  private static final VarcharColumnDef uuidColumnDefinition = newVarcharColumnDefBuilder()
+    .setColumnName("uuid")
+    .setIsNullable(true)
+    .setDefaultValue(null)
+    .setLimit(VarcharColumnDef.UUID_SIZE)
+    .build();
+
+  private static final BigIntegerColumnDef createdAtColumnDefinition = newBigIntegerColumnDefBuilder()
+    .setColumnName("created_at")
+    .setIsNullable(true)
+    .build();
+
+  public AddUuidAndCreatedAtColumnsToNotification(Database db) {
+    super(db);
+  }
+
+  @Override
+  public void execute(Context context) throws SQLException {
+    context.execute(new AddColumnsBuilder(getDialect(), TABLE)
+      .addColumn(uuidColumnDefinition)
+      .addColumn(createdAtColumnDefinition)
+      .build());
+  }
+}
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v83/notifications/DropIdColumnOfNotificationTable.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v83/notifications/DropIdColumnOfNotificationTable.java
new file mode 100644 (file)
index 0000000..8ae8853
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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.v83.notifications;
+
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.server.platform.db.migration.sql.DropColumnsBuilder;
+import org.sonar.server.platform.db.migration.step.DdlChange;
+
+public class DropIdColumnOfNotificationTable extends DdlChange {
+
+  public DropIdColumnOfNotificationTable(Database db) {
+    super(db);
+  }
+
+  @Override
+  public void execute(Context context) throws SQLException {
+    context.execute(new DropColumnsBuilder(getDialect(), "notifications", "id").build());
+  }
+}
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v83/notifications/DropPrimaryKeyOnIdColumnOfNotificationTable.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v83/notifications/DropPrimaryKeyOnIdColumnOfNotificationTable.java
new file mode 100644 (file)
index 0000000..3ff0231
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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.v83.notifications;
+
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.server.platform.db.migration.step.DdlChange;
+import org.sonar.server.platform.db.migration.version.v83.util.DropPrimaryKeySqlGenerator;
+
+public class DropPrimaryKeyOnIdColumnOfNotificationTable extends DdlChange {
+
+  private final DropPrimaryKeySqlGenerator dropPrimaryKeySqlGenerator;
+
+  public DropPrimaryKeyOnIdColumnOfNotificationTable(Database db, DropPrimaryKeySqlGenerator dropPrimaryKeySqlGenerator) {
+    super(db);
+    this.dropPrimaryKeySqlGenerator = dropPrimaryKeySqlGenerator;
+  }
+
+  @Override
+  public void execute(Context context) throws SQLException {
+    context.execute(dropPrimaryKeySqlGenerator.generate("notifications", "notifications","id"));
+  }
+
+}
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v83/notifications/MakeNotificationUuidAndCreatedAtColumnsNotNullable.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v83/notifications/MakeNotificationUuidAndCreatedAtColumnsNotNullable.java
new file mode 100644 (file)
index 0000000..c0aa4de
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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.v83.notifications;
+
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.server.platform.db.migration.def.BigIntegerColumnDef;
+import org.sonar.server.platform.db.migration.def.VarcharColumnDef;
+import org.sonar.server.platform.db.migration.sql.AlterColumnsBuilder;
+import org.sonar.server.platform.db.migration.step.DdlChange;
+
+import static org.sonar.server.platform.db.migration.def.BigIntegerColumnDef.newBigIntegerColumnDefBuilder;
+import static org.sonar.server.platform.db.migration.def.VarcharColumnDef.newVarcharColumnDefBuilder;
+
+public class MakeNotificationUuidAndCreatedAtColumnsNotNullable extends DdlChange {
+  private static final String TABLE = "notifications";
+
+  private static final VarcharColumnDef uuidColumnDefinition = newVarcharColumnDefBuilder()
+    .setColumnName("uuid")
+    .setIsNullable(false)
+    .setDefaultValue(null)
+    .setLimit(VarcharColumnDef.UUID_SIZE)
+    .build();
+
+  private static final BigIntegerColumnDef createdAtColumnDefinition = newBigIntegerColumnDefBuilder()
+    .setColumnName("created_at")
+    .setIsNullable(false)
+    .build();
+
+
+  public MakeNotificationUuidAndCreatedAtColumnsNotNullable(Database db) {
+    super(db);
+  }
+
+  @Override
+  public void execute(Context context) throws SQLException {
+    context.execute(new AlterColumnsBuilder(getDialect(), TABLE)
+      .updateColumn(uuidColumnDefinition)
+      .updateColumn(createdAtColumnDefinition)
+      .build());
+  }
+}
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v83/notifications/PopulateNotificationUuidAndCreatedAt.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v83/notifications/PopulateNotificationUuidAndCreatedAt.java
new file mode 100644 (file)
index 0000000..a34eab5
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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.v83.notifications;
+
+import java.sql.SQLException;
+import java.util.concurrent.atomic.AtomicLong;
+import org.sonar.api.utils.System2;
+import org.sonar.core.util.UuidFactory;
+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 PopulateNotificationUuidAndCreatedAt extends DataChange {
+
+  private final UuidFactory uuidFactory;
+  private final System2 system2;
+
+  public PopulateNotificationUuidAndCreatedAt(Database db, UuidFactory uuidFactory, System2 system2) {
+    super(db);
+    this.uuidFactory = uuidFactory;
+    this.system2 = system2;
+  }
+
+  @Override
+  protected void execute(Context context) throws SQLException {
+    MassUpdate massUpdate = context.prepareMassUpdate();
+
+    massUpdate.select("select id from notifications order by id asc");
+    massUpdate.update("update notifications set uuid = ?, created_at = ? where id = ?");
+
+    // now - 7 days, to have previous notification in the past
+    long lastWeek = system2.now() - (1000 * 60 * 60 * 24 * 7);
+
+    AtomicLong cpt = new AtomicLong(0);
+    massUpdate.execute((row, update) -> {
+      update.setString(1, uuidFactory.create());
+      update.setLong(2, lastWeek + cpt.longValue());
+      update.setLong(3, row.getLong(1));
+      cpt.addAndGet(1);
+      return true;
+    });
+  }
+}
index e3d3655a36b6a581060af86f5eb4fe91758fc78d..786d77e604262660ce90185750866432b5d75b16 100644 (file)
@@ -52,6 +52,12 @@ public class DbVersionTestUtils {
     assertThat(registry.migrationNumbers).hasSize(migrationCount);
   }
 
+  public static void verifyMigrationNotEmpty(DbVersion underTest) {
+    TestMigrationStepRegistry registry = new TestMigrationStepRegistry();
+    underTest.addSteps(registry);
+    assertThat(registry.migrationNumbers).isNotEmpty();
+  }
+
   private static class TestMigrationStepRegistry implements MigrationStepRegistry {
     private Set<Long> migrationNumbers = new HashSet<>();
 
index 6fd177bf8e9bd26b8a573f6255f84cf7f2ae313c..e298cf83538a7b3781f4a00f81f5f187e77b8598 100644 (file)
@@ -22,7 +22,7 @@ package org.sonar.server.platform.db.migration.version.v83;
 import org.junit.Test;
 import org.sonar.server.platform.db.migration.version.DbVersion;
 
-import static org.sonar.server.platform.db.migration.version.DbVersionTestUtils.verifyMigrationCount;
+import static org.sonar.server.platform.db.migration.version.DbVersionTestUtils.verifyMigrationNotEmpty;
 import static org.sonar.server.platform.db.migration.version.DbVersionTestUtils.verifyMinimumMigrationNumber;
 
 public class DbVersion83Test {
@@ -36,7 +36,7 @@ public class DbVersion83Test {
 
   @Test
   public void verify_migration_count() {
-    verifyMigrationCount(underTest, 15);
+    verifyMigrationNotEmpty(underTest);
   }
 
 }
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v83/notifications/AddPrimaryKeyOnUuidColumnOfNotificationTableTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v83/notifications/AddPrimaryKeyOnUuidColumnOfNotificationTableTest.java
new file mode 100644 (file)
index 0000000..4ea3fef
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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.v83.notifications;
+
+import java.sql.SQLException;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.db.CoreDbTester;
+import org.sonar.server.platform.db.migration.step.MigrationStep;
+
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+public class AddPrimaryKeyOnUuidColumnOfNotificationTableTest {
+
+  @Rule
+  public CoreDbTester db = CoreDbTester.createForSchema(AddPrimaryKeyOnUuidColumnOfNotificationTableTest.class, "schema.sql");
+
+  private MigrationStep underTest = new AddPrimaryKeyOnUuidColumnOfNotificationTable(db.database());
+
+  @Test
+  public void execute() throws SQLException {
+    underTest.execute();
+
+    db.assertPrimaryKey("notifications", "pk_notifications", "uuid");
+  }
+
+  @Test
+  public void migration_is_not_re_entrant() throws SQLException {
+    underTest.execute();
+
+    assertThatThrownBy(() -> underTest.execute()).isInstanceOf(IllegalStateException.class);
+  }
+
+}
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v83/notifications/AddUuidAndCreatedAtColumnsToNotificationTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v83/notifications/AddUuidAndCreatedAtColumnsToNotificationTest.java
new file mode 100644 (file)
index 0000000..6215068
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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.v83.notifications;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectOutputStream;
+import java.sql.SQLException;
+import java.sql.Types;
+import org.apache.commons.io.IOUtils;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.SonarException;
+import org.sonar.db.CoreDbTester;
+import org.sonar.server.platform.db.migration.step.DdlChange;
+
+import static java.sql.Types.BIGINT;
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class AddUuidAndCreatedAtColumnsToNotificationTest {
+
+  @Rule
+  public CoreDbTester db = CoreDbTester.createForSchema(AddUuidAndCreatedAtColumnsToNotificationTest.class, "schema.sql");
+
+  private DdlChange underTest = new AddUuidAndCreatedAtColumnsToNotification(db.database());
+
+  @Before
+  public void setup() {
+    insertNotification(1L, "data1");
+    insertNotification(2L, "data2");
+    insertNotification(3L, "data3");
+  }
+
+  @Test
+  public void add_uuid_and_created_at_columns_to_notification() throws SQLException {
+    underTest.execute();
+
+    db.assertColumnDefinition("notifications", "uuid", Types.VARCHAR, 40, true);
+    db.assertColumnDefinition("notifications", "created_at", BIGINT, null, true);
+
+    assertThat(db.countSql("select count(id) from notifications"))
+      .isEqualTo(3);
+  }
+
+  private void insertNotification(Long id, String data) {
+
+    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+    try {
+      ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
+      objectOutputStream.writeObject(data);
+      objectOutputStream.close();
+      byte[] byteArray = byteArrayOutputStream.toByteArray();
+
+      db.executeInsert("notifications",
+        "id", id,
+        "data", byteArray);
+
+    } catch (IOException e) {
+      throw new SonarException("Unable to write notification", e);
+
+    } finally {
+      IOUtils.closeQuietly(byteArrayOutputStream);
+    }
+  }
+}
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v83/notifications/DropIdColumnOfNotificationTableTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v83/notifications/DropIdColumnOfNotificationTableTest.java
new file mode 100644 (file)
index 0000000..957991e
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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.v83.notifications;
+
+import java.sql.SQLException;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.db.CoreDbTester;
+import org.sonar.server.platform.db.migration.step.MigrationStep;
+
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+public class DropIdColumnOfNotificationTableTest {
+
+  @Rule
+  public CoreDbTester db = CoreDbTester.createForSchema(DropIdColumnOfNotificationTableTest.class, "schema.sql");
+
+  private MigrationStep underTest = new DropIdColumnOfNotificationTable(db.database());
+
+  @Test
+  public void execute() throws SQLException {
+    underTest.execute();
+
+    db.assertColumnDoesNotExist("notifications", "id");
+  }
+
+  @Test
+  public void migration_is_not_re_entrant() throws SQLException {
+    underTest.execute();
+
+    assertThatThrownBy(() -> underTest.execute()).isInstanceOf(IllegalStateException.class);
+  }
+
+}
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v83/notifications/DropPrimaryKeyOnIdColumnOfNotificationTableTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v83/notifications/DropPrimaryKeyOnIdColumnOfNotificationTableTest.java
new file mode 100644 (file)
index 0000000..9c80ca5
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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.v83.notifications;
+
+import java.sql.SQLException;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.db.CoreDbTester;
+import org.sonar.server.platform.db.migration.step.MigrationStep;
+import org.sonar.server.platform.db.migration.version.v83.util.DropPrimaryKeySqlGenerator;
+import org.sonar.server.platform.db.migration.version.v83.util.GetConstraintHelper;
+
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+public class DropPrimaryKeyOnIdColumnOfNotificationTableTest {
+
+  private static final String TABLE_NAME = "notifications";
+  @Rule
+  public CoreDbTester db = CoreDbTester.createForSchema(DropPrimaryKeyOnIdColumnOfNotificationTableTest.class, "schema.sql");
+
+  private DropPrimaryKeySqlGenerator dropPrimaryKeySqlGenerator = new DropPrimaryKeySqlGenerator(db.database(), new GetConstraintHelper(db.database()));
+
+  private MigrationStep underTest = new DropPrimaryKeyOnIdColumnOfNotificationTable(db.database(), dropPrimaryKeySqlGenerator);
+
+  @Test
+  public void execute() throws SQLException {
+    underTest.execute();
+
+    db.assertNoPrimaryKey(TABLE_NAME);
+  }
+
+  @Test
+  public void migration_is_not_re_entrant() throws SQLException {
+    underTest.execute();
+
+    assertThatThrownBy(() -> underTest.execute()).isInstanceOf(IllegalStateException.class);
+  }
+
+}
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v83/notifications/MakeNotificationUuidAndCreatedAtColumnsNotNullableTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v83/notifications/MakeNotificationUuidAndCreatedAtColumnsNotNullableTest.java
new file mode 100644 (file)
index 0000000..87f1dc1
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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.v83.notifications;
+
+import java.sql.SQLException;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.db.CoreDbTester;
+import org.sonar.server.platform.db.migration.step.MigrationStep;
+
+import static java.sql.Types.BIGINT;
+import static java.sql.Types.VARCHAR;
+
+public class MakeNotificationUuidAndCreatedAtColumnsNotNullableTest {
+
+  @Rule
+  public CoreDbTester db = CoreDbTester.createForSchema(MakeNotificationUuidAndCreatedAtColumnsNotNullableTest.class, "schema.sql");
+
+  private MigrationStep underTest = new MakeNotificationUuidAndCreatedAtColumnsNotNullable(db.database());
+
+  @Test
+  public void created_at_and_uuid_columns_are_not_null() throws SQLException {
+    underTest.execute();
+
+    db.assertColumnDefinition("notifications", "uuid", VARCHAR, null, false);
+    db.assertColumnDefinition("notifications", "created_at", BIGINT, null, false);
+  }
+}
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v83/notifications/PopulateNotificationUuidAndCreatedAtTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v83/notifications/PopulateNotificationUuidAndCreatedAtTest.java
new file mode 100644 (file)
index 0000000..6b3611f
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2020 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.v83.notifications;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectOutputStream;
+import java.sql.SQLException;
+import java.util.Objects;
+import java.util.stream.Collectors;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.System2;
+import org.sonar.core.util.UuidFactory;
+import org.sonar.core.util.UuidFactoryFast;
+import org.sonar.db.CoreDbTester;
+import org.sonar.server.platform.db.migration.step.DataChange;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class PopulateNotificationUuidAndCreatedAtTest {
+
+  @Rule
+  public CoreDbTester db = CoreDbTester.createForSchema(PopulateNotificationUuidAndCreatedAtTest.class, "schema.sql");
+
+  private UuidFactory uuidFactory = UuidFactoryFast.getInstance();
+  private System2 system2 = mock(System2.class);
+  private DataChange underTest = new PopulateNotificationUuidAndCreatedAt(db.database(), uuidFactory, system2);
+
+  @Before
+  public void before() {
+    // exactly one week before now + 1ms, so that ID are exactly equals to timestamp in the tests
+    when(system2.now()).thenReturn((1000 * 60 * 60 * 24 * 7) + 1L);
+  }
+
+  @Test
+  public void populate_uuids_and_created_at() throws IOException, SQLException {
+    insertNotification(1L, "data1");
+    insertNotification(2L, "data2");
+    insertNotification(3L, "data3");
+
+    underTest.execute();
+
+    verifyUuidsAreNotNull();
+    verifyCreatedAt();
+  }
+
+  @Test
+  public void migration_is_reentrant() throws IOException, SQLException {
+    insertNotification(1L, "data1");
+    insertNotification(2L, "data2");
+    insertNotification(3L, "data3");
+
+    underTest.execute();
+    // re-entrant
+    underTest.execute();
+
+    verifyUuidsAreNotNull();
+    verifyCreatedAt();
+  }
+
+  private void verifyUuidsAreNotNull() {
+    assertThat(db.select("select uuid from notifications")
+      .stream()
+      .map(row -> row.get("UUID"))
+      .filter(Objects::isNull)
+      .collect(Collectors.toList())).isEmpty();
+  }
+
+  private void verifyCreatedAt() {
+    assertThat(db.select("select id, created_at from notifications")
+      .stream()
+      .filter(row -> !row.get("CREATED_AT").equals(row.get("ID")))
+      .collect(Collectors.toList())).isEmpty();
+
+  }
+
+  private void insertNotification(Long id, String data) throws IOException {
+    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+    ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
+    objectOutputStream.writeObject(data);
+    objectOutputStream.close();
+    byte[] byteArray = byteArrayOutputStream.toByteArray();
+
+    db.executeInsert("notifications",
+      "id", id,
+      "data", byteArray);
+  }
+
+}
diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v83/notifications/AddPrimaryKeyOnUuidColumnOfNotificationTableTest/schema.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v83/notifications/AddPrimaryKeyOnUuidColumnOfNotificationTableTest/schema.sql
new file mode 100644 (file)
index 0000000..530b33a
--- /dev/null
@@ -0,0 +1,6 @@
+CREATE TABLE "NOTIFICATIONS"(
+    "ID" INTEGER NOT NULL,
+    "DATA" BLOB,
+    "UUID" VARCHAR(40) NOT NULL,
+    "CREATED_AT" BIGINT NOT NULL
+);
diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v83/notifications/AddUuidAndCreatedAtColumnsToNotificationTest/schema.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v83/notifications/AddUuidAndCreatedAtColumnsToNotificationTest/schema.sql
new file mode 100644 (file)
index 0000000..bde193d
--- /dev/null
@@ -0,0 +1,5 @@
+CREATE TABLE "NOTIFICATIONS"(
+    "ID" INTEGER NOT NULL AUTO_INCREMENT (1,1),
+    "DATA" BLOB
+);
+ALTER TABLE "NOTIFICATIONS" ADD CONSTRAINT "PK_NOTIFICATIONS" PRIMARY KEY("ID");
diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v83/notifications/DropIdColumnOfNotificationTableTest/schema.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v83/notifications/DropIdColumnOfNotificationTableTest/schema.sql
new file mode 100644 (file)
index 0000000..b007218
--- /dev/null
@@ -0,0 +1,7 @@
+CREATE TABLE "NOTIFICATIONS"(
+    "ID" INTEGER NOT NULL,
+    "DATA" BLOB,
+    "UUID" VARCHAR(40) NOT NULL,
+    "CREATED_AT" BIGINT NOT NULL
+);
+ALTER TABLE "NOTIFICATIONS" ADD CONSTRAINT "PK_NOTIFICATIONS" PRIMARY KEY("UUID");
diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v83/notifications/DropPrimaryKeyOnIdColumnOfNotificationTableTest/schema.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v83/notifications/DropPrimaryKeyOnIdColumnOfNotificationTableTest/schema.sql
new file mode 100644 (file)
index 0000000..082c809
--- /dev/null
@@ -0,0 +1,7 @@
+CREATE TABLE "NOTIFICATIONS"(
+    "ID" INTEGER NOT NULL AUTO_INCREMENT (1,1),
+    "UUID" VARCHAR(40) NOT NULL,
+    "CREATED_AT" BIGINT NOT NULL,
+    "DATA" BLOB
+);
+ALTER TABLE "NOTIFICATIONS" ADD CONSTRAINT "PK_NOTIFICATIONS" PRIMARY KEY("ID");
diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v83/notifications/MakeNotificationUuidAndCreatedAtColumnsNotNullableTest/schema.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v83/notifications/MakeNotificationUuidAndCreatedAtColumnsNotNullableTest/schema.sql
new file mode 100644 (file)
index 0000000..3e3bae5
--- /dev/null
@@ -0,0 +1,7 @@
+CREATE TABLE "NOTIFICATIONS"(
+    "ID" INTEGER NOT NULL AUTO_INCREMENT (1,1),
+    "UUID" VARCHAR(40),
+    "CREATED_AT" BIGINT,
+    "DATA" BLOB
+);
+ALTER TABLE "NOTIFICATIONS" ADD CONSTRAINT "PK_NOTIFICATIONS" PRIMARY KEY("ID");
diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v83/notifications/PopulateNotificationUuidAndCreatedAtTest/schema.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v83/notifications/PopulateNotificationUuidAndCreatedAtTest/schema.sql
new file mode 100644 (file)
index 0000000..ca8451d
--- /dev/null
@@ -0,0 +1,7 @@
+CREATE TABLE "NOTIFICATIONS"(
+    "ID" INTEGER NOT NULL AUTO_INCREMENT (1,1),
+    "UUID" VARCHAR(40),
+    "CREATED_AT" BIGINT NULL,
+    "DATA" BLOB
+);
+ALTER TABLE "NOTIFICATIONS" ADD CONSTRAINT "PK_NOTIFICATIONS" PRIMARY KEY("ID");
index 91a41921994c146b580eea56b18ffa4012e04989..d164616accc10e079c0c742e3e71b084cd480e3a 100644 (file)
@@ -36,8 +36,10 @@ import javax.annotation.Nullable;
 import org.sonar.api.notifications.Notification;
 import org.sonar.api.notifications.NotificationChannel;
 import org.sonar.api.utils.SonarException;
+import org.sonar.api.utils.System2;
 import org.sonar.api.utils.log.Logger;
 import org.sonar.api.utils.log.Loggers;
+import org.sonar.core.util.UuidFactory;
 import org.sonar.core.util.stream.MoreCollectors;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
@@ -59,13 +61,13 @@ public class DefaultNotificationManager implements NotificationManager {
   private NotificationChannel[] notificationChannels;
   private final DbClient dbClient;
 
+
   private boolean alreadyLoggedDeserializationIssue = false;
 
   /**
    * Default constructor used by Pico
    */
-  public DefaultNotificationManager(NotificationChannel[] channels,
-    DbClient dbClient) {
+  public DefaultNotificationManager(NotificationChannel[] channels, DbClient dbClient) {
     this.notificationChannels = channels;
     this.dbClient = dbClient;
   }
index c3297286b6ecee0ded954ee82de5d14886d909b9..7997c3f1f82811fc625487f0e0fa22859a7301d4 100644 (file)
@@ -38,7 +38,10 @@ import org.junit.rules.ExpectedException;
 import org.mockito.InOrder;
 import org.sonar.api.notifications.Notification;
 import org.sonar.api.notifications.NotificationChannel;
+import org.sonar.api.utils.System2;
 import org.sonar.api.web.UserRole;
+import org.sonar.core.util.UuidFactory;
+import org.sonar.core.util.UuidFactoryFast;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
 import org.sonar.db.EmailSubscriberDto;
@@ -83,6 +86,7 @@ public class DefaultNotificationManagerTest {
   private AuthorizationDao authorizationDao = mock(AuthorizationDao.class);
   private DbClient dbClient = mock(DbClient.class);
   private DbSession dbSession = mock(DbSession.class);
+  private System2 system2 = mock(System2.class);
 
   @Before
   public void setUp() {
@@ -93,6 +97,7 @@ public class DefaultNotificationManagerTest {
     when(dbClient.propertiesDao()).thenReturn(propertiesDao);
     when(dbClient.notificationQueueDao()).thenReturn(notificationQueueDao);
     when(dbClient.authorizationDao()).thenReturn(authorizationDao);
+    when(system2.now()).thenReturn(0L);
 
     underTest = new DefaultNotificationManager(new NotificationChannel[] {emailChannel, twitterChannel}, dbClient);
   }
@@ -345,7 +350,8 @@ public class DefaultNotificationManagerTest {
     String projectKey = randomAlphabetic(6);
     when(propertiesDao.findEmailSubscribersForNotification(dbSession, dispatcherKey, "EmailNotificationChannel", projectKey))
       .thenReturn(
-        newHashSet(EmailSubscriberDto.create("user1", false, "user1@foo"), EmailSubscriberDto.create("user3", false, "user3@foo"), EmailSubscriberDto.create("user3", true, "user3@foo")));
+        newHashSet(EmailSubscriberDto.create("user1", false, "user1@foo"), EmailSubscriberDto.create("user3", false, "user3@foo"),
+          EmailSubscriberDto.create("user3", true, "user3@foo")));
     when(authorizationDao.keepAuthorizedLoginsOnProject(dbSession, newHashSet("user3", "user4"), projectKey, globalPermission))
       .thenReturn(newHashSet("user3"));
     when(authorizationDao.keepAuthorizedLoginsOnProject(dbSession, newHashSet("user1", "user3"), projectKey, projectPermission))
@@ -370,7 +376,8 @@ public class DefaultNotificationManagerTest {
     Set<String> logins = ImmutableSet.of("user1", "user2", "user3");
     when(propertiesDao.findEmailSubscribersForNotification(dbSession, dispatcherKey, "EmailNotificationChannel", projectKey, logins))
       .thenReturn(
-        newHashSet(EmailSubscriberDto.create("user1", false, "user1@foo"), EmailSubscriberDto.create("user3", false, "user3@foo"), EmailSubscriberDto.create("user3", true, "user3@foo")));
+        newHashSet(EmailSubscriberDto.create("user1", false, "user1@foo"), EmailSubscriberDto.create("user3", false, "user3@foo"),
+          EmailSubscriberDto.create("user3", true, "user3@foo")));
     when(authorizationDao.keepAuthorizedLoginsOnProject(dbSession, newHashSet("user3", "user4"), projectKey, globalPermission))
       .thenReturn(newHashSet("user3"));
     when(authorizationDao.keepAuthorizedLoginsOnProject(dbSession, newHashSet("user1", "user3"), projectKey, projectPermission))