From 71f5d128323d1749ae8ae684b18eca7ef5002f3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=83=87=E3=83=B4=E3=81=81=E3=82=93=E3=81=99?= Date: Wed, 10 Apr 2024 16:30:23 +0530 Subject: [PATCH 01/10] init draaft --- src/server/accessors/MessageRead.ts | 4 ++++ src/server/bridges/MessageBridge.ts | 8 ++++++++ tests/server/accessors/MessageRead.spec.ts | 4 ++++ 3 files changed, 16 insertions(+) diff --git a/src/server/accessors/MessageRead.ts b/src/server/accessors/MessageRead.ts index fc33eee13..5c78fcf4c 100644 --- a/src/server/accessors/MessageRead.ts +++ b/src/server/accessors/MessageRead.ts @@ -30,4 +30,8 @@ export class MessageRead implements IMessageRead { return msg.room; } + + public async getUnreadByRoomAndUser(rid: string, uid: string): Promise> { + return this.messageBridge.doGetUnreadByRoomAndUser(rid, uid, this.appId); + } } diff --git a/src/server/bridges/MessageBridge.ts b/src/server/bridges/MessageBridge.ts index ea3af1437..9b20dea26 100644 --- a/src/server/bridges/MessageBridge.ts +++ b/src/server/bridges/MessageBridge.ts @@ -54,6 +54,12 @@ export abstract class MessageBridge extends BaseBridge { } } + public async doGetUnreadByRoomAndUser(rid: string, uid: string, appId: string): Promise> { + if (this.hasReadPermission(appId)) { + return this.getUnreadByRoomAndUser(rid, uid, appId); + } + } + protected abstract create(message: IMessage, appId: string): Promise; protected abstract update(message: IMessage, appId: string): Promise; @@ -68,6 +74,8 @@ export abstract class MessageBridge extends BaseBridge { protected abstract delete(message: IMessage, user: IUser, appId: string): Promise; + protected abstract getUnreadByRoomAndUser(rid: string, uid: string, appId: string): Promise>; + private hasReadPermission(appId: string): boolean { if (AppPermissionManager.hasPermission(appId, AppPermissions.message.read)) { return true; diff --git a/tests/server/accessors/MessageRead.spec.ts b/tests/server/accessors/MessageRead.spec.ts index 144926c71..7c87faed5 100644 --- a/tests/server/accessors/MessageRead.spec.ts +++ b/tests/server/accessors/MessageRead.spec.ts @@ -44,6 +44,9 @@ export class MessageReadAccessorTestFixture { Expect(await mr.getRoom('fake')).toBeDefined(); Expect(await mr.getRoom('fake')).toEqual(this.msg.room); + + Expect(await mr.getUnreadByRoomAndUser('fake', 'fake')).toBeDefined(); + Expect(await mr.getUnreadByRoomAndUser('fake', 'fake')).toEqual([this.msg]); } @AsyncTest() @@ -54,5 +57,6 @@ export class MessageReadAccessorTestFixture { Expect(await nomr.getById('fake')).not.toBeDefined(); Expect(await nomr.getSenderUser('fake')).not.toBeDefined(); Expect(await nomr.getRoom('fake')).not.toBeDefined(); + Expect(await nomr.getUnreadByRoomAndUser('fake', 'fake')).not.toBeDefined(); } } From d7730c0fd9bd92420c9c03a60a642541630ccc12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=83=87=E3=83=B4=E3=81=81=E3=82=93=E3=81=99?= Date: Fri, 12 Apr 2024 16:31:45 +0530 Subject: [PATCH 02/10] add method for read unread --- src/definition/accessors/IMessageRead.ts | 21 +++++++++++++++++++++ src/server/accessors/MessageRead.ts | 12 ++++++++++-- src/server/bridges/MessageBridge.ts | 24 +++++++++++++++++++++--- 3 files changed, 52 insertions(+), 5 deletions(-) diff --git a/src/definition/accessors/IMessageRead.ts b/src/definition/accessors/IMessageRead.ts index 10c99d263..96c0365d4 100644 --- a/src/definition/accessors/IMessageRead.ts +++ b/src/definition/accessors/IMessageRead.ts @@ -12,4 +12,25 @@ export interface IMessageRead { getSenderUser(messageId: string): Promise; getRoom(messageId: string): Promise; + + /** + * Retrieves an array of unread messages for a specific user in a specific room. + * + * @param rid The unique identifier of the room from which to retrieve unread messages. + * @param uid The unique identifier of the user for whom to retrieve unread messages. + * @param options Optional parameters for retrieving messages: + * - limit: The maximum number of messages to retrieve. If more than 100 is passed, it defaults to 100. + * - skip: The number of messages to skip (for pagination). + * - sort: An object defining the sorting order of the messages. Each key is a field to sort by, and the value is either 1 for ascending order or -1 for descending order. + * @returns A Promise that resolves to an array of IMessage objects representing the unread messages for the specified user in the specified room. + */ + getUnreadByRoomAndUser( + rid: string, + uid: string, + options?: Partial<{ + limit: number; + skip: number; + sort: Record; + }>, + ): Promise; } diff --git a/src/server/accessors/MessageRead.ts b/src/server/accessors/MessageRead.ts index 5c78fcf4c..a0ddb7d23 100644 --- a/src/server/accessors/MessageRead.ts +++ b/src/server/accessors/MessageRead.ts @@ -31,7 +31,15 @@ export class MessageRead implements IMessageRead { return msg.room; } - public async getUnreadByRoomAndUser(rid: string, uid: string): Promise> { - return this.messageBridge.doGetUnreadByRoomAndUser(rid, uid, this.appId); + public async getUnreadByRoomAndUser( + rid: string, + uid: string, + options?: Partial<{ + limit: number; + skip: number; + sort: Record; + }>, + ): Promise { + return this.messageBridge.doGetUnreadByRoomAndUser(rid, uid, this.appId, options); } } diff --git a/src/server/bridges/MessageBridge.ts b/src/server/bridges/MessageBridge.ts index 9b20dea26..638f760d7 100644 --- a/src/server/bridges/MessageBridge.ts +++ b/src/server/bridges/MessageBridge.ts @@ -54,9 +54,18 @@ export abstract class MessageBridge extends BaseBridge { } } - public async doGetUnreadByRoomAndUser(rid: string, uid: string, appId: string): Promise> { + public async doGetUnreadByRoomAndUser( + rid: string, + uid: string, + appId: string, + options?: Partial<{ + limit: number; + skip: number; + sort: Record; + }>, + ): Promise { if (this.hasReadPermission(appId)) { - return this.getUnreadByRoomAndUser(rid, uid, appId); + return this.getUnreadByRoomAndUser(rid, uid, appId, options); } } @@ -74,7 +83,16 @@ export abstract class MessageBridge extends BaseBridge { protected abstract delete(message: IMessage, user: IUser, appId: string): Promise; - protected abstract getUnreadByRoomAndUser(rid: string, uid: string, appId: string): Promise>; + protected abstract getUnreadByRoomAndUser( + rid: string, + uid: string, + appId: string, + options?: Partial<{ + limit: number; + skip: number; + sort: Record; + }>, + ): Promise; private hasReadPermission(appId: string): boolean { if (AppPermissionManager.hasPermission(appId, AppPermissions.message.read)) { From 5f7c769d797449c32bfd90ba394ba8fcd574ef79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=83=87=E3=83=B4=E3=81=81=E3=82=93=E3=81=99?= Date: Fri, 12 Apr 2024 17:00:49 +0530 Subject: [PATCH 03/10] add unreadByRoomAndUser test --- tests/server/accessors/MessageRead.spec.ts | 26 +++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/tests/server/accessors/MessageRead.spec.ts b/tests/server/accessors/MessageRead.spec.ts index 7c87faed5..26f262fc0 100644 --- a/tests/server/accessors/MessageRead.spec.ts +++ b/tests/server/accessors/MessageRead.spec.ts @@ -8,6 +8,12 @@ import { TestData } from '../../test-data/utilities'; export class MessageReadAccessorTestFixture { private msg: IMessage; + private unreadMsgs: IMessage[]; + + private unreadRoomId: string; + + private unreadUserId: string; + private mockMsgBridgeWithMsg: MessageBridge; private mockMsgBridgeNoMsg: MessageBridge; @@ -15,18 +21,33 @@ export class MessageReadAccessorTestFixture { @SetupFixture public setupFixture() { this.msg = TestData.getMessage(); + this.unreadMsgs = ['507f1f77bcf86cd799439011', '507f191e810c19729de860ea'].map((id) => TestData.getMessage(id)); + this.unreadRoomId = this.unreadMsgs[0].room.id; + this.unreadUserId = this.unreadMsgs[0].sender.id; const theMsg = this.msg; + const theUnreadMsg = this.unreadMsgs; + const { unreadRoomId } = this; + const { unreadUserId } = this; this.mockMsgBridgeWithMsg = { doGetById(id, appId): Promise { return Promise.resolve(theMsg); }, + doGetUnreadByRoomAndUser(rid, uid, appId, options): Promise { + if (rid === unreadRoomId && uid === unreadUserId) { + return Promise.resolve(theUnreadMsg); + } + return Promise.resolve([]); + }, } as MessageBridge; this.mockMsgBridgeNoMsg = { doGetById(id, appId): Promise { return Promise.resolve(undefined); }, + doGetUnreadByRoomAndUser(rid, uid, appId, options): Promise { + return Promise.resolve(undefined); + }, } as MessageBridge; } @@ -45,8 +66,11 @@ export class MessageReadAccessorTestFixture { Expect(await mr.getRoom('fake')).toBeDefined(); Expect(await mr.getRoom('fake')).toEqual(this.msg.room); + Expect(await mr.getUnreadByRoomAndUser(this.unreadRoomId, this.unreadUserId)).toBeDefined(); + Expect(await mr.getUnreadByRoomAndUser(this.unreadRoomId, this.unreadUserId)).toEqual(this.unreadMsgs); + Expect(await mr.getUnreadByRoomAndUser('fake', 'fake')).toBeDefined(); - Expect(await mr.getUnreadByRoomAndUser('fake', 'fake')).toEqual([this.msg]); + Expect(await mr.getUnreadByRoomAndUser('fake', 'fake')).toEqual([]); } @AsyncTest() From c6cac9bcdd0ed7ea710b76b44b8867f7d29e6101 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=83=87=E3=83=B4=E3=81=81=E3=82=93=E3=81=99?= Date: Fri, 12 Apr 2024 17:09:58 +0530 Subject: [PATCH 04/10] add missing test --- tests/test-data/bridges/messageBridge.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/test-data/bridges/messageBridge.ts b/tests/test-data/bridges/messageBridge.ts index f6f37755e..e782637c3 100644 --- a/tests/test-data/bridges/messageBridge.ts +++ b/tests/test-data/bridges/messageBridge.ts @@ -13,6 +13,15 @@ export class TestsMessageBridge extends MessageBridge { throw new Error('Method not implemented.'); } + public getUnreadByRoomAndUser( + rid: string, + uid: string, + appId: string, + options?: Partial<{ limit: number; skip: number; sort: Record }>, + ): Promise { + throw new Error('Method not implemented.'); + } + public update(message: IMessage, appId: string): Promise { throw new Error('Method not implemented.'); } From 46bf341e4c1709e9e7ee1ab9ad766d1c0bd6f4ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=83=87=E3=83=B4=E3=81=81=E3=82=93=E3=81=99?= Date: Mon, 15 Apr 2024 18:22:08 +0530 Subject: [PATCH 05/10] refactor: rename parameter rid to roomId in IMessageRead interface and its implementations --- src/definition/accessors/IMessageRead.ts | 4 ++-- src/server/accessors/MessageRead.ts | 4 ++-- src/server/bridges/MessageBridge.ts | 6 +++--- tests/server/accessors/MessageRead.spec.ts | 6 +++--- tests/test-data/bridges/messageBridge.ts | 2 +- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/definition/accessors/IMessageRead.ts b/src/definition/accessors/IMessageRead.ts index 96c0365d4..e871f787a 100644 --- a/src/definition/accessors/IMessageRead.ts +++ b/src/definition/accessors/IMessageRead.ts @@ -16,7 +16,7 @@ export interface IMessageRead { /** * Retrieves an array of unread messages for a specific user in a specific room. * - * @param rid The unique identifier of the room from which to retrieve unread messages. + * @param roomId The unique identifier of the room from which to retrieve unread messages. * @param uid The unique identifier of the user for whom to retrieve unread messages. * @param options Optional parameters for retrieving messages: * - limit: The maximum number of messages to retrieve. If more than 100 is passed, it defaults to 100. @@ -25,7 +25,7 @@ export interface IMessageRead { * @returns A Promise that resolves to an array of IMessage objects representing the unread messages for the specified user in the specified room. */ getUnreadByRoomAndUser( - rid: string, + roomId: string, uid: string, options?: Partial<{ limit: number; diff --git a/src/server/accessors/MessageRead.ts b/src/server/accessors/MessageRead.ts index a0ddb7d23..b02beaf02 100644 --- a/src/server/accessors/MessageRead.ts +++ b/src/server/accessors/MessageRead.ts @@ -32,7 +32,7 @@ export class MessageRead implements IMessageRead { } public async getUnreadByRoomAndUser( - rid: string, + roomId: string, uid: string, options?: Partial<{ limit: number; @@ -40,6 +40,6 @@ export class MessageRead implements IMessageRead { sort: Record; }>, ): Promise { - return this.messageBridge.doGetUnreadByRoomAndUser(rid, uid, this.appId, options); + return this.messageBridge.doGetUnreadByRoomAndUser(roomId, uid, this.appId, options); } } diff --git a/src/server/bridges/MessageBridge.ts b/src/server/bridges/MessageBridge.ts index 638f760d7..9caf21e62 100644 --- a/src/server/bridges/MessageBridge.ts +++ b/src/server/bridges/MessageBridge.ts @@ -55,7 +55,7 @@ export abstract class MessageBridge extends BaseBridge { } public async doGetUnreadByRoomAndUser( - rid: string, + roomId: string, uid: string, appId: string, options?: Partial<{ @@ -65,7 +65,7 @@ export abstract class MessageBridge extends BaseBridge { }>, ): Promise { if (this.hasReadPermission(appId)) { - return this.getUnreadByRoomAndUser(rid, uid, appId, options); + return this.getUnreadByRoomAndUser(roomId, uid, appId, options); } } @@ -84,7 +84,7 @@ export abstract class MessageBridge extends BaseBridge { protected abstract delete(message: IMessage, user: IUser, appId: string): Promise; protected abstract getUnreadByRoomAndUser( - rid: string, + roomId: string, uid: string, appId: string, options?: Partial<{ diff --git a/tests/server/accessors/MessageRead.spec.ts b/tests/server/accessors/MessageRead.spec.ts index 26f262fc0..3f2f36ab9 100644 --- a/tests/server/accessors/MessageRead.spec.ts +++ b/tests/server/accessors/MessageRead.spec.ts @@ -33,8 +33,8 @@ export class MessageReadAccessorTestFixture { doGetById(id, appId): Promise { return Promise.resolve(theMsg); }, - doGetUnreadByRoomAndUser(rid, uid, appId, options): Promise { - if (rid === unreadRoomId && uid === unreadUserId) { + doGetUnreadByRoomAndUser(roomId, uid, appId, options): Promise { + if (roomId === unreadRoomId && uid === unreadUserId) { return Promise.resolve(theUnreadMsg); } return Promise.resolve([]); @@ -45,7 +45,7 @@ export class MessageReadAccessorTestFixture { doGetById(id, appId): Promise { return Promise.resolve(undefined); }, - doGetUnreadByRoomAndUser(rid, uid, appId, options): Promise { + doGetUnreadByRoomAndUser(roomId, uid, appId, options): Promise { return Promise.resolve(undefined); }, } as MessageBridge; diff --git a/tests/test-data/bridges/messageBridge.ts b/tests/test-data/bridges/messageBridge.ts index e782637c3..f42f8d31f 100644 --- a/tests/test-data/bridges/messageBridge.ts +++ b/tests/test-data/bridges/messageBridge.ts @@ -14,7 +14,7 @@ export class TestsMessageBridge extends MessageBridge { } public getUnreadByRoomAndUser( - rid: string, + roomId: string, uid: string, appId: string, options?: Partial<{ limit: number; skip: number; sort: Record }>, From d0d660833e27656c6d99f58495e356364f1c1d98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=83=87=E3=83=B4=E3=81=81=E3=82=93=E3=81=99?= Date: Mon, 15 Apr 2024 18:29:21 +0530 Subject: [PATCH 06/10] add: getUserUnreadMessageCountByRoom to get count of unread messages for a user in any room --- src/definition/accessors/IUserRead.ts | 7 +++++++ src/server/accessors/UserRead.ts | 4 ++++ src/server/bridges/UserBridge.ts | 12 ++++++++++-- tests/test-data/bridges/userBridge.ts | 6 +++++- 4 files changed, 26 insertions(+), 3 deletions(-) diff --git a/src/definition/accessors/IUserRead.ts b/src/definition/accessors/IUserRead.ts index 33c4c6e45..c99c65923 100644 --- a/src/definition/accessors/IUserRead.ts +++ b/src/definition/accessors/IUserRead.ts @@ -19,4 +19,11 @@ export interface IUserRead { * @param uid user's id */ getUserUnreadMessageCount(uid: string): Promise; + + /** + * Gets the user's unread messages count in a room. + * @param uid user's id + * @param roomId room's id + */ + getUserUnreadMessageCountByRoom(uid: string, roomId: string): Promise; } diff --git a/src/server/accessors/UserRead.ts b/src/server/accessors/UserRead.ts index 4eeb38cc7..5112046ea 100644 --- a/src/server/accessors/UserRead.ts +++ b/src/server/accessors/UserRead.ts @@ -20,4 +20,8 @@ export class UserRead implements IUserRead { public getUserUnreadMessageCount(uid: string): Promise { return this.userBridge.doGetUserUnreadMessageCount(uid, this.appId); } + + public getUserUnreadMessageCountByRoom(uid: string, rid: string): Promise { + return this.userBridge.doGetUserUnreadMessageCountByRoom(uid, rid, this.appId); + } } diff --git a/src/server/bridges/UserBridge.ts b/src/server/bridges/UserBridge.ts index 232f66507..59f4e12d4 100644 --- a/src/server/bridges/UserBridge.ts +++ b/src/server/bridges/UserBridge.ts @@ -41,7 +41,13 @@ export abstract class UserBridge extends BaseBridge { public async doGetUserUnreadMessageCount(uid: string, appId: string): Promise { if (this.hasReadPermission(appId)) { - return this.getUserUnreadMessageCount(uid); + return this.getUserUnreadMessageCount(uid, appId); + } + } + + public async doGetUserUnreadMessageCountByRoom(uid: string, roomId: string, appId: string): Promise { + if (this.hasReadPermission(appId)) { + return this.getUserUnreadMessageCountByRoom(uid, roomId, appId); } } @@ -65,7 +71,9 @@ export abstract class UserBridge extends BaseBridge { protected abstract getActiveUserCount(): Promise; - protected abstract getUserUnreadMessageCount(uid: string): Promise; + protected abstract getUserUnreadMessageCount(uid: string, appId: string): Promise; + + protected abstract getUserUnreadMessageCountByRoom(uid: string, roomId: string, appId: string): Promise; /** * Creates a user. diff --git a/tests/test-data/bridges/userBridge.ts b/tests/test-data/bridges/userBridge.ts index c9399f5b6..a8cbca41d 100644 --- a/tests/test-data/bridges/userBridge.ts +++ b/tests/test-data/bridges/userBridge.ts @@ -34,7 +34,11 @@ export class TestsUserBridge extends UserBridge { throw new Error('Method not implemented'); } - protected getUserUnreadMessageCount(uid: string): Promise { + protected getUserUnreadMessageCount(uid: string, appId: string): Promise { + throw new Error('Method not implemented.'); + } + + protected getUserUnreadMessageCountByRoom(uid: string, roomId: string, appId: string): Promise { throw new Error('Method not implemented.'); } From 551dd9f2567dd50867f16486f976a620bcbed586 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=83=87=E3=83=B4=E3=81=81=E3=82=93=E3=81=99?= Date: Tue, 16 Apr 2024 20:38:59 +0530 Subject: [PATCH 07/10] change options param order --- src/server/accessors/MessageRead.ts | 2 +- src/server/bridges/MessageBridge.ts | 22 +++++++++++----------- tests/server/accessors/MessageRead.spec.ts | 4 ++-- tests/test-data/bridges/messageBridge.ts | 2 +- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/server/accessors/MessageRead.ts b/src/server/accessors/MessageRead.ts index b02beaf02..731d68dc0 100644 --- a/src/server/accessors/MessageRead.ts +++ b/src/server/accessors/MessageRead.ts @@ -40,6 +40,6 @@ export class MessageRead implements IMessageRead { sort: Record; }>, ): Promise { - return this.messageBridge.doGetUnreadByRoomAndUser(roomId, uid, this.appId, options); + return this.messageBridge.doGetUnreadByRoomAndUser(roomId, uid, { limit: 100, ...options }, this.appId); } } diff --git a/src/server/bridges/MessageBridge.ts b/src/server/bridges/MessageBridge.ts index 9caf21e62..de2749fc0 100644 --- a/src/server/bridges/MessageBridge.ts +++ b/src/server/bridges/MessageBridge.ts @@ -57,15 +57,15 @@ export abstract class MessageBridge extends BaseBridge { public async doGetUnreadByRoomAndUser( roomId: string, uid: string, - appId: string, - options?: Partial<{ + options: { limit: number; - skip: number; - sort: Record; - }>, + skip?: number; + sort?: Record; + }, + appId: string, ): Promise { if (this.hasReadPermission(appId)) { - return this.getUnreadByRoomAndUser(roomId, uid, appId, options); + return this.getUnreadByRoomAndUser(roomId, uid, options, appId); } } @@ -86,12 +86,12 @@ export abstract class MessageBridge extends BaseBridge { protected abstract getUnreadByRoomAndUser( roomId: string, uid: string, - appId: string, - options?: Partial<{ + options: { limit: number; - skip: number; - sort: Record; - }>, + skip?: number; + sort?: Record; + }, + appId: string, ): Promise; private hasReadPermission(appId: string): boolean { diff --git a/tests/server/accessors/MessageRead.spec.ts b/tests/server/accessors/MessageRead.spec.ts index 3f2f36ab9..79e9d839e 100644 --- a/tests/server/accessors/MessageRead.spec.ts +++ b/tests/server/accessors/MessageRead.spec.ts @@ -33,7 +33,7 @@ export class MessageReadAccessorTestFixture { doGetById(id, appId): Promise { return Promise.resolve(theMsg); }, - doGetUnreadByRoomAndUser(roomId, uid, appId, options): Promise { + doGetUnreadByRoomAndUser(roomId, uid, options, appId): Promise { if (roomId === unreadRoomId && uid === unreadUserId) { return Promise.resolve(theUnreadMsg); } @@ -45,7 +45,7 @@ export class MessageReadAccessorTestFixture { doGetById(id, appId): Promise { return Promise.resolve(undefined); }, - doGetUnreadByRoomAndUser(roomId, uid, appId, options): Promise { + doGetUnreadByRoomAndUser(roomId, uid, options, appId): Promise { return Promise.resolve(undefined); }, } as MessageBridge; diff --git a/tests/test-data/bridges/messageBridge.ts b/tests/test-data/bridges/messageBridge.ts index f42f8d31f..32d3b9be7 100644 --- a/tests/test-data/bridges/messageBridge.ts +++ b/tests/test-data/bridges/messageBridge.ts @@ -16,8 +16,8 @@ export class TestsMessageBridge extends MessageBridge { public getUnreadByRoomAndUser( roomId: string, uid: string, + options: { limit: number; skip?: number; sort?: Record }, appId: string, - options?: Partial<{ limit: number; skip: number; sort: Record }>, ): Promise { throw new Error('Method not implemented.'); } From 9b9fd64eaf44a83367463597cc5d1a3bb0f1f7db Mon Sep 17 00:00:00 2001 From: Dnouv Date: Fri, 20 Sep 2024 16:32:45 +0530 Subject: [PATCH 08/10] IMessages <-> IMessageRaw and raw options to GetMessageOptions --- src/definition/accessors/IMessageRead.ts | 13 +++-------- src/server/accessors/MessageRead.ts | 13 +++-------- src/server/bridges/MessageBridge.ts | 25 ++++------------------ tests/server/accessors/MessageRead.spec.ts | 14 ++++++------ tests/test-data/bridges/messageBridge.ts | 10 +++------ 5 files changed, 20 insertions(+), 55 deletions(-) diff --git a/src/definition/accessors/IMessageRead.ts b/src/definition/accessors/IMessageRead.ts index e871f787a..4502689d2 100644 --- a/src/definition/accessors/IMessageRead.ts +++ b/src/definition/accessors/IMessageRead.ts @@ -1,4 +1,5 @@ -import type { IMessage } from '../messages/index'; +import type { GetMessagesOptions } from '../../server/bridges/RoomBridge'; +import type { IMessage, IMessageRaw } from '../messages/index'; import type { IRoom } from '../rooms/IRoom'; import type { IUser } from '../users/IUser'; @@ -24,13 +25,5 @@ export interface IMessageRead { * - sort: An object defining the sorting order of the messages. Each key is a field to sort by, and the value is either 1 for ascending order or -1 for descending order. * @returns A Promise that resolves to an array of IMessage objects representing the unread messages for the specified user in the specified room. */ - getUnreadByRoomAndUser( - roomId: string, - uid: string, - options?: Partial<{ - limit: number; - skip: number; - sort: Record; - }>, - ): Promise; + getUnreadByRoomAndUser(roomId: string, uid: string, options?: Partial): Promise; } diff --git a/src/server/accessors/MessageRead.ts b/src/server/accessors/MessageRead.ts index 731d68dc0..5c0b81a04 100644 --- a/src/server/accessors/MessageRead.ts +++ b/src/server/accessors/MessageRead.ts @@ -1,8 +1,9 @@ import type { MessageBridge } from '../bridges/MessageBridge'; import type { IMessageRead } from '../../definition/accessors'; -import type { IMessage } from '../../definition/messages'; +import type { IMessage, IMessageRaw } from '../../definition/messages'; import type { IRoom } from '../../definition/rooms'; import type { IUser } from '../../definition/users'; +import type { GetMessagesOptions } from '../bridges/RoomBridge'; export class MessageRead implements IMessageRead { constructor(private messageBridge: MessageBridge, private appId: string) {} @@ -31,15 +32,7 @@ export class MessageRead implements IMessageRead { return msg.room; } - public async getUnreadByRoomAndUser( - roomId: string, - uid: string, - options?: Partial<{ - limit: number; - skip: number; - sort: Record; - }>, - ): Promise { + public async getUnreadByRoomAndUser(roomId: string, uid: string, options?: Partial): Promise { return this.messageBridge.doGetUnreadByRoomAndUser(roomId, uid, { limit: 100, ...options }, this.appId); } } diff --git a/src/server/bridges/MessageBridge.ts b/src/server/bridges/MessageBridge.ts index 380a1ffdb..19ae426ca 100644 --- a/src/server/bridges/MessageBridge.ts +++ b/src/server/bridges/MessageBridge.ts @@ -1,11 +1,12 @@ import type { ITypingOptions } from '../../definition/accessors/INotifier'; -import type { IMessage, Reaction } from '../../definition/messages'; +import type { IMessage, IMessageRaw, Reaction } from '../../definition/messages'; import type { IRoom } from '../../definition/rooms'; import type { IUser } from '../../definition/users'; import { PermissionDeniedError } from '../errors/PermissionDeniedError'; import { AppPermissionManager } from '../managers/AppPermissionManager'; import { AppPermissions } from '../permissions/AppPermissions'; import { BaseBridge } from './BaseBridge'; +import type { GetMessagesOptions } from './RoomBridge'; export interface ITypingDescriptor extends ITypingOptions { isTyping: boolean; @@ -54,16 +55,7 @@ export abstract class MessageBridge extends BaseBridge { } } - public async doGetUnreadByRoomAndUser( - roomId: string, - uid: string, - options: { - limit: number; - skip?: number; - sort?: Record; - }, - appId: string, - ): Promise { + public async doGetUnreadByRoomAndUser(roomId: string, uid: string, options: GetMessagesOptions, appId: string): Promise { if (this.hasReadPermission(appId)) { return this.getUnreadByRoomAndUser(roomId, uid, options, appId); } @@ -95,16 +87,7 @@ export abstract class MessageBridge extends BaseBridge { protected abstract delete(message: IMessage, user: IUser, appId: string): Promise; - protected abstract getUnreadByRoomAndUser( - roomId: string, - uid: string, - options: { - limit: number; - skip?: number; - sort?: Record; - }, - appId: string, - ): Promise; + protected abstract getUnreadByRoomAndUser(roomId: string, uid: string, options: GetMessagesOptions, appId: string): Promise; protected abstract addReaction(messageId: string, userId: string, reaction: Reaction): Promise; diff --git a/tests/server/accessors/MessageRead.spec.ts b/tests/server/accessors/MessageRead.spec.ts index 79e9d839e..aeac24a02 100644 --- a/tests/server/accessors/MessageRead.spec.ts +++ b/tests/server/accessors/MessageRead.spec.ts @@ -1,6 +1,6 @@ import { AsyncTest, Expect, SetupFixture } from 'alsatian'; -import type { IMessage } from '../../../src/definition/messages'; +import type { IMessage, IMessageRaw } from '../../../src/definition/messages'; import { MessageRead } from '../../../src/server/accessors'; import type { MessageBridge } from '../../../src/server/bridges'; import { TestData } from '../../test-data/utilities'; @@ -8,7 +8,7 @@ import { TestData } from '../../test-data/utilities'; export class MessageReadAccessorTestFixture { private msg: IMessage; - private unreadMsgs: IMessage[]; + private unreadMsgs: IMessageRaw[]; private unreadRoomId: string; @@ -21,9 +21,9 @@ export class MessageReadAccessorTestFixture { @SetupFixture public setupFixture() { this.msg = TestData.getMessage(); - this.unreadMsgs = ['507f1f77bcf86cd799439011', '507f191e810c19729de860ea'].map((id) => TestData.getMessage(id)); - this.unreadRoomId = this.unreadMsgs[0].room.id; - this.unreadUserId = this.unreadMsgs[0].sender.id; + this.unreadMsgs = ['507f1f77bcf86cd799439011', '507f191e810c19729de860ea'].map((id) => TestData.getMessageRaw(id)); + this.unreadRoomId = this.unreadMsgs[0].roomId; + this.unreadUserId = this.unreadMsgs[0].sender._id; const theMsg = this.msg; const theUnreadMsg = this.unreadMsgs; @@ -33,7 +33,7 @@ export class MessageReadAccessorTestFixture { doGetById(id, appId): Promise { return Promise.resolve(theMsg); }, - doGetUnreadByRoomAndUser(roomId, uid, options, appId): Promise { + doGetUnreadByRoomAndUser(roomId, uid, options, appId): Promise { if (roomId === unreadRoomId && uid === unreadUserId) { return Promise.resolve(theUnreadMsg); } @@ -45,7 +45,7 @@ export class MessageReadAccessorTestFixture { doGetById(id, appId): Promise { return Promise.resolve(undefined); }, - doGetUnreadByRoomAndUser(roomId, uid, options, appId): Promise { + doGetUnreadByRoomAndUser(roomId, uid, options, appId): Promise { return Promise.resolve(undefined); }, } as MessageBridge; diff --git a/tests/test-data/bridges/messageBridge.ts b/tests/test-data/bridges/messageBridge.ts index ea90b979c..e5929bac6 100644 --- a/tests/test-data/bridges/messageBridge.ts +++ b/tests/test-data/bridges/messageBridge.ts @@ -1,8 +1,9 @@ -import type { IMessage, Reaction } from '../../../src/definition/messages'; +import type { IMessage, IMessageRaw, Reaction } from '../../../src/definition/messages'; import type { IRoom } from '../../../src/definition/rooms'; import type { IUser } from '../../../src/definition/users'; import { MessageBridge } from '../../../src/server/bridges'; import type { ITypingDescriptor } from '../../../src/server/bridges/MessageBridge'; +import type { GetMessagesOptions } from '../../../src/server/bridges/RoomBridge'; export class TestsMessageBridge extends MessageBridge { public create(message: IMessage, appId: string): Promise { @@ -13,12 +14,7 @@ export class TestsMessageBridge extends MessageBridge { throw new Error('Method not implemented.'); } - public getUnreadByRoomAndUser( - roomId: string, - uid: string, - options: { limit: number; skip?: number; sort?: Record }, - appId: string, - ): Promise { + public getUnreadByRoomAndUser(roomId: string, uid: string, options: GetMessagesOptions, appId: string): Promise { throw new Error('Method not implemented.'); } From 104452353e77ab8dbaab2e956ab79c414da2b1c1 Mon Sep 17 00:00:00 2001 From: Dnouv Date: Fri, 20 Sep 2024 16:40:00 +0530 Subject: [PATCH 09/10] add options checks and validations --- src/server/accessors/MessageRead.ts | 34 +++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/src/server/accessors/MessageRead.ts b/src/server/accessors/MessageRead.ts index 5c0b81a04..2e71c752b 100644 --- a/src/server/accessors/MessageRead.ts +++ b/src/server/accessors/MessageRead.ts @@ -3,7 +3,7 @@ import type { IMessageRead } from '../../definition/accessors'; import type { IMessage, IMessageRaw } from '../../definition/messages'; import type { IRoom } from '../../definition/rooms'; import type { IUser } from '../../definition/users'; -import type { GetMessagesOptions } from '../bridges/RoomBridge'; +import { GetMessagesSortableFields, type GetMessagesOptions } from '../bridges/RoomBridge'; export class MessageRead implements IMessageRead { constructor(private messageBridge: MessageBridge, private appId: string) {} @@ -33,6 +33,36 @@ export class MessageRead implements IMessageRead { } public async getUnreadByRoomAndUser(roomId: string, uid: string, options?: Partial): Promise { - return this.messageBridge.doGetUnreadByRoomAndUser(roomId, uid, { limit: 100, ...options }, this.appId); + if (typeof options.limit !== 'undefined' && (!Number.isFinite(options.limit) || options.limit > 100)) { + throw new Error(`Invalid limit provided. Expected number <= 100, got ${options.limit}`); + } + + if (typeof roomId !== 'string' || roomId.trim().length === 0) { + throw new Error('Invalid roomId: must be a non-empty string'); + } + + if (options.sort) { + this.validateSort(options.sort); + } + + const completeOptions: GetMessagesOptions = { + limit: options.limit ?? 100, + sort: options.sort ?? { createdAt: 'asc' }, + skip: options.skip ?? 0, + }; + + return this.messageBridge.doGetUnreadByRoomAndUser(roomId, uid, completeOptions, this.appId); + } + + private validateSort(sort: Record) { + Object.entries(sort).forEach(([key, value]) => { + if (!GetMessagesSortableFields.includes(key as typeof GetMessagesSortableFields[number])) { + throw new Error(`Invalid key "${key}" used in sort. Available keys for sorting are ${GetMessagesSortableFields.join(', ')}`); + } + + if (value !== 'asc' && value !== 'desc') { + throw new Error(`Invalid sort direction for field "${key}". Expected "asc" or "desc", got ${value}`); + } + }); } } From 6c1b1b726d3138ca8157d9c07704ccb4947567ad Mon Sep 17 00:00:00 2001 From: Dnouv Date: Fri, 20 Sep 2024 17:07:12 +0530 Subject: [PATCH 10/10] remove ?'s --- src/server/accessors/MessageRead.ts | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/src/server/accessors/MessageRead.ts b/src/server/accessors/MessageRead.ts index 2e71c752b..b437ce187 100644 --- a/src/server/accessors/MessageRead.ts +++ b/src/server/accessors/MessageRead.ts @@ -32,24 +32,20 @@ export class MessageRead implements IMessageRead { return msg.room; } - public async getUnreadByRoomAndUser(roomId: string, uid: string, options?: Partial): Promise { - if (typeof options.limit !== 'undefined' && (!Number.isFinite(options.limit) || options.limit > 100)) { - throw new Error(`Invalid limit provided. Expected number <= 100, got ${options.limit}`); - } + public async getUnreadByRoomAndUser(roomId: string, uid: string, options: Partial = {}): Promise { + const { limit = 100, sort = { createdAt: 'asc' }, skip = 0 } = options; if (typeof roomId !== 'string' || roomId.trim().length === 0) { throw new Error('Invalid roomId: must be a non-empty string'); } - if (options.sort) { - this.validateSort(options.sort); + if (!Number.isFinite(limit) || limit <= 0 || limit > 100) { + throw new Error(`Invalid limit provided. Expected number between 1 and 100, got ${limit}`); } - const completeOptions: GetMessagesOptions = { - limit: options.limit ?? 100, - sort: options.sort ?? { createdAt: 'asc' }, - skip: options.skip ?? 0, - }; + this.validateSort(sort); + + const completeOptions: GetMessagesOptions = { limit, sort, skip }; return this.messageBridge.doGetUnreadByRoomAndUser(roomId, uid, completeOptions, this.appId); }