Skip to content

Commit

Permalink
Notification migration (#2063)
Browse files Browse the repository at this point in the history
Add compatibility between Notification V1 and Notification V2. Integrate Notification V2 methods into Notification V1 to begin populating the database in advance of the data migration, ensuring clients can transition to V2 seamlessly without any changes on their end.
  • Loading branch information
PooyaRaki authored Dec 5, 2024
1 parent 7b55a0e commit e455fba
Show file tree
Hide file tree
Showing 50 changed files with 2,334 additions and 1,585 deletions.
35 changes: 35 additions & 0 deletions migrations/1726452966034-notification_cleanup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import type { MigrationInterface, QueryRunner } from 'typeorm';

export class NotificationCleanup1726452966034 implements MigrationInterface {
name = 'NotificationCleanup1726452966034';

public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`DROP TRIGGER IF EXISTS "update_push_notification_devices_updated_at" ON "push_notification_devices"`,
);
await queryRunner.query(
`DROP TRIGGER IF EXISTS "update_notification_subscriptions_updated_at" ON "notification_subscriptions"`,
);
await queryRunner.query(
`DROP TYPE IF EXISTS "notification_types_name_enum" CASCADE`,
);
await queryRunner.query(
`DROP TYPE IF EXISTS "push_notification_devices_device_type_enum" CASCADE`,
);
await queryRunner.query(`DROP FUNCTION IF EXISTS update_updated_at()`);
await queryRunner.query(
`DROP TABLE IF EXISTS "push_notification_devices" CASCADE`,
);
await queryRunner.query(
`DROP TABLE IF EXISTS "notification_subscriptions" CASCADE`,
);
await queryRunner.query(
`DROP TABLE IF EXISTS "notification_subscription_notification_types" CASCADE`,
);
await queryRunner.query(
`DROP TABLE IF EXISTS "notification_types" CASCADE`,
);
}

public async down(): Promise<void> {}
}
10 changes: 5 additions & 5 deletions migrations/1726752966034-notification.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,22 @@ export class Notification1726752966034 implements MigrationInterface {
`CREATE TABLE "push_notification_devices" ("id" SERIAL NOT NULL, "device_type" character varying(255) NOT NULL, "device_uuid" uuid NOT NULL, "cloud_messaging_token" character varying(255) NOT NULL, "created_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updated_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), CONSTRAINT "device_uuid" UNIQUE ("device_uuid"), CONSTRAINT "PK_e387f5cc5b4f66d63804d596c64" PRIMARY KEY ("id"))`,
);
await queryRunner.query(
`CREATE TABLE "notification_subscriptions" ("id" SERIAL NOT NULL, "chain_id" character varying(255) NOT NULL, "safe_address" character varying(42) NOT NULL, "signer_address" character varying(42), "created_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updated_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "pushNotificationDeviceId" integer, CONSTRAINT "UQ_3c2531929422835e4f2717ec5db" UNIQUE ("chain_id", "safe_address", "pushNotificationDeviceId", "signer_address"), CONSTRAINT "PK_8cfec5d2a549ff20d1f4e648226" PRIMARY KEY ("id"))`,
`CREATE TABLE "notification_subscriptions" ("id" SERIAL NOT NULL, "chain_id" character varying(255) NOT NULL, "safe_address" character varying(42) NOT NULL, "signer_address" character varying(42), "created_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updated_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "push_notification_device_id" integer, CONSTRAINT "UQ_3c2531929422835e4f2717ec5db" UNIQUE ("chain_id", "safe_address", "push_notification_device_id", "signer_address"), CONSTRAINT "PK_8cfec5d2a549ff20d1f4e648226" PRIMARY KEY ("id"))`,
);
await queryRunner.query(
`CREATE TABLE "notification_subscription_notification_types" ("id" SERIAL NOT NULL, "notificationSubscriptionId" integer, "notificationTypeId" integer, CONSTRAINT "UQ_5e7563e15aa2f994bd7b07ecec8" UNIQUE ("notificationSubscriptionId", "notificationTypeId"), CONSTRAINT "PK_3754c1a419741973072e5ed92eb" PRIMARY KEY ("id"))`,
`CREATE TABLE "notification_subscription_notification_types" ("id" SERIAL NOT NULL, "notification_subscription_id" integer, "notification_type_id" integer, CONSTRAINT "UQ_5e7563e15aa2f994bd7b07ecec8" UNIQUE ("notification_subscription_id", "notification_type_id"), CONSTRAINT "PK_3754c1a419741973072e5ed92eb" PRIMARY KEY ("id"))`,
);
await queryRunner.query(
`CREATE TABLE "notification_types" ("id" SERIAL NOT NULL, "name" character varying(255) NOT NULL, CONSTRAINT "name" UNIQUE ("name"), CONSTRAINT "PK_aa965e094494e2c4c5942cfb42d" PRIMARY KEY ("id"))`,
);
await queryRunner.query(
`ALTER TABLE "notification_subscriptions" ADD CONSTRAINT "FK_9f59e655926203074b833d6f909" FOREIGN KEY ("pushNotificationDeviceId") REFERENCES "push_notification_devices"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
`ALTER TABLE "notification_subscriptions" ADD CONSTRAINT "FK_9f59e655926203074b833d6f909" FOREIGN KEY ("push_notification_device_id") REFERENCES "push_notification_devices"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
);
await queryRunner.query(
`ALTER TABLE "notification_subscription_notification_types" ADD CONSTRAINT "FK_44702b7d6132421d2049ed994de" FOREIGN KEY ("notificationSubscriptionId") REFERENCES "notification_subscriptions"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
`ALTER TABLE "notification_subscription_notification_types" ADD CONSTRAINT "FK_44702b7d6132421d2049ed994de" FOREIGN KEY ("notification_subscription_id") REFERENCES "notification_subscriptions"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
);
await queryRunner.query(
`ALTER TABLE "notification_subscription_notification_types" ADD CONSTRAINT "FK_3e3e49a32dc1862742a322a6149" FOREIGN KEY ("notificationTypeId") REFERENCES "notification_types"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
`ALTER TABLE "notification_subscription_notification_types" ADD CONSTRAINT "FK_3e3e49a32dc1862742a322a6149" FOREIGN KEY ("notification_type_id") REFERENCES "notification_types"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
);
}

