From 266b329782ef7e5470845015fcb34e85ef70e75e Mon Sep 17 00:00:00 2001 From: John Meeuwissen Date: Fri, 21 Mar 2025 21:43:38 +0100 Subject: [PATCH 1/4] #342 final code + tests --- src/domain/notification/create/insertData.ts | 2 +- .../notification/getByPostId/getByPostId.ts | 17 ++++++++++++ src/domain/notification/getByPostId/index.ts | 2 ++ .../notification/getRecent/getRecent.ts | 3 ++- src/domain/notification/notify/removedPost.ts | 10 +++++++ .../notification/notify/subscriptions.ts | 3 +++ src/domain/notification/remove/deleteData.ts | 9 +++++++ src/domain/notification/remove/index.ts | 2 ++ .../fixtures/databases.fixture.ts | 2 +- .../notification/fixtures/records.fixture.ts | 15 ++++++----- .../notification/fixtures/values.fixture.ts | 3 ++- test/domain/notification/removedPost.spec.ts | 26 +++++++++++++++++++ 12 files changed, 83 insertions(+), 11 deletions(-) create mode 100644 src/domain/notification/getByPostId/getByPostId.ts create mode 100644 src/domain/notification/getByPostId/index.ts create mode 100644 src/domain/notification/notify/removedPost.ts create mode 100644 src/domain/notification/remove/deleteData.ts create mode 100644 src/domain/notification/remove/index.ts create mode 100644 test/domain/notification/removedPost.spec.ts diff --git a/src/domain/notification/create/insertData.ts b/src/domain/notification/create/insertData.ts index aa1171c6..1742358e 100644 --- a/src/domain/notification/create/insertData.ts +++ b/src/domain/notification/create/insertData.ts @@ -6,5 +6,5 @@ import type { DataModel } from '../types'; export default async function insertData(data: DataModel): Promise { - return database.createRecord(RECORD_TYPE, { ...data }); + return database.createRecord(RECORD_TYPE, { ...data, deleted: false }); } diff --git a/src/domain/notification/getByPostId/getByPostId.ts b/src/domain/notification/getByPostId/getByPostId.ts new file mode 100644 index 00000000..31037dd7 --- /dev/null +++ b/src/domain/notification/getByPostId/getByPostId.ts @@ -0,0 +1,17 @@ + +import database from '^/integrations/database'; + +import type { RecordQuery } from '^/integrations/database'; +import { RECORD_TYPE } from '../definitions'; +import type { DataModel } from '../types'; + +export default async function getByPostId(postId: string): Promise +{ + const query: RecordQuery = + { + deleted: { EQUALS: false }, + postId: { EQUALS: postId } + }; + + return database.searchRecords(RECORD_TYPE, query) as Promise; +} diff --git a/src/domain/notification/getByPostId/index.ts b/src/domain/notification/getByPostId/index.ts new file mode 100644 index 00000000..8c7bc494 --- /dev/null +++ b/src/domain/notification/getByPostId/index.ts @@ -0,0 +1,2 @@ + +export { default } from './getByPostId'; diff --git a/src/domain/notification/getRecent/getRecent.ts b/src/domain/notification/getRecent/getRecent.ts index 36f7be6e..640e31cc 100644 --- a/src/domain/notification/getRecent/getRecent.ts +++ b/src/domain/notification/getRecent/getRecent.ts @@ -1,5 +1,5 @@ -import type { RecordQuery, RecordSort} from '^/integrations/database'; +import type { RecordQuery, RecordSort } from '^/integrations/database'; import database, { SortDirections } from '^/integrations/database'; import { RECORD_TYPE } from '../definitions'; @@ -9,6 +9,7 @@ export default async function getRecent(receiverId: string, limit: number, offse { const query: RecordQuery = { + deleted: { EQUALS: false }, receiverId: { EQUALS: receiverId } }; diff --git a/src/domain/notification/notify/removedPost.ts b/src/domain/notification/notify/removedPost.ts new file mode 100644 index 00000000..2c4531f1 --- /dev/null +++ b/src/domain/notification/notify/removedPost.ts @@ -0,0 +1,10 @@ + +import getNotifications from '../getByPostId'; +import deleteData from '../remove'; + +export default async function removedPost(postId: string): Promise[]> +{ + const notifications = await getNotifications(postId); + + return notifications.map(item => deleteData(item.id)); +} diff --git a/src/domain/notification/notify/subscriptions.ts b/src/domain/notification/notify/subscriptions.ts index b85bc9c4..884fc682 100644 --- a/src/domain/notification/notify/subscriptions.ts +++ b/src/domain/notification/notify/subscriptions.ts @@ -1,10 +1,12 @@ import { subscribe as subscribeToPostCreated } from '^/domain/post/create'; +import { subscribe as subscribeToPostRemoved } from '^/domain/post/remove'; import { subscribe as subscribeToPostRated } from '^/domain/rating/toggle'; import { subscribe as subscribeToRelationEstablished } from '^/domain/relation/establish'; import reactedToPost from './createdPost'; import ratedPost from './ratedPost'; +import removedPost from './removedPost'; import startedFollowing from './startedFollowing'; async function subscribe(): Promise @@ -13,6 +15,7 @@ async function subscribe(): Promise subscribeToPostRated(({ creatorId, postId, rated }) => ratedPost(creatorId, postId, rated)), subscribeToPostCreated(({ creatorId, postId, parentId }) => reactedToPost(creatorId, postId, parentId)), subscribeToRelationEstablished(({ followerId, followingId }) => startedFollowing(followerId, followingId)), + subscribeToPostRemoved(({ postId }) => removedPost(postId)), ]); } diff --git a/src/domain/notification/remove/deleteData.ts b/src/domain/notification/remove/deleteData.ts new file mode 100644 index 00000000..2f66f82a --- /dev/null +++ b/src/domain/notification/remove/deleteData.ts @@ -0,0 +1,9 @@ + +import database from '^/integrations/database'; + +import { RECORD_TYPE } from '../definitions'; + +export default async function deleteData(id: string): Promise +{ + return database.updateRecord(RECORD_TYPE, id, { deleted: true }); +} diff --git a/src/domain/notification/remove/index.ts b/src/domain/notification/remove/index.ts new file mode 100644 index 00000000..49805c88 --- /dev/null +++ b/src/domain/notification/remove/index.ts @@ -0,0 +1,2 @@ + +export { default } from './deleteData'; diff --git a/test/domain/notification/fixtures/databases.fixture.ts b/test/domain/notification/fixtures/databases.fixture.ts index c35ce8a7..4a60a183 100644 --- a/test/domain/notification/fixtures/databases.fixture.ts +++ b/test/domain/notification/fixtures/databases.fixture.ts @@ -37,7 +37,7 @@ async function withCreatorsPostsAndNotifications(): Promise RECORDS.POSTS.map(post => database.createRecord(POST_RECORD_TYPE, { ...post })), RECORDS.POST_METRICS.map(postMetric => database.createRecord(POST_METRICS_RECORD_TYPE, { ...postMetric })), RECORDS.RATINGS.map(rating => database.createRecord(RATING_RECORD_TYPE, { rating })), - RECORDS.NOTIFICATIONS.map(notification => database.createRecord(NOTIFICATION_RECORD_TYPE, { ...notification })) + RECORDS.NOTIFICATIONS.map(notification => database.createRecord(NOTIFICATION_RECORD_TYPE, { ...notification, deleted: false })) ]; await Promise.all(promises.flat()); diff --git a/test/domain/notification/fixtures/records.fixture.ts b/test/domain/notification/fixtures/records.fixture.ts index bde961db..04ed7602 100644 --- a/test/domain/notification/fixtures/records.fixture.ts +++ b/test/domain/notification/fixtures/records.fixture.ts @@ -5,7 +5,7 @@ import type { DataModel as ComicDataModel } from '^/domain/comic'; import type { DataModel as CreatorDataModel } from '^/domain/creator'; import type { DataModel as CreatorMetricsDataModel } from '^/domain/creator.metrics'; import type { DataModel as ImageDataModel } from '^/domain/image'; -import type { DataModel as NotificationDataModel} from '^/domain/notification'; +import type { DataModel as NotificationDataModel } from '^/domain/notification'; import { Types } from '^/domain/notification'; import type { DataModel as PostDataModel } from '^/domain/post'; import type { DataModel as PostMetricsModel } from '^/domain/post.metrics'; @@ -56,12 +56,13 @@ const RATINGS: RatingDataModel[] = [ { id: VALUES.IDS.RATING2, createdAt: new Date().toISOString(), creatorId: VALUES.IDS.CREATOR2, postId: VALUES.IDS.REACTION_LIKED } ]; -const NOTIFICATIONS: NotificationDataModel[] = [ - { id: VALUES.IDS.NOTIFICATION1, createdAt: new Date().toISOString(), type: Types.STARTED_FOLLOWING, senderId: VALUES.IDS.CREATOR1, receiverId: VALUES.IDS.CREATOR2, postId: undefined }, - { id: VALUES.IDS.NOTIFICATION2, createdAt: new Date().toISOString(), type: Types.STARTED_FOLLOWING, senderId: VALUES.IDS.CREATOR2, receiverId: VALUES.IDS.CREATOR1, postId: undefined }, - { id: VALUES.IDS.NOTIFICATION3, createdAt: new Date('01-05-2024').toISOString(), type: Types.RATED_POST, senderId: VALUES.IDS.CREATOR3, receiverId: VALUES.IDS.CREATOR2, postId: VALUES.IDS.POST_RATED }, - { id: VALUES.IDS.NOTIFICATION4, createdAt: new Date('01-04-2024').toISOString(), type: Types.RATED_POST, senderId: VALUES.IDS.CREATOR2, receiverId: VALUES.IDS.CREATOR1, postId: VALUES.IDS.REACTION_LIKED }, - { id: VALUES.IDS.NOTIFICATION5, createdAt: new Date('01-03-2024').toISOString(), type: Types.RATED_POST, senderId: VALUES.IDS.CREATOR1, receiverId: VALUES.IDS.CREATOR1, postId: VALUES.IDS.POST_DELETED } +const NOTIFICATIONS: (NotificationDataModel & { deleted: boolean; })[] = [ + { id: VALUES.IDS.NOTIFICATION1, createdAt: new Date().toISOString(), type: Types.STARTED_FOLLOWING, senderId: VALUES.IDS.CREATOR1, receiverId: VALUES.IDS.CREATOR2, postId: undefined, deleted: false }, + { id: VALUES.IDS.NOTIFICATION2, createdAt: new Date().toISOString(), type: Types.STARTED_FOLLOWING, senderId: VALUES.IDS.CREATOR2, receiverId: VALUES.IDS.CREATOR1, postId: undefined, deleted: false }, + { id: VALUES.IDS.NOTIFICATION3, createdAt: new Date('01-05-2024').toISOString(), type: Types.RATED_POST, senderId: VALUES.IDS.CREATOR3, receiverId: VALUES.IDS.CREATOR2, postId: VALUES.IDS.POST_RATED, deleted: false }, + { id: VALUES.IDS.NOTIFICATION6, createdAt: new Date().toISOString(), type: Types.REACTED_TO_POST, senderId: VALUES.IDS.CREATOR1, receiverId: VALUES.IDS.CREATOR3, postId: VALUES.IDS.REACTION_LIKED, deleted: false }, + { id: VALUES.IDS.NOTIFICATION4, createdAt: new Date('01-04-2024').toISOString(), type: Types.RATED_POST, senderId: VALUES.IDS.CREATOR2, receiverId: VALUES.IDS.CREATOR1, postId: VALUES.IDS.REACTION_LIKED, deleted: false }, + { id: VALUES.IDS.NOTIFICATION5, createdAt: new Date('01-03-2024').toISOString(), type: Types.RATED_POST, senderId: VALUES.IDS.CREATOR1, receiverId: VALUES.IDS.CREATOR1, postId: VALUES.IDS.POST_DELETED, deleted: false } ]; export const RECORDS: Record = { CREATORS, CREATOR_METRICS, RELATIONS, IMAGES, COMICS, POSTS, POST_METRICS, RATINGS, NOTIFICATIONS }; diff --git a/test/domain/notification/fixtures/values.fixture.ts b/test/domain/notification/fixtures/values.fixture.ts index c7c560dc..46fed52b 100644 --- a/test/domain/notification/fixtures/values.fixture.ts +++ b/test/domain/notification/fixtures/values.fixture.ts @@ -27,7 +27,8 @@ export const VALUES = NOTIFICATION2: 'N2', NOTIFICATION3: 'N3', NOTIFICATION4: 'N4', - NOTIFICATION5: 'N5' + NOTIFICATION5: 'N5', + NOTIFICATION6: 'N6' }, STORAGE_KEYS: { diff --git a/test/domain/notification/removedPost.spec.ts b/test/domain/notification/removedPost.spec.ts new file mode 100644 index 00000000..938a7a96 --- /dev/null +++ b/test/domain/notification/removedPost.spec.ts @@ -0,0 +1,26 @@ + +import { beforeEach, describe, expect, it } from 'vitest'; + +import getByPostId from '^/domain/notification/getByPostId'; +import removedPost from '^/domain/notification/notify/removedPost'; +import { DATABASES, VALUES } from './fixtures'; + +beforeEach(async () => +{ + await Promise.all([ + DATABASES.withCreatorsPostsAndNotifications() + ]); +}); + +describe('domain/notification/remove', () => +{ + it('should remove all notifications of a removed post', async () => + { + await removedPost(VALUES.IDS.POST_RATED); + + const result = await getByPostId(VALUES.IDS.POST_RATED); + + expect(result).toHaveLength(0); + }); + +}); From 078844cd22d99d559292bd41e4026e54da3f4151 Mon Sep 17 00:00:00 2001 From: John Meeuwissen Date: Sun, 23 Mar 2025 17:39:06 +0100 Subject: [PATCH 2/4] #342 processed review comment --- src/domain/notification/getById/getById.ts | 8 +++++++- src/domain/notification/notify/removedPost.ts | 7 ++++--- src/domain/notification/remove/index.ts | 2 +- .../notification/remove/{deleteData.ts => remove.ts} | 4 ++-- 4 files changed, 14 insertions(+), 7 deletions(-) rename src/domain/notification/remove/{deleteData.ts => remove.ts} (65%) diff --git a/src/domain/notification/getById/getById.ts b/src/domain/notification/getById/getById.ts index 954137d2..55bb2837 100644 --- a/src/domain/notification/getById/getById.ts +++ b/src/domain/notification/getById/getById.ts @@ -1,10 +1,16 @@ import database from '^/integrations/database'; +import type { RecordQuery } from '^/integrations/database'; import { RECORD_TYPE } from '../definitions'; import type { DataModel } from '../types'; export default async function getById(id: string): Promise { - return database.readRecord(RECORD_TYPE, id) as Promise; + const query: RecordQuery = + { + id: { EQUALS: id }, + deleted: { EQUALS: false } + }; + return database.findRecord(RECORD_TYPE, query) as Promise; } diff --git a/src/domain/notification/notify/removedPost.ts b/src/domain/notification/notify/removedPost.ts index 2c4531f1..68e9ac6f 100644 --- a/src/domain/notification/notify/removedPost.ts +++ b/src/domain/notification/notify/removedPost.ts @@ -1,10 +1,11 @@ import getNotifications from '../getByPostId'; -import deleteData from '../remove'; +import remove from '../remove'; -export default async function removedPost(postId: string): Promise[]> +export default async function removedPost(postId: string): Promise { const notifications = await getNotifications(postId); + const promises = notifications.map(item => remove(item.id)); - return notifications.map(item => deleteData(item.id)); + await Promise.allSettled(promises); } diff --git a/src/domain/notification/remove/index.ts b/src/domain/notification/remove/index.ts index 49805c88..16bee921 100644 --- a/src/domain/notification/remove/index.ts +++ b/src/domain/notification/remove/index.ts @@ -1,2 +1,2 @@ -export { default } from './deleteData'; +export { default } from './remove'; diff --git a/src/domain/notification/remove/deleteData.ts b/src/domain/notification/remove/remove.ts similarity index 65% rename from src/domain/notification/remove/deleteData.ts rename to src/domain/notification/remove/remove.ts index 2f66f82a..1a1b244f 100644 --- a/src/domain/notification/remove/deleteData.ts +++ b/src/domain/notification/remove/remove.ts @@ -3,7 +3,7 @@ import database from '^/integrations/database'; import { RECORD_TYPE } from '../definitions'; -export default async function deleteData(id: string): Promise +export default async function remove(id: string): Promise { - return database.updateRecord(RECORD_TYPE, id, { deleted: true }); + return database.updateRecord(RECORD_TYPE, id, { deleted: true }) as Promise; } From 91ed63c139b7bfb424f136b3a2ce3d3ce6a339fe Mon Sep 17 00:00:00 2001 From: John Meeuwissen Date: Mon, 24 Mar 2025 10:13:43 +0100 Subject: [PATCH 3/4] #342 fix sonarqube issue --- src/domain/notification/remove/remove.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/domain/notification/remove/remove.ts b/src/domain/notification/remove/remove.ts index 1a1b244f..961a5a8f 100644 --- a/src/domain/notification/remove/remove.ts +++ b/src/domain/notification/remove/remove.ts @@ -5,5 +5,5 @@ import { RECORD_TYPE } from '../definitions'; export default async function remove(id: string): Promise { - return database.updateRecord(RECORD_TYPE, id, { deleted: true }) as Promise; + return database.updateRecord(RECORD_TYPE, id, { deleted: true }); } From f3546fefd4b12287b537c6b0b627cc0bc956f08c Mon Sep 17 00:00:00 2001 From: John Meeuwissen Date: Tue, 25 Mar 2025 14:45:49 +0100 Subject: [PATCH 4/4] #342 processed review comments --- src/domain/notification/getById/getById.ts | 2 +- src/domain/notification/getByPostId/getByPostId.ts | 2 +- .../{removedPost.spec.ts => removePostNotifications.spec.ts} | 3 +-- 3 files changed, 3 insertions(+), 4 deletions(-) rename test/domain/notification/{removedPost.spec.ts => removePostNotifications.spec.ts} (99%) diff --git a/src/domain/notification/getById/getById.ts b/src/domain/notification/getById/getById.ts index 55bb2837..4f4f52db 100644 --- a/src/domain/notification/getById/getById.ts +++ b/src/domain/notification/getById/getById.ts @@ -1,7 +1,7 @@ +import type { RecordQuery } from '^/integrations/database'; import database from '^/integrations/database'; -import type { RecordQuery } from '^/integrations/database'; import { RECORD_TYPE } from '../definitions'; import type { DataModel } from '../types'; diff --git a/src/domain/notification/getByPostId/getByPostId.ts b/src/domain/notification/getByPostId/getByPostId.ts index 31037dd7..7a4a1d2c 100644 --- a/src/domain/notification/getByPostId/getByPostId.ts +++ b/src/domain/notification/getByPostId/getByPostId.ts @@ -1,7 +1,7 @@ +import type { RecordQuery } from '^/integrations/database'; import database from '^/integrations/database'; -import type { RecordQuery } from '^/integrations/database'; import { RECORD_TYPE } from '../definitions'; import type { DataModel } from '../types'; diff --git a/test/domain/notification/removedPost.spec.ts b/test/domain/notification/removePostNotifications.spec.ts similarity index 99% rename from test/domain/notification/removedPost.spec.ts rename to test/domain/notification/removePostNotifications.spec.ts index 938a7a96..24eab02f 100644 --- a/test/domain/notification/removedPost.spec.ts +++ b/test/domain/notification/removePostNotifications.spec.ts @@ -3,6 +3,7 @@ import { beforeEach, describe, expect, it } from 'vitest'; import getByPostId from '^/domain/notification/getByPostId'; import removedPost from '^/domain/notification/notify/removedPost'; + import { DATABASES, VALUES } from './fixtures'; beforeEach(async () => @@ -17,10 +18,8 @@ describe('domain/notification/remove', () => it('should remove all notifications of a removed post', async () => { await removedPost(VALUES.IDS.POST_RATED); - const result = await getByPostId(VALUES.IDS.POST_RATED); expect(result).toHaveLength(0); }); - });