Skip to content

Commit

Permalink
feat: Apps Engine Bridge Method for Unread Room Messages (#754)
Browse files Browse the repository at this point in the history
  • Loading branch information
Dnouv authored Sep 27, 2024
1 parent e2e1dd4 commit 3e1da83
Show file tree
Hide file tree
Showing 8 changed files with 91 additions and 3 deletions.
20 changes: 20 additions & 0 deletions src/definition/accessors/IRoomRead.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,24 @@ export interface IRoomRead {
* @returns a list of the users with the leader role in the room
*/
getLeaders(roomId: string): Promise<Array<IUser>>;

/**
* Retrieves an array of unread messages for a specific user in a specific room.
*
* @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.
* - 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 'asc' for ascending order or 'desc' 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.
*/
getUnreadByUser(roomId: string, uid: string, options?: Partial<GetMessagesOptions>): Promise<IMessageRaw[]>;

/**
* Gets the user's unread messages count in a room.
* @param roomId room's id
* @param uid user's id
*/
getUserUnreadMessageCount(roomId: string, uid: string): Promise<number>;
}
22 changes: 22 additions & 0 deletions src/server/accessors/RoomRead.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,28 @@ export class RoomRead implements IRoomRead {
return this.roomBridge.doGetLeaders(roomId, this.appId);
}

public async getUnreadByUser(roomId: string, uid: string, options: Partial<GetMessagesOptions> = {}): Promise<IMessageRaw[]> {
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 (!Number.isFinite(limit) || limit <= 0 || limit > 100) {
throw new Error(`Invalid limit provided. Expected number between 1 and 100, got ${limit}`);
}

this.validateSort(sort);

const completeOptions: GetMessagesOptions = { limit, sort, skip };

return this.roomBridge.doGetUnreadByUser(roomId, uid, completeOptions, this.appId);
}

public getUserUnreadMessageCount(roomId: string, uid: string): Promise<number> {
return this.roomBridge.doGetUserUnreadMessageCount(roomId, uid, this.appId);
}

// If there are any invalid fields or values, throw
private validateSort(sort: Record<string, unknown>) {
Object.entries(sort).forEach(([key, value]) => {
Expand Down
16 changes: 16 additions & 0 deletions src/server/bridges/RoomBridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,18 @@ export abstract class RoomBridge extends BaseBridge {
}
}

public async doGetUnreadByUser(roomId: string, uid: string, options: GetMessagesOptions, appId: string): Promise<IMessageRaw[]> {
if (this.hasReadPermission(appId)) {
return this.getUnreadByUser(roomId, uid, options, appId);
}
}

public async doGetUserUnreadMessageCount(roomId: string, uid: string, appId: string): Promise<number> {
if (this.hasReadPermission(appId)) {
return this.getUserUnreadMessageCount(roomId, uid, appId);
}
}

protected abstract create(room: IRoom, members: Array<string>, appId: string): Promise<string>;

protected abstract getById(roomId: string, appId: string): Promise<IRoom>;
Expand Down Expand Up @@ -147,6 +159,10 @@ export abstract class RoomBridge extends BaseBridge {

protected abstract removeUsers(roomId: string, usernames: Array<string>, appId: string): Promise<void>;

protected abstract getUnreadByUser(roomId: string, uid: string, options: GetMessagesOptions, appId: string): Promise<IMessageRaw[]>;

protected abstract getUserUnreadMessageCount(roomId: string, uid: string, appId: string): Promise<number>;

private hasWritePermission(appId: string): boolean {
if (AppPermissionManager.hasPermission(appId, AppPermissions.room.write)) {
return true;
Expand Down
4 changes: 2 additions & 2 deletions src/server/bridges/UserBridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export abstract class UserBridge extends BaseBridge {

public async doGetUserUnreadMessageCount(uid: string, appId: string): Promise<number> {
if (this.hasReadPermission(appId)) {
return this.getUserUnreadMessageCount(uid);
return this.getUserUnreadMessageCount(uid, appId);
}
}

Expand All @@ -65,7 +65,7 @@ export abstract class UserBridge extends BaseBridge {

protected abstract getActiveUserCount(): Promise<number>;

protected abstract getUserUnreadMessageCount(uid: string): Promise<number>;
protected abstract getUserUnreadMessageCount(uid: string, appId: string): Promise<number>;

/**
* Creates a user.
Expand Down
1 change: 1 addition & 0 deletions tests/server/accessors/MessageRead.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export class MessageReadAccessorTestFixture {
this.msg = TestData.getMessage();

const theMsg = this.msg;

this.mockMsgBridgeWithMsg = {
doGetById(id, appId): Promise<IMessage> {
return Promise.resolve(theMsg);
Expand Down
21 changes: 21 additions & 0 deletions tests/server/accessors/RoomRead.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,27 @@ export class RoomReadAccessorTestFixture {

private messages: IMessageRaw[];

private unreadRoomId: string;

private unreadUserId: string;

private mockRoomBridgeWithRoom: RoomBridge;

@SetupFixture
public setupFixture() {
this.room = TestData.getRoom();
this.user = TestData.getUser();
this.messages = ['507f1f77bcf86cd799439011', '507f191e810c19729de860ea'].map((id) => TestData.getMessageRaw(id));
this.unreadRoomId = this.messages[0].roomId;
this.unreadUserId = this.messages[0].sender._id;

const theRoom = this.room;
const theUser = this.user;
const theMessages = this.messages;

const theUnreadMsg = this.messages;
const { unreadRoomId } = this;
const { unreadUserId } = this;
this.mockRoomBridgeWithRoom = {
doGetById(id, appId): Promise<IRoom> {
return Promise.resolve(theRoom);
Expand All @@ -47,6 +57,12 @@ export class RoomReadAccessorTestFixture {
doGetMessages(roomId, options, appId): Promise<IMessageRaw[]> {
return Promise.resolve(theMessages);
},
doGetUnreadByUser(roomId, uid, options, appId): Promise<IMessageRaw[]> {
if (roomId === unreadRoomId && uid === unreadUserId) {
return Promise.resolve(theUnreadMsg);
}
return Promise.resolve([]);
},
} as RoomBridge;
}

Expand All @@ -68,6 +84,11 @@ export class RoomReadAccessorTestFixture {
Expect(await rr.getDirectByUsernames([this.user.username])).toBe(this.room);
Expect(await rr.getMessages('testing')).toBeDefined();
Expect(await rr.getMessages('testing')).toBe(this.messages);
Expect(await rr.getUnreadByUser(this.unreadRoomId, this.unreadUserId)).toBeDefined();
Expect(await rr.getUnreadByUser(this.unreadRoomId, this.unreadUserId)).toEqual(this.messages);

Expect(await rr.getUnreadByUser('fake', 'fake')).toBeDefined();
Expect(await rr.getUnreadByUser('fake', 'fake')).toEqual([]);
}

@AsyncTest()
Expand Down
8 changes: 8 additions & 0 deletions tests/test-data/bridges/roomBridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,12 @@ export class TestsRoomBridge extends RoomBridge {
public removeUsers(roomId: string, usernames: string[], appId: string): Promise<void> {
throw new Error('Method not implemented');
}

public getUnreadByUser(roomId: string, uid: string, options: GetMessagesOptions, appId: string): Promise<IMessageRaw[]> {
throw new Error('Method not implemented.');
}

protected getUserUnreadMessageCount(roomId: string, uid: string, appId: string): Promise<number> {
throw new Error('Method not implemented.');
}
}
2 changes: 1 addition & 1 deletion tests/test-data/bridges/userBridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export class TestsUserBridge extends UserBridge {
throw new Error('Method not implemented');
}

protected getUserUnreadMessageCount(uid: string): Promise<number> {
protected getUserUnreadMessageCount(uid: string, appId: string): Promise<number> {
throw new Error('Method not implemented.');
}

Expand Down

0 comments on commit 3e1da83

Please sign in to comment.