Expand Down
2 changes: 1 addition & 1 deletion migrations/1727451367471-notifications_enum.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
import type { MigrationInterface, QueryRunner } from 'typeorm';

export class NotificationsEnum1727451367471 implements MigrationInterface {
name = 'NotificationsEnum1727451367471';
Expand Down
2 changes: 1 addition & 1 deletion migrations/1727701600427-update_timestamp_trigger.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
import type { MigrationInterface, QueryRunner } from 'typeorm';

export class UpdateTimestampTrigger1727701600427 implements MigrationInterface {
name = 'UpdateTimestampTrigger1727701600427';
Expand Down
2 changes: 1 addition & 1 deletion migrations/1727701873513-notification_update_updated_at.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
import type { MigrationInterface, QueryRunner } from 'typeorm';

export class NotificationUpdateUpdatedAt1727701873513
implements MigrationInterface
Expand Down
2 changes: 1 addition & 1 deletion src/datasources/db/v1/entities/row.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@ export type Row = z.infer<typeof RowSchema>;
*/
export const RowSchema = z.object({
id: z.number().int(),
created_at: z.coerce.date(),
created_at: z.coerce.date(), // @TODO when migrated all the entities to TypeOrm Remove `.coerce`
updated_at: z.coerce.date(),
});

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import type { IBuilder } from '@/__tests__/builder';
import { Builder } from '@/__tests__/builder';
import type { NotificationDevice } from '@/datasources/notifications/entities/notification-devices.entity.db';
import { DeviceType } from '@/domain/notifications/v2/entities/device-type.entity';
import { faker } from '@faker-js/faker/.';
import type { UUID } from 'crypto';

export function notificationDeviceBuilder(): IBuilder<NotificationDevice> {
return new Builder<NotificationDevice>()
.with('id', faker.number.int())
.with('device_uuid', faker.string.uuid() as UUID)
.with('device_type', faker.helpers.enumValue(DeviceType))
.with(
'cloud_messaging_token',
faker.string.alphanumeric({ length: { min: 10, max: 255 } }),
)
.with('created_at', new Date())
.with('updated_at', new Date());
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import type { IBuilder } from '@/__tests__/builder';
import { Builder } from '@/__tests__/builder';
import { faker } from '@faker-js/faker/.';
import { notificationTypeBuilder } from '@/datasources/notifications/entities/__tests__/notification-type.entity.db.builder';
import { notificationSubscriptionBuilder } from '@/datasources/notifications/entities/__tests__/notification-subscription.entity.db.builder';
import type { NotificationSubscriptionNotificationType } from '@/datasources/notifications/entities/notification-subscription-notification-type.entity.db';

export function notificationSubscriptionNotificationTypeTypeBuilder(): IBuilder<NotificationSubscriptionNotificationType> {
return new Builder<NotificationSubscriptionNotificationType>()
.with('id', faker.number.int())
.with(
'notification_subscription',
notificationSubscriptionBuilder().build(),
)
.with('notification_type', notificationTypeBuilder().build());
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import type { IBuilder } from '@/__tests__/builder';
import { Builder } from '@/__tests__/builder';
import { notificationDeviceBuilder } from '@/datasources/notifications/entities/__tests__/notification-devices.entity.db.builder';
import type { NotificationSubscription } from '@/datasources/notifications/entities/notification-subscription.entity.db';
import { faker } from '@faker-js/faker/.';
import { getAddress } from 'viem';

export function notificationSubscriptionBuilder(): IBuilder<NotificationSubscription> {
return new Builder<NotificationSubscription>()
.with('id', faker.number.int())
.with('chain_id', faker.number.int({ min: 1, max: 100 }).toString())
.with('safe_address', getAddress(faker.finance.ethereumAddress()))
.with('signer_address', getAddress(faker.finance.ethereumAddress()))
.with('created_at', new Date())
.with('updated_at', new Date())
.with('notification_subscription_notification_type', [])
.with('push_notification_device', notificationDeviceBuilder().build());
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import type { IBuilder } from '@/__tests__/builder';
import { Builder } from '@/__tests__/builder';
import type { NotificationType } from '@/datasources/notifications/entities/notification-type.entity.db';
import { faker } from '@faker-js/faker/.';
import { NotificationType as NotificationTypeEnum } from '@/domain/notifications/v2/entities/notification.entity';

export function notificationTypeBuilder(): IBuilder<NotificationType> {
return new Builder<NotificationType>()
.with('id', faker.number.int())
.with('name', faker.helpers.enumValue(NotificationTypeEnum))
.with('notification_subscription_notification_type', []);
}
Original file line number Diff line number Diff line change
@@ -1,26 +1,36 @@
import { NotificationSubscription } from '@/datasources/notifications/entities/notification-subscription.entity';
import { NotificationSubscription } from '@/datasources/notifications/entities/notification-subscription.entity.db';
import { RowSchema } from '@/datasources/db/v1/entities/row.entity';
import { DeviceType } from '@/domain/notifications/v2/entities/device-type.entity';
import { UuidSchema } from '@/validation/entities/schemas/uuid.schema';
import type { UUID } from 'crypto';
import {
Check,
Column,
Entity,
Unique,
OneToMany,
PrimaryGeneratedColumn,
} from 'typeorm';
import { z } from 'zod';

export const NotificationDeviceSchema = RowSchema.extend({
device_type: z.nativeEnum(DeviceType),
device_uuid: UuidSchema,
cloud_messaging_token: z.string(),
});

@Entity('push_notification_devices')
@Unique('device_uuid', ['device_uuid'])
@Check('device_type', 'device_type IN ("ANDROID", "IOS", "WEB")')
export class NotificationDevice {
export class NotificationDevice
implements z.infer<typeof NotificationDeviceSchema>
{
@PrimaryGeneratedColumn()
id!: number;

@Column({
type: 'varchar',
length: 255,
type: 'enum',
enum: DeviceType,
})
device_type!: string;
device_type!: DeviceType;

@Column({ type: 'uuid' })
device_uuid!: UUID;
Expand All @@ -41,8 +51,12 @@ export class NotificationDevice {
})
updated_at!: Date;

@OneToMany(() => NotificationSubscription, (device) => device.id, {
onDelete: 'CASCADE',
})
@OneToMany(
() => NotificationSubscription,
(subscription) => subscription.id,
{
onDelete: 'CASCADE',
},
)
notification_subscriptions!: NotificationSubscription[];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import {
Entity,
JoinColumn,
ManyToOne,
PrimaryGeneratedColumn,
Unique,
} from 'typeorm';
import { NotificationType } from '@/datasources/notifications/entities/notification-type.entity.db';
import { NotificationSubscription } from '@/datasources/notifications/entities/notification-subscription.entity.db';
import { z } from 'zod';

export const NotificationSubscriptionNotificationTypeSchema = z.object({
id: z.number(),
});

@Entity('notification_subscription_notification_types')
@Unique(['notification_subscription', 'notification_type'])
export class NotificationSubscriptionNotificationType
implements z.infer<typeof NotificationSubscriptionNotificationTypeSchema>
{
@PrimaryGeneratedColumn()
id!: number;

@ManyToOne(
() => NotificationSubscription,
(subscription) => subscription.id,
{ onDelete: 'CASCADE' },
)
@JoinColumn({
name: 'notification_subscription_id',
})
notification_subscription!: NotificationSubscription;

@ManyToOne(
() => NotificationType,
(notificationType) =>
notificationType.notification_subscription_notification_type,
{
onDelete: 'CASCADE',
},
)
@JoinColumn({ name: 'notification_type_id' })
notification_type!: NotificationType;
}

This file was deleted.

Loading

0 comments on commit e455fba

Please sign in to comment.