From 490de6e2f53cff925212b17b1b168a0c7569d6cd Mon Sep 17 00:00:00 2001 From: Douglas Gubert Date: Mon, 3 Feb 2025 12:01:32 -0300 Subject: [PATCH 01/16] wip --- .../deno-runtime/lib/accessors/builders/RoomBuilder.ts | 10 ++++++++++ .../src/definition/accessors/IRoomBuilder.ts | 7 +++++++ 2 files changed, 17 insertions(+) diff --git a/packages/apps-engine/deno-runtime/lib/accessors/builders/RoomBuilder.ts b/packages/apps-engine/deno-runtime/lib/accessors/builders/RoomBuilder.ts index 38983475162d1..67186a907f2fa 100644 --- a/packages/apps-engine/deno-runtime/lib/accessors/builders/RoomBuilder.ts +++ b/packages/apps-engine/deno-runtime/lib/accessors/builders/RoomBuilder.ts @@ -18,6 +18,8 @@ export class RoomBuilder implements IRoomBuilder { private members: Array; + private changes: Partial = {}; + constructor(data?: Partial) { this.kind = RocketChatAssociationModel.ROOM; this.room = (data || { customFields: {} }) as IRoom; @@ -33,6 +35,7 @@ export class RoomBuilder implements IRoomBuilder { public setDisplayName(name: string): IRoomBuilder { this.room.displayName = name; + this.changes.displayName = name; return this; } @@ -42,6 +45,7 @@ export class RoomBuilder implements IRoomBuilder { public setSlugifiedName(name: string): IRoomBuilder { this.room.slugifiedName = name; + this.changes.slugifiedName = name; return this; } @@ -51,6 +55,7 @@ export class RoomBuilder implements IRoomBuilder { public setType(type: RoomType): IRoomBuilder { this.room.type = type; + this.changes.type = type; return this; } @@ -60,6 +65,7 @@ export class RoomBuilder implements IRoomBuilder { public setCreator(creator: IUser): IRoomBuilder { this.room.creator = creator; + this.changes.creator = creator; return this; } @@ -110,6 +116,7 @@ export class RoomBuilder implements IRoomBuilder { public setDefault(isDefault: boolean): IRoomBuilder { this.room.isDefault = isDefault; + this.changes.isDefault = isDefault; return this; } @@ -119,6 +126,7 @@ export class RoomBuilder implements IRoomBuilder { public setReadOnly(isReadOnly: boolean): IRoomBuilder { this.room.isReadOnly = isReadOnly; + this.changes.isReadOnly = isReadOnly; return this; } @@ -128,6 +136,7 @@ export class RoomBuilder implements IRoomBuilder { public setDisplayingOfSystemMessages(displaySystemMessages: boolean): IRoomBuilder { this.room.displaySystemMessages = displaySystemMessages; + this.changes.displaySystemMessages = displaySystemMessages; return this; } @@ -146,6 +155,7 @@ export class RoomBuilder implements IRoomBuilder { public setCustomFields(fields: { [key: string]: object }): IRoomBuilder { this.room.customFields = fields; + this.changes.customFields = fields; return this; } diff --git a/packages/apps-engine/src/definition/accessors/IRoomBuilder.ts b/packages/apps-engine/src/definition/accessors/IRoomBuilder.ts index b92955896380f..2b0b4071a6782 100644 --- a/packages/apps-engine/src/definition/accessors/IRoomBuilder.ts +++ b/packages/apps-engine/src/definition/accessors/IRoomBuilder.ts @@ -183,4 +183,11 @@ export interface IRoomBuilder { * Note: modifying the returned value will have no effect. */ getRoom(): IRoom; + + /** + * Gets all changes applied by calling the `set` methods in the builder + * Essentially, a diff between the original data that the builder was + * constructed with and modifications applied by the app + */ + getChanges(): Partial; } From e0159f40881548b3720d370ce6c6501bb7f07b1b Mon Sep 17 00:00:00 2001 From: Douglas Gubert Date: Tue, 4 Feb 2025 16:56:11 -0300 Subject: [PATCH 02/16] wip #2 --- .../app/apps/server/converters/rooms.js | 43 ++++++++++++++++--- .../lib/accessors/builders/RoomBuilder.ts | 5 +++ .../lib/accessors/modify/ModifyUpdater.ts | 18 ++++---- 3 files changed, 52 insertions(+), 14 deletions(-) diff --git a/apps/meteor/app/apps/server/converters/rooms.js b/apps/meteor/app/apps/server/converters/rooms.js index 741f989321916..b090db8558685 100644 --- a/apps/meteor/app/apps/server/converters/rooms.js +++ b/apps/meteor/app/apps/server/converters/rooms.js @@ -20,7 +20,7 @@ export class AppRoomsConverter { return this.convertRoom(room); } - async convertAppRoom(room) { + async convertAppRoom(room, isPartial) { if (!room) { return undefined; } @@ -81,6 +81,26 @@ export class AppRoomsConverter { contactId = contact._id; } + let _default; + if (typeof room.isDefault !== 'undefined') { + _default = room.isDefault; + } + + let ro; + if (typeof room.isReadOnly !== 'undefined') { + ro = room.isReadOnly; + } + + let sysMes; + if (typeof room.displaySystemMessages !== 'undefined') { + sysMes = room.displaySystemMessages; + } + + let msgs; + if (typeof room.messageCount !== 'undefined') { + msgs = room.messageCount; + } + const newRoom = { ...(room.id && { _id: room.id }), fname: room.displayName, @@ -88,17 +108,17 @@ export class AppRoomsConverter { t: room.type, u, v, + ro, + sysMes, + msgs, departmentId, servedBy, closedBy, members: room.members, uids: room.userIds, - default: typeof room.isDefault === 'undefined' ? false : room.isDefault, - ro: typeof room.isReadOnly === 'undefined' ? false : room.isReadOnly, - sysMes: typeof room.displaySystemMessages === 'undefined' ? true : room.displaySystemMessages, + default: _default, waitingResponse: typeof room.isWaitingResponse === 'undefined' ? undefined : !!room.isWaitingResponse, open: typeof room.isOpen === 'undefined' ? undefined : !!room.isOpen, - msgs: room.messageCount || 0, ts: room.createdAt, _updatedAt: room.updatedAt, closedAt: room.closedAt, @@ -115,7 +135,17 @@ export class AppRoomsConverter { }), }; - return Object.assign(newRoom, room._unmappedProperties_); + if (isPartial) { + Object.entries(newRoom).forEach(([key, value]) => { + if (typeof value === 'undefined') { + delete newRoom[key]; + } + }); + } else { + Object.assign(newRoom, room._unmappedProperties_); + } + + return newRoom; } async convertRoom(originalRoom) { @@ -238,6 +268,7 @@ export class AppRoomsConverter { if (originalRoom.closer === 'user') { return this.orch.getConverters().get('users').convertById(closedBy._id); } + return this.orch.getConverters().get('visitors').convertById(closedBy._id); }, servedBy: async (room) => { diff --git a/packages/apps-engine/deno-runtime/lib/accessors/builders/RoomBuilder.ts b/packages/apps-engine/deno-runtime/lib/accessors/builders/RoomBuilder.ts index 67186a907f2fa..8bb34032b2eaf 100644 --- a/packages/apps-engine/deno-runtime/lib/accessors/builders/RoomBuilder.ts +++ b/packages/apps-engine/deno-runtime/lib/accessors/builders/RoomBuilder.ts @@ -36,6 +36,7 @@ export class RoomBuilder implements IRoomBuilder { public setDisplayName(name: string): IRoomBuilder { this.room.displayName = name; this.changes.displayName = name; + return this; } @@ -170,4 +171,8 @@ export class RoomBuilder implements IRoomBuilder { public getRoom(): IRoom { return this.room; } + + public getChanges() { + return structuredClone(this.changes); + } } diff --git a/packages/apps-engine/deno-runtime/lib/accessors/modify/ModifyUpdater.ts b/packages/apps-engine/deno-runtime/lib/accessors/modify/ModifyUpdater.ts index 8befe7bfa983e..1faa05f1d0765 100644 --- a/packages/apps-engine/deno-runtime/lib/accessors/modify/ModifyUpdater.ts +++ b/packages/apps-engine/deno-runtime/lib/accessors/modify/ModifyUpdater.ts @@ -121,33 +121,35 @@ export class ModifyUpdater implements IModifyUpdater { } private async _finishRoom(builder: IRoomBuilder): Promise { - const result = builder.getRoom(); + const room = builder.getRoom(); - if (!result.id) { + if (!room.id) { throw new Error("Invalid room, can't update a room without an id."); } - if (!result.type) { + if (!room.type) { throw new Error('Invalid type assigned to the room.'); } - if (result.type !== RoomType.LIVE_CHAT) { - if (!result.creator || !result.creator.id) { + if (room.type !== RoomType.LIVE_CHAT) { + if (!room.creator || !room.creator.id) { throw new Error('Invalid creator assigned to the room.'); } - if (!result.slugifiedName || !result.slugifiedName.trim()) { + if (!room.slugifiedName || !room.slugifiedName.trim()) { throw new Error('Invalid slugifiedName assigned to the room.'); } } - if (!result.displayName || !result.displayName.trim()) { + if (!room.displayName || !room.displayName.trim()) { throw new Error('Invalid displayName assigned to the room.'); } + const changes = { id: room.id, ...builder.getChanges() }; + await this.senderFn({ method: 'bridges:getRoomBridge:doUpdate', - params: [result, builder.getMembersToBeAddedUsernames(), AppObjectRegistry.get('id')], + params: [changes, builder.getMembersToBeAddedUsernames(), AppObjectRegistry.get('id')], }); } } From f1f9126087ae8a56367502501e18e86a377abb9a Mon Sep 17 00:00:00 2001 From: Douglas Gubert Date: Tue, 11 Feb 2025 16:43:54 -0300 Subject: [PATCH 03/16] Set editor when creating a message builder from the modifier --- .../deno-runtime/lib/accessors/modify/ModifyUpdater.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/apps-engine/deno-runtime/lib/accessors/modify/ModifyUpdater.ts b/packages/apps-engine/deno-runtime/lib/accessors/modify/ModifyUpdater.ts index 1faa05f1d0765..04d7bfd14f261 100644 --- a/packages/apps-engine/deno-runtime/lib/accessors/modify/ModifyUpdater.ts +++ b/packages/apps-engine/deno-runtime/lib/accessors/modify/ModifyUpdater.ts @@ -70,13 +70,17 @@ export class ModifyUpdater implements IModifyUpdater { ) as IUserUpdater; } - public async message(messageId: string, _updater: IUser): Promise { + public async message(messageId: string, editor: IUser): Promise { const response = await this.senderFn({ method: 'bridges:getMessageBridge:doGetById', params: [messageId, AppObjectRegistry.get('id')], }); - return new MessageBuilder(response.result as IMessage); + const builder = new MessageBuilder(response.result as IMessage); + + builder.setEditor(editor); + + return builder; } public async room(roomId: string, _updater: IUser): Promise { From f3de95f3489544060f34b96836fa9d7ced9d390c Mon Sep 17 00:00:00 2001 From: Douglas Gubert Date: Tue, 11 Feb 2025 16:48:59 -0300 Subject: [PATCH 04/16] Adapt room and message converters --- .../app/apps/server/converters/messages.js | 54 +++++++++++++++---- .../app/apps/server/converters/rooms.js | 2 +- .../src/converters/IAppMessagesConverter.ts | 1 + .../apps/src/converters/IAppRoomsConverter.ts | 3 +- 4 files changed, 47 insertions(+), 13 deletions(-) diff --git a/apps/meteor/app/apps/server/converters/messages.js b/apps/meteor/app/apps/server/converters/messages.js index 704dde1850490..891db7c78ad30 100644 --- a/apps/meteor/app/apps/server/converters/messages.js +++ b/apps/meteor/app/apps/server/converters/messages.js @@ -135,19 +135,23 @@ export class AppMessagesConverter { return transformMappedData(msgObj, map); } - async convertAppMessage(message) { - if (!message || !message.room) { + async convertAppMessage(message, isPartial = false) { + if (!message) { return undefined; } - const room = await Rooms.findOneById(message.room.id); + let rid; + if (message.room?.id) { + const room = await Rooms.findOneById(message.room.id); + rid = room._id; + } - if (!room) { + if (!rid && !isPartial) { throw new Error('Invalid room provided on the message.'); } let u; - if (message.sender && message.sender.id) { + if (message.sender?.id) { const user = await Users.findOneById(message.sender.id); if (user) { @@ -156,7 +160,7 @@ export class AppMessagesConverter { username: user.username, name: user.name, }; - } else { + } else if (!isPartial) { u = { _id: message.sender.id, username: message.sender.username, @@ -176,14 +180,32 @@ export class AppMessagesConverter { const attachments = this._convertAppAttachments(message.attachments); + let _id; + let ts; + let _updatedAt; + + if (!isPartial) { + if (!message.id) { + _id = Random.id(); + } + + if (!message.createdAt) { + ts = new Date(); + } + + if (!message.updatedAt) { + _updatedAt = new Date(); + } + } + const newMessage = { - _id: message.id || Random.id(), + _id, ...('threadId' in message && { tmid: message.threadId }), - rid: room._id, + rid, u, msg: message.text, - ts: message.createdAt || new Date(), - _updatedAt: message.updatedAt || new Date(), + ts, + _updatedAt, ...(editedBy && { editedBy }), ...('editedAt' in message && { editedAt: message.editedAt }), ...('emoji' in message && { emoji: message.emoji }), @@ -198,7 +220,17 @@ export class AppMessagesConverter { ...('token' in message && { token: message.token }), }; - return Object.assign(newMessage, message._unmappedProperties_); + if (isPartial) { + Object.entries(newMessage).forEach(([key, value]) => { + if (typeof value === 'undefined') { + delete newMessage[key]; + } + }); + } else { + Object.assign(newMessage, message._unmappedProperties_); + } + + return newMessage; } _convertAppAttachments(attachments) { diff --git a/apps/meteor/app/apps/server/converters/rooms.js b/apps/meteor/app/apps/server/converters/rooms.js index b090db8558685..35cf9ccdf2aed 100644 --- a/apps/meteor/app/apps/server/converters/rooms.js +++ b/apps/meteor/app/apps/server/converters/rooms.js @@ -20,7 +20,7 @@ export class AppRoomsConverter { return this.convertRoom(room); } - async convertAppRoom(room, isPartial) { + async convertAppRoom(room, isPartial = false) { if (!room) { return undefined; } diff --git a/packages/apps/src/converters/IAppMessagesConverter.ts b/packages/apps/src/converters/IAppMessagesConverter.ts index 863c10c954777..8c10114304d09 100644 --- a/packages/apps/src/converters/IAppMessagesConverter.ts +++ b/packages/apps/src/converters/IAppMessagesConverter.ts @@ -9,6 +9,7 @@ export interface IAppMessagesConverter { convertMessage(message: IMessage | undefined | null): Promise; convertAppMessage(message: undefined | null): Promise; convertAppMessage(message: IAppsMessage): Promise; + convertAppMessage(message: IAppsMessage, isPartial: boolean): Promise>; convertAppMessage(message: IAppsMessage | undefined | null): Promise; convertMessageRaw(message: IMessage): Promise; convertMessageRaw(message: IMessage | undefined | null): Promise; diff --git a/packages/apps/src/converters/IAppRoomsConverter.ts b/packages/apps/src/converters/IAppRoomsConverter.ts index 9408b3f9b63ca..83b12ae4503ca 100644 --- a/packages/apps/src/converters/IAppRoomsConverter.ts +++ b/packages/apps/src/converters/IAppRoomsConverter.ts @@ -10,5 +10,6 @@ export interface IAppRoomsConverter { convertRoom(room: IRoom | undefined | null): Promise; convertAppRoom(room: undefined | null): Promise; convertAppRoom(room: IAppsRoom): Promise; - convertAppRoom(room: IAppsRoom | undefined | null): Promise; + convertAppRoom(room: IAppsRoom, isPartial: boolean): Promise>; + convertAppRoom(room: IAppsRoom | undefined | null, isPartial?: boolean): Promise | undefined>; } From fafeb03a555b90e13a9069f3b14310ae6551770f Mon Sep 17 00:00:00 2001 From: Douglas Gubert Date: Tue, 11 Feb 2025 16:49:40 -0300 Subject: [PATCH 05/16] Adapt message and room builders --- .../lib/accessors/builders/MessageBuilder.ts | 62 ++++++++++++++++--- .../lib/accessors/builders/RoomBuilder.ts | 21 ++++++- .../lib/accessors/modify/ModifyUpdater.ts | 12 ++-- .../src/definition/accessors/IRoomBuilder.ts | 7 --- 4 files changed, 78 insertions(+), 24 deletions(-) diff --git a/packages/apps-engine/deno-runtime/lib/accessors/builders/MessageBuilder.ts b/packages/apps-engine/deno-runtime/lib/accessors/builders/MessageBuilder.ts index 98cd919f7b002..b129f564db2e0 100644 --- a/packages/apps-engine/deno-runtime/lib/accessors/builders/MessageBuilder.ts +++ b/packages/apps-engine/deno-runtime/lib/accessors/builders/MessageBuilder.ts @@ -20,6 +20,10 @@ export class MessageBuilder implements IMessageBuilder { private msg: IMessage; + private changes: Partial = {}; + private attachmentsChanged = false; + private customFieldsChanged = false; + constructor(message?: IMessage) { this.kind = RocketChatAssociationModel.MESSAGE; this.msg = message || ({} as IMessage); @@ -37,11 +41,15 @@ export class MessageBuilder implements IMessageBuilder { this.msg.editor = editor; this.msg.editedAt = new Date(); + this.changes.editor = editor; + this.changes.editedAt = this.msg.editedAt; + return this as IMessageBuilder; } public setThreadId(threadId: string): IMessageBuilder { this.msg.threadId = threadId; + this.changes.threadId = threadId; return this as IMessageBuilder; } @@ -52,6 +60,8 @@ export class MessageBuilder implements IMessageBuilder { public setRoom(room: IRoom): IMessageBuilder { this.msg.room = room; + this.changes.room = room; + return this as IMessageBuilder; } @@ -61,6 +71,8 @@ export class MessageBuilder implements IMessageBuilder { public setSender(sender: IUser): IMessageBuilder { this.msg.sender = sender; + this.changes.sender = sender; + return this as IMessageBuilder; } @@ -70,6 +82,8 @@ export class MessageBuilder implements IMessageBuilder { public setText(text: string): IMessageBuilder { this.msg.text = text; + this.changes.text = text; + return this as IMessageBuilder; } @@ -79,6 +93,8 @@ export class MessageBuilder implements IMessageBuilder { public setEmojiAvatar(emoji: string): IMessageBuilder { this.msg.emoji = emoji; + this.changes.emoji = emoji; + return this as IMessageBuilder; } @@ -88,6 +104,8 @@ export class MessageBuilder implements IMessageBuilder { public setAvatarUrl(avatarUrl: string): IMessageBuilder { this.msg.avatarUrl = avatarUrl; + this.changes.avatarUrl = avatarUrl; + return this as IMessageBuilder; } @@ -97,6 +115,8 @@ export class MessageBuilder implements IMessageBuilder { public setUsernameAlias(alias: string): IMessageBuilder { this.msg.alias = alias; + this.changes.alias = alias; + return this as IMessageBuilder; } @@ -110,11 +130,15 @@ export class MessageBuilder implements IMessageBuilder { } this.msg.attachments.push(attachment); + this.attachmentsChanged = true; + return this as IMessageBuilder; } public setAttachments(attachments: Array): IMessageBuilder { this.msg.attachments = attachments; + this.attachmentsChanged = true; + return this as IMessageBuilder; } @@ -123,34 +147,31 @@ export class MessageBuilder implements IMessageBuilder { } public replaceAttachment(position: number, attachment: IMessageAttachment): IMessageBuilder { - if (!this.msg.attachments) { - this.msg.attachments = []; - } - - if (!this.msg.attachments[position]) { + if (!this.msg.attachments?.[position]) { throw new Error(`No attachment found at the index of "${position}" to replace.`); } this.msg.attachments[position] = attachment; + this.attachmentsChanged = true; + return this as IMessageBuilder; } public removeAttachment(position: number): IMessageBuilder { - if (!this.msg.attachments) { - this.msg.attachments = []; - } - - if (!this.msg.attachments[position]) { + if (!this.msg.attachments?.[position]) { throw new Error(`No attachment found at the index of "${position}" to remove.`); } this.msg.attachments.splice(position, 1); + this.attachmentsChanged = true; return this as IMessageBuilder; } public setEditor(user: IUser): IMessageBuilder { this.msg.editor = user; + this.changes.editor = user; + return this as IMessageBuilder; } @@ -160,6 +181,8 @@ export class MessageBuilder implements IMessageBuilder { public setGroupable(groupable: boolean): IMessageBuilder { this.msg.groupable = groupable; + this.changes.groupable = groupable; + return this as IMessageBuilder; } @@ -169,6 +192,8 @@ export class MessageBuilder implements IMessageBuilder { public setParseUrls(parseUrls: boolean): IMessageBuilder { this.msg.parseUrls = parseUrls; + this.changes.parseUrls = parseUrls; + return this as IMessageBuilder; } @@ -203,6 +228,7 @@ export class MessageBuilder implements IMessageBuilder { this.msg.blocks = blocks.getBlocks(); } else { this.msg.blocks = blocks; + this.changes.blocks = blocks; } return this as IMessageBuilder; @@ -227,6 +253,22 @@ export class MessageBuilder implements IMessageBuilder { this.msg.customFields[key] = value; + this.customFieldsChanged = true; + return this as IMessageBuilder; } + + public getChanges(): Partial { + const changes: typeof this.changes = structuredClone(this.changes); + + if (this.attachmentsChanged) { + changes.attachments = structuredClone(this.msg.attachments); + } + + if (this.customFieldsChanged) { + changes.customFields = structuredClone(this.msg.customFields); + } + + return changes; + } } diff --git a/packages/apps-engine/deno-runtime/lib/accessors/builders/RoomBuilder.ts b/packages/apps-engine/deno-runtime/lib/accessors/builders/RoomBuilder.ts index 8bb34032b2eaf..3fa59feefe3f6 100644 --- a/packages/apps-engine/deno-runtime/lib/accessors/builders/RoomBuilder.ts +++ b/packages/apps-engine/deno-runtime/lib/accessors/builders/RoomBuilder.ts @@ -19,6 +19,7 @@ export class RoomBuilder implements IRoomBuilder { private members: Array; private changes: Partial = {}; + private customFieldsChanged = false; constructor(data?: Partial) { this.kind = RocketChatAssociationModel.ROOM; @@ -47,6 +48,7 @@ export class RoomBuilder implements IRoomBuilder { public setSlugifiedName(name: string): IRoomBuilder { this.room.slugifiedName = name; this.changes.slugifiedName = name; + return this; } @@ -57,6 +59,7 @@ export class RoomBuilder implements IRoomBuilder { public setType(type: RoomType): IRoomBuilder { this.room.type = type; this.changes.type = type; + return this; } @@ -67,6 +70,7 @@ export class RoomBuilder implements IRoomBuilder { public setCreator(creator: IUser): IRoomBuilder { this.room.creator = creator; this.changes.creator = creator; + return this; } @@ -118,6 +122,7 @@ export class RoomBuilder implements IRoomBuilder { public setDefault(isDefault: boolean): IRoomBuilder { this.room.isDefault = isDefault; this.changes.isDefault = isDefault; + return this; } @@ -128,6 +133,7 @@ export class RoomBuilder implements IRoomBuilder { public setReadOnly(isReadOnly: boolean): IRoomBuilder { this.room.isReadOnly = isReadOnly; this.changes.isReadOnly = isReadOnly; + return this; } @@ -138,6 +144,7 @@ export class RoomBuilder implements IRoomBuilder { public setDisplayingOfSystemMessages(displaySystemMessages: boolean): IRoomBuilder { this.room.displaySystemMessages = displaySystemMessages; this.changes.displaySystemMessages = displaySystemMessages; + return this; } @@ -151,12 +158,16 @@ export class RoomBuilder implements IRoomBuilder { } this.room.customFields[key] = value; + + this.customFieldsChanged = true; + return this; } public setCustomFields(fields: { [key: string]: object }): IRoomBuilder { this.room.customFields = fields; - this.changes.customFields = fields; + this.customFieldsChanged = true; + return this; } @@ -173,6 +184,12 @@ export class RoomBuilder implements IRoomBuilder { } public getChanges() { - return structuredClone(this.changes); + const changes: Partial = structuredClone(this.changes); + + if (this.customFieldsChanged) { + changes.customFields = structuredClone(this.room.customFields); + } + + return changes; } } diff --git a/packages/apps-engine/deno-runtime/lib/accessors/modify/ModifyUpdater.ts b/packages/apps-engine/deno-runtime/lib/accessors/modify/ModifyUpdater.ts index 04d7bfd14f261..fbdeee609e9d2 100644 --- a/packages/apps-engine/deno-runtime/lib/accessors/modify/ModifyUpdater.ts +++ b/packages/apps-engine/deno-runtime/lib/accessors/modify/ModifyUpdater.ts @@ -95,15 +95,15 @@ export class ModifyUpdater implements IModifyUpdater { public finish(builder: IMessageBuilder | IRoomBuilder): Promise { switch (builder.kind) { case RocketChatAssociationModel.MESSAGE: - return this._finishMessage(builder as IMessageBuilder); + return this._finishMessage(builder as MessageBuilder); case RocketChatAssociationModel.ROOM: - return this._finishRoom(builder as IRoomBuilder); + return this._finishRoom(builder as RoomBuilder); default: throw new Error('Invalid builder passed to the ModifyUpdater.finish function.'); } } - private async _finishMessage(builder: IMessageBuilder): Promise { + private async _finishMessage(builder: MessageBuilder): Promise { const result = builder.getMessage(); if (!result.id) { @@ -118,13 +118,15 @@ export class ModifyUpdater implements IModifyUpdater { result.blocks = UIHelper.assignIds(result.blocks, AppObjectRegistry.get('id') || ''); } + const changes = { id: result.id, ...builder.getChanges() }; + await this.senderFn({ method: 'bridges:getMessageBridge:doUpdate', - params: [result, AppObjectRegistry.get('id')], + params: [changes, AppObjectRegistry.get('id')], }); } - private async _finishRoom(builder: IRoomBuilder): Promise { + private async _finishRoom(builder: RoomBuilder): Promise { const room = builder.getRoom(); if (!room.id) { diff --git a/packages/apps-engine/src/definition/accessors/IRoomBuilder.ts b/packages/apps-engine/src/definition/accessors/IRoomBuilder.ts index 2b0b4071a6782..b92955896380f 100644 --- a/packages/apps-engine/src/definition/accessors/IRoomBuilder.ts +++ b/packages/apps-engine/src/definition/accessors/IRoomBuilder.ts @@ -183,11 +183,4 @@ export interface IRoomBuilder { * Note: modifying the returned value will have no effect. */ getRoom(): IRoom; - - /** - * Gets all changes applied by calling the `set` methods in the builder - * Essentially, a diff between the original data that the builder was - * constructed with and modifications applied by the app - */ - getChanges(): Partial; } From 0ac31184dda7f8a1825752d8d22096fef634b3f6 Mon Sep 17 00:00:00 2001 From: Douglas Gubert Date: Tue, 11 Feb 2025 16:50:25 -0300 Subject: [PATCH 06/16] Adapt bridge methods --- apps/meteor/app/apps/server/bridges/messages.ts | 8 ++------ apps/meteor/app/apps/server/bridges/rooms.ts | 12 ++++++------ 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/apps/meteor/app/apps/server/bridges/messages.ts b/apps/meteor/app/apps/server/bridges/messages.ts index 824a9d5c15afd..7d9f46b401d7f 100644 --- a/apps/meteor/app/apps/server/bridges/messages.ts +++ b/apps/meteor/app/apps/server/bridges/messages.ts @@ -5,7 +5,7 @@ import type { ITypingDescriptor } from '@rocket.chat/apps-engine/server/bridges/ import { MessageBridge } from '@rocket.chat/apps-engine/server/bridges/MessageBridge'; import { api } from '@rocket.chat/core-services'; import type { IMessage } from '@rocket.chat/core-typings'; -import { Users, Subscriptions, Messages } from '@rocket.chat/models'; +import { Users, Subscriptions } from '@rocket.chat/models'; import { deleteMessage } from '../../../lib/server/functions/deleteMessage'; import { updateMessage } from '../../../lib/server/functions/updateMessage'; @@ -44,12 +44,8 @@ export class AppMessageBridge extends MessageBridge { throw new Error('Invalid editor assigned to the message for the update.'); } - if (!message.id || !(await Messages.findOneById(message.id))) { - throw new Error('A message must exist to update.'); - } - // #TODO: #AppsEngineTypes - Remove explicit types and typecasts once the apps-engine definition/implementation mismatch is fixed. - const msg: IMessage | undefined = await this.orch.getConverters()?.get('messages').convertAppMessage(message); + const msg: IMessage | undefined = await this.orch.getConverters()?.get('messages').convertAppMessage(message, true); const editor = await Users.findOneById(message.editor.id); if (!editor) { diff --git a/apps/meteor/app/apps/server/bridges/rooms.ts b/apps/meteor/app/apps/server/bridges/rooms.ts index d88881330a17d..ea8a7fdf6e7e6 100644 --- a/apps/meteor/app/apps/server/bridges/rooms.ts +++ b/apps/meteor/app/apps/server/bridges/rooms.ts @@ -163,13 +163,13 @@ export class AppRoomBridge extends RoomBridge { protected async update(room: IRoom, members: Array = [], appId: string): Promise { this.orch.debugLog(`The App ${appId} is updating a room.`); - if (!room.id || !(await Rooms.findOneById(room.id))) { - throw new Error('A room must exist to update.'); - } + const rm = await this.orch.getConverters()?.get('rooms').convertAppRoom(room, true); - const rm = await this.orch.getConverters()?.get('rooms').convertAppRoom(room); + const updateResult = await Rooms.updateOne({ _id: room.id }, { $set: rm }); - await Rooms.updateOne({ _id: rm._id }, { $set: rm as Partial }); + if (!updateResult.matchedCount) { + throw new Error('Room id not found'); + } for await (const username of members) { const member = await Users.findOneByUsername(username, {}); @@ -178,7 +178,7 @@ export class AppRoomBridge extends RoomBridge { continue; } - await addUserToRoom(rm._id, member); + await addUserToRoom(room.id, member); } } From a7441ee0c612136db5db0e498f981fa275d9b79f Mon Sep 17 00:00:00 2001 From: Douglas Gubert Date: Tue, 11 Feb 2025 20:02:03 -0300 Subject: [PATCH 07/16] Fix types --- apps/meteor/app/apps/server/bridges/messages.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/meteor/app/apps/server/bridges/messages.ts b/apps/meteor/app/apps/server/bridges/messages.ts index 7d9f46b401d7f..16366626c07c8 100644 --- a/apps/meteor/app/apps/server/bridges/messages.ts +++ b/apps/meteor/app/apps/server/bridges/messages.ts @@ -45,7 +45,7 @@ export class AppMessageBridge extends MessageBridge { } // #TODO: #AppsEngineTypes - Remove explicit types and typecasts once the apps-engine definition/implementation mismatch is fixed. - const msg: IMessage | undefined = await this.orch.getConverters()?.get('messages').convertAppMessage(message, true); + const msg = await this.orch.getConverters()?.get('messages').convertAppMessage(message, true); const editor = await Users.findOneById(message.editor.id); if (!editor) { From e369046f1bf87ff5d87783e115855039bc32109d Mon Sep 17 00:00:00 2001 From: Douglas Gubert Date: Thu, 13 Feb 2025 16:32:48 -0300 Subject: [PATCH 08/16] Fix tests --- .../deno-runtime/lib/accessors/builders/MessageBuilder.ts | 3 +-- .../deno-runtime/lib/accessors/builders/RoomBuilder.ts | 2 ++ .../deno-runtime/lib/accessors/tests/ModifyUpdater.test.ts | 7 ++++--- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/apps-engine/deno-runtime/lib/accessors/builders/MessageBuilder.ts b/packages/apps-engine/deno-runtime/lib/accessors/builders/MessageBuilder.ts index b129f564db2e0..d4005e3ec52b3 100644 --- a/packages/apps-engine/deno-runtime/lib/accessors/builders/MessageBuilder.ts +++ b/packages/apps-engine/deno-runtime/lib/accessors/builders/MessageBuilder.ts @@ -41,8 +41,7 @@ export class MessageBuilder implements IMessageBuilder { this.msg.editor = editor; this.msg.editedAt = new Date(); - this.changes.editor = editor; - this.changes.editedAt = this.msg.editedAt; + this.changes = structuredClone(this.msg); return this as IMessageBuilder; } diff --git a/packages/apps-engine/deno-runtime/lib/accessors/builders/RoomBuilder.ts b/packages/apps-engine/deno-runtime/lib/accessors/builders/RoomBuilder.ts index 3fa59feefe3f6..44119ab67ab41 100644 --- a/packages/apps-engine/deno-runtime/lib/accessors/builders/RoomBuilder.ts +++ b/packages/apps-engine/deno-runtime/lib/accessors/builders/RoomBuilder.ts @@ -31,6 +31,8 @@ export class RoomBuilder implements IRoomBuilder { delete data.id; this.room = data as IRoom; + this.changes = structuredClone(this.room); + return this; } diff --git a/packages/apps-engine/deno-runtime/lib/accessors/tests/ModifyUpdater.test.ts b/packages/apps-engine/deno-runtime/lib/accessors/tests/ModifyUpdater.test.ts index 313275c967cfa..1201179952172 100644 --- a/packages/apps-engine/deno-runtime/lib/accessors/tests/ModifyUpdater.test.ts +++ b/packages/apps-engine/deno-runtime/lib/accessors/tests/ModifyUpdater.test.ts @@ -5,6 +5,7 @@ import { assertEquals } from 'https://deno.land/std@0.203.0/assert/mod.ts'; import { AppObjectRegistry } from '../../../AppObjectRegistry.ts'; import { ModifyUpdater } from '../modify/ModifyUpdater.ts'; +import { RoomBuilder } from "../builders/RoomBuilder.ts"; describe('ModifyUpdater', () => { let modifyUpdater: ModifyUpdater; @@ -61,7 +62,7 @@ describe('ModifyUpdater', () => { args: [ { method: 'bridges:getMessageBridge:doUpdate', - params: [messageBuilder.getMessage(), 'deno-test'], + params: [{ id: '123', ...messageBuilder.getChanges() }, 'deno-test'], }, ], }); @@ -72,7 +73,7 @@ describe('ModifyUpdater', () => { it('correctly formats requests for the update room flow', async () => { const _spy = spy(modifyUpdater, 'senderFn' as keyof ModifyUpdater); - const roomBuilder = await modifyUpdater.room('123', { id: '456' } as any); + const roomBuilder = await modifyUpdater.room('123', { id: '456' } as any) as RoomBuilder; assertSpyCall(_spy, 0, { args: [ @@ -102,7 +103,7 @@ describe('ModifyUpdater', () => { args: [ { method: 'bridges:getRoomBridge:doUpdate', - params: [roomBuilder.getRoom(), roomBuilder.getMembersToBeAddedUsernames(), 'deno-test'], + params: [{ id: '123', ...roomBuilder.getChanges() }, roomBuilder.getMembersToBeAddedUsernames(), 'deno-test'], }, ], }); From 24c926d840c21c339fa4adb1707db9af1fafbc34 Mon Sep 17 00:00:00 2001 From: Douglas Gubert Date: Thu, 13 Feb 2025 19:47:04 -0300 Subject: [PATCH 09/16] Fix tests --- .../app/apps/server/converters/messages.js | 9 +++--- .../unit/app/apps/server/messages.tests.js | 30 +++++++++++++++++-- .../apps/server/mocks/data/messages.data.js | 8 +++++ 3 files changed, 41 insertions(+), 6 deletions(-) diff --git a/apps/meteor/app/apps/server/converters/messages.js b/apps/meteor/app/apps/server/converters/messages.js index 891db7c78ad30..a92b819a137db 100644 --- a/apps/meteor/app/apps/server/converters/messages.js +++ b/apps/meteor/app/apps/server/converters/messages.js @@ -141,9 +141,10 @@ export class AppMessagesConverter { } let rid; + console.log(message) if (message.room?.id) { const room = await Rooms.findOneById(message.room.id); - rid = room._id; + rid = room?._id; } if (!rid && !isPartial) { @@ -180,9 +181,9 @@ export class AppMessagesConverter { const attachments = this._convertAppAttachments(message.attachments); - let _id; - let ts; - let _updatedAt; + let _id = message.id; + let ts = message.createdAt; + let _updatedAt = message.updatedAt; if (!isPartial) { if (!message.id) { diff --git a/apps/meteor/tests/unit/app/apps/server/messages.tests.js b/apps/meteor/tests/unit/app/apps/server/messages.tests.js index 04866f6440b6d..2e0a119ec3c25 100644 --- a/apps/meteor/tests/unit/app/apps/server/messages.tests.js +++ b/apps/meteor/tests/unit/app/apps/server/messages.tests.js @@ -1,7 +1,7 @@ import { expect } from 'chai'; import proxyquire from 'proxyquire'; -import { appMessageMock, appMessageInvalidRoomMock } from './mocks/data/messages.data'; +import { appMessageMock, appMessageInvalidRoomMock, appPartialMessageMock } from './mocks/data/messages.data'; import { MessagesMock } from './mocks/models/Messages.mock'; import { RoomsMock } from './mocks/models/Rooms.mock'; import { UsersMock } from './mocks/models/Users.mock'; @@ -23,7 +23,7 @@ const { AppMessagesConverter } = proxyquire.noCallThru().load('../../../../../ap }, }); -describe('The AppMessagesConverter instance', () => { +describe.only('The AppMessagesConverter instance', () => { let messagesConverter; let messagesMock; @@ -140,6 +140,20 @@ describe('The AppMessagesConverter instance', () => { }); }); + it('should return a proper schema when receiving a partial object', async () => { + const rocketchatMessage = await messagesConverter.convertAppMessage(appPartialMessageMock, true); + + expect(rocketchatMessage).to.have.property('_id', 'appPartialMessageMock'); + expect(rocketchatMessage).to.have.property('groupable', false); + expect(rocketchatMessage).to.have.property('emoji', ':smirk:'); + expect(rocketchatMessage).to.have.property('alias', 'rocket.feline'); + + expect(rocketchatMessage).to.not.have.property('ts'); + expect(rocketchatMessage).to.not.have.property('u'); + expect(rocketchatMessage).to.not.have.property('rid'); + expect(rocketchatMessage).to.not.have.property('_updatedAt'); + }); + it('should merge `_unmappedProperties_` into the returned message', async () => { const rocketchatMessage = await messagesConverter.convertAppMessage(appMessageMock); @@ -147,6 +161,18 @@ describe('The AppMessagesConverter instance', () => { expect(rocketchatMessage).to.have.property('t', 'uj'); }); + it('should not merge `_unmappedProperties_` into the returned message when receiving a partial object', async () => { + const invalidPartialMessage = structuredClone(appPartialMessageMock); + invalidPartialMessage._unmappedProperties_ = { + t: 'uj', + }; + + const rocketchatMessage = await messagesConverter.convertAppMessage(invalidPartialMessage, true); + + expect(rocketchatMessage).to.not.have.property('_unmappedProperties_'); + expect(rocketchatMessage).to.not.have.property('t'); + }); + it('should throw if message has an invalid room', async () => { try { await messagesConverter.convertAppMessage(appMessageInvalidRoomMock); diff --git a/apps/meteor/tests/unit/app/apps/server/mocks/data/messages.data.js b/apps/meteor/tests/unit/app/apps/server/mocks/data/messages.data.js index 70d1505d97fef..50e9089140f5b 100644 --- a/apps/meteor/tests/unit/app/apps/server/mocks/data/messages.data.js +++ b/apps/meteor/tests/unit/app/apps/server/mocks/data/messages.data.js @@ -56,6 +56,14 @@ export const appMessageMock = { }, }; +export const appPartialMessageMock = { + id: 'appPartialMessageMock', + text: 'rocket.cat', + groupable: false, + emoji: ':smirk:', + alias: 'rocket.feline', +}; + export const appMessageInvalidRoomMock = { id: 'appMessageInvalidRoomMock', text: 'rocket.cat', From dd802e2b23069272a72b8d35ecd2040674ab4a65 Mon Sep 17 00:00:00 2001 From: Douglas Gubert Date: Thu, 13 Feb 2025 20:02:34 -0300 Subject: [PATCH 10/16] No only --- apps/meteor/app/apps/server/converters/messages.js | 1 - apps/meteor/tests/unit/app/apps/server/messages.tests.js | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/meteor/app/apps/server/converters/messages.js b/apps/meteor/app/apps/server/converters/messages.js index a92b819a137db..db0406a45e9b9 100644 --- a/apps/meteor/app/apps/server/converters/messages.js +++ b/apps/meteor/app/apps/server/converters/messages.js @@ -141,7 +141,6 @@ export class AppMessagesConverter { } let rid; - console.log(message) if (message.room?.id) { const room = await Rooms.findOneById(message.room.id); rid = room?._id; diff --git a/apps/meteor/tests/unit/app/apps/server/messages.tests.js b/apps/meteor/tests/unit/app/apps/server/messages.tests.js index 2e0a119ec3c25..b92d9378218c6 100644 --- a/apps/meteor/tests/unit/app/apps/server/messages.tests.js +++ b/apps/meteor/tests/unit/app/apps/server/messages.tests.js @@ -23,7 +23,7 @@ const { AppMessagesConverter } = proxyquire.noCallThru().load('../../../../../ap }, }); -describe.only('The AppMessagesConverter instance', () => { +describe('The AppMessagesConverter instance', () => { let messagesConverter; let messagesMock; From b3e9d22a656d38af83fe5f571cdde92c18b001f7 Mon Sep 17 00:00:00 2001 From: Douglas Gubert Date: Fri, 14 Feb 2025 18:26:58 -0300 Subject: [PATCH 11/16] Add room converter tests --- .../apps/server/mocks/models/Rooms.mock.js | 7 + .../tests/unit/app/apps/server/rooms.tests.ts | 122 ++++++++++++++++++ 2 files changed, 129 insertions(+) create mode 100644 apps/meteor/tests/unit/app/apps/server/rooms.tests.ts diff --git a/apps/meteor/tests/unit/app/apps/server/mocks/models/Rooms.mock.js b/apps/meteor/tests/unit/app/apps/server/mocks/models/Rooms.mock.js index 5a0a776f037bf..6cf0370f1754e 100644 --- a/apps/meteor/tests/unit/app/apps/server/mocks/models/Rooms.mock.js +++ b/apps/meteor/tests/unit/app/apps/server/mocks/models/Rooms.mock.js @@ -108,6 +108,13 @@ export class RoomsMock extends BaseModelMock { customFields: {}, }, + GENERALPartial: { + id: 'GENERAL', + slugifiedName: 'general', + displaySystemMessages: true, + updatedAt: new Date('2019-04-10T17:44:34.931Z'), + }, + LivechatRoom: { id: 'LivechatRoom', slugifiedName: undefined, diff --git a/apps/meteor/tests/unit/app/apps/server/rooms.tests.ts b/apps/meteor/tests/unit/app/apps/server/rooms.tests.ts new file mode 100644 index 0000000000000..718d79baef361 --- /dev/null +++ b/apps/meteor/tests/unit/app/apps/server/rooms.tests.ts @@ -0,0 +1,122 @@ +import type { IAppRoomsConverter, IAppsRoom } from '@rocket.chat/apps'; +import type { IRoom } from '@rocket.chat/core-typings'; +import { expect } from 'chai'; +import { before, describe, it } from 'mocha'; +import proxyquire from 'proxyquire'; + +import { MessagesMock } from './mocks/models/Messages.mock'; +import { RoomsMock } from './mocks/models/Rooms.mock'; +import { UsersMock } from './mocks/models/Users.mock'; +import { AppServerOrchestratorMock } from './mocks/orchestrator.mock'; + +const { AppRoomsConverter } = proxyquire.noCallThru().load('../../../../../app/apps/server/converters/rooms', { + '@rocket.chat/random': { + Random: { + id: () => 1, + }, + }, + '@rocket.chat/models': { + Rooms: new RoomsMock(), + Messages: new MessagesMock(), + Users: new UsersMock(), + }, +}); + +describe('The AppMessagesConverter instance', () => { + let roomConverter: IAppRoomsConverter; + let roomsMock: RoomsMock; + + before(() => { + const orchestrator = new AppServerOrchestratorMock(); + + const usersConverter = orchestrator.getConverters().get('users'); + + usersConverter.convertById = function convertUserByIdStub(id: string) { + return UsersMock.convertedData[id as 'rocket.cat'] || undefined; + }; + + usersConverter.convertToApp = function convertUserToAppStub(user: UsersMock['data']['rocket.cat']) { + return { + id: user._id, + username: user.username, + name: user.name, + }; + }; + + orchestrator.getConverters().get('messages').convertById = async function convertRoomByIdStub(_id: string) { + return {}; + }; + + roomConverter = new AppRoomsConverter(orchestrator); + roomsMock = new RoomsMock(); + }); + + describe('when converting a room from Rocket.Chat to the Engine schema', () => { + it('should return `undefined` when `originalRoom` is falsy', async () => { + const appRoom = await roomConverter.convertRoom(undefined); + + expect(appRoom).to.be.undefined; + }); + + it('should return a proper schema', async () => { + const mockedRoom = roomsMock.findOneById('GENERAL') as RoomsMock['data']['GENERAL']; + const appRoom = await roomConverter.convertRoom(mockedRoom as unknown as IRoom); + + expect(appRoom).to.have.property('id', mockedRoom._id); + expect(appRoom).to.have.property('type', mockedRoom.t); + expect(appRoom).to.have.property('slugifiedName', mockedRoom.name); + expect(appRoom).to.have.property('createdAt').which.equalTime(mockedRoom.ts); + expect(appRoom).to.have.property('updatedAt').which.equalTime(mockedRoom._updatedAt); + expect(appRoom).to.have.property('messageCount', mockedRoom.msgs); + }); + + it('should not mutate the original room object', async () => { + const rocketchatRoomMock = structuredClone(roomsMock.findOneById('GENERAL')); + + await roomConverter.convertRoom(rocketchatRoomMock); + + expect(rocketchatRoomMock).to.deep.equal(roomsMock.findOneById('GENERAL')); + }); + + it('should add an `_unmappedProperties_` field to the converted room which contains the `lastMessage` property of the room', async () => { + const mockedRoom = roomsMock.findOneById('GENERAL') as RoomsMock['data']['GENERAL']; + const appMessage = await roomConverter.convertRoom(mockedRoom as unknown as IRoom); + + expect(appMessage).to.have.property('_unmappedProperties_').which.has.property('lastMessage').to.deep.equal(mockedRoom.lastMessage); + }); + }); + + describe('when converting a room from the Engine schema back to Rocket.Chat', () => { + it('should return `undefined` when `room` is falsy', async () => { + const rocketchatMessage = await roomConverter.convertAppRoom(undefined); + + expect(rocketchatMessage).to.be.undefined; + }); + + it('should return a proper schema', async () => { + const appRoom = RoomsMock.convertedData.GENERAL as unknown as IAppsRoom; + const rocketchatRoom = await roomConverter.convertAppRoom(appRoom); + + expect(rocketchatRoom).to.have.property('_id', appRoom.id); + expect(rocketchatRoom).to.have.property('ts', appRoom.createdAt); + expect(rocketchatRoom).to.have.property('lm', appRoom.lastModifiedAt); + expect(rocketchatRoom).to.have.property('_updatedAt', appRoom.updatedAt); + expect(rocketchatRoom).to.have.property('t', appRoom.type); + expect(rocketchatRoom).to.have.property('name', appRoom.slugifiedName); + }); + + it('should return a proper schema when receiving a partial object', async () => { + const appRoom = RoomsMock.convertedData.GENERALPartial as unknown as IAppsRoom; + const rocketchatRoom = await roomConverter.convertAppRoom(appRoom, true); + + expect(rocketchatRoom).to.have.property('_id', appRoom.id); + expect(rocketchatRoom).to.have.property('name', appRoom.slugifiedName); + expect(rocketchatRoom).to.have.property('sysMes', appRoom.displaySystemMessages); + expect(rocketchatRoom).to.have.property('_updatedAt', appRoom.updatedAt); + + expect(rocketchatRoom).to.not.have.property('msgs'); + expect(rocketchatRoom).to.not.have.property('ro'); + expect(rocketchatRoom).to.not.have.property('default'); + }); + }); +}); From 48978a64cc2b344c63caf6369a88c018bfffeeec Mon Sep 17 00:00:00 2001 From: Douglas Gubert Date: Fri, 14 Feb 2025 18:41:15 -0300 Subject: [PATCH 12/16] Update apps/meteor/app/apps/server/converters/messages.js --- apps/meteor/app/apps/server/converters/messages.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/meteor/app/apps/server/converters/messages.js b/apps/meteor/app/apps/server/converters/messages.js index db0406a45e9b9..a3e7e6260f310 100644 --- a/apps/meteor/app/apps/server/converters/messages.js +++ b/apps/meteor/app/apps/server/converters/messages.js @@ -160,7 +160,7 @@ export class AppMessagesConverter { username: user.username, name: user.name, }; - } else if (!isPartial) { + } else { u = { _id: message.sender.id, username: message.sender.username, From dec112388270b8ef605fcd4c4b036a5b318ac088 Mon Sep 17 00:00:00 2001 From: Douglas Gubert Date: Mon, 17 Feb 2025 11:01:34 -0300 Subject: [PATCH 13/16] Add changeset --- .changeset/five-cherries-hang.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 .changeset/five-cherries-hang.md diff --git a/.changeset/five-cherries-hang.md b/.changeset/five-cherries-hang.md new file mode 100644 index 0000000000000..d306fd635a956 --- /dev/null +++ b/.changeset/five-cherries-hang.md @@ -0,0 +1,20 @@ +--- +'@rocket.chat/omnichannel-transcript': patch +'@rocket.chat/authorization-service': patch +'@rocket.chat/stream-hub-service': patch +'@rocket.chat/network-broker': patch +'@rocket.chat/presence-service': patch +'@rocket.chat/fuselage-ui-kit': patch +'@rocket.chat/account-service': patch +'@rocket.chat/ui-video-conf': patch +'@rocket.chat/core-typings': patch +'@rocket.chat/ddp-streamer': patch +'@rocket.chat/queue-worker': patch +'@rocket.chat/apps-engine': patch +'@rocket.chat/ui-client': patch +'@rocket.chat/apps': patch +'@rocket.chat/i18n': patch +'@rocket.chat/meteor': patch +--- + +Fix behavior of app updates that would save undesired field changes to documents From e31f11074c35c14d03848576d0db6d8282cd7b54 Mon Sep 17 00:00:00 2001 From: Douglas Gubert Date: Mon, 17 Feb 2025 17:26:12 -0300 Subject: [PATCH 14/16] Apply suggestions from code review Co-authored-by: Kevin Aleman --- .changeset/five-cherries-hang.md | 2 +- apps/meteor/app/apps/server/converters/messages.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.changeset/five-cherries-hang.md b/.changeset/five-cherries-hang.md index d306fd635a956..bc33eb7cf309b 100644 --- a/.changeset/five-cherries-hang.md +++ b/.changeset/five-cherries-hang.md @@ -17,4 +17,4 @@ '@rocket.chat/meteor': patch --- -Fix behavior of app updates that would save undesired field changes to documents +Fixes behavior of app updates that would save undesired field changes to documents diff --git a/apps/meteor/app/apps/server/converters/messages.js b/apps/meteor/app/apps/server/converters/messages.js index a3e7e6260f310..6ee31d58a48a4 100644 --- a/apps/meteor/app/apps/server/converters/messages.js +++ b/apps/meteor/app/apps/server/converters/messages.js @@ -142,7 +142,7 @@ export class AppMessagesConverter { let rid; if (message.room?.id) { - const room = await Rooms.findOneById(message.room.id); + const room = await Rooms.findOneById(message.room.id, { projection: {_id: 1}}); rid = room?._id; } From 024d37f4d47859a05940c83421d1bb4843c45501 Mon Sep 17 00:00:00 2001 From: Douglas Gubert Date: Wed, 19 Feb 2025 11:38:26 -0300 Subject: [PATCH 15/16] Fix lint --- apps/meteor/app/apps/server/converters/messages.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/meteor/app/apps/server/converters/messages.js b/apps/meteor/app/apps/server/converters/messages.js index d9c278c72d950..01335fb98b6f8 100644 --- a/apps/meteor/app/apps/server/converters/messages.js +++ b/apps/meteor/app/apps/server/converters/messages.js @@ -144,7 +144,7 @@ export class AppMessagesConverter { let rid; if (message.room?.id) { - const room = await Rooms.findOneById(message.room.id, { projection: {_id: 1}}); + const room = await Rooms.findOneById(message.room.id, { projection: { _id: 1 } }); rid = room?._id; } From 248e4694123fa340647d0567d371940d79bb73ad Mon Sep 17 00:00:00 2001 From: Douglas Gubert Date: Wed, 19 Feb 2025 11:42:46 -0300 Subject: [PATCH 16/16] Apply review suggestion --- apps/meteor/app/apps/server/converters/messages.js | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/apps/meteor/app/apps/server/converters/messages.js b/apps/meteor/app/apps/server/converters/messages.js index 01335fb98b6f8..8cc6f4ea270f2 100644 --- a/apps/meteor/app/apps/server/converters/messages.js +++ b/apps/meteor/app/apps/server/converters/messages.js @@ -184,7 +184,6 @@ export class AppMessagesConverter { let _id = message.id; let ts = message.createdAt; - let _updatedAt = message.updatedAt; if (!isPartial) { if (!message.id) { @@ -194,10 +193,6 @@ export class AppMessagesConverter { if (!message.createdAt) { ts = new Date(); } - - if (!message.updatedAt) { - _updatedAt = new Date(); - } } const newMessage = { @@ -207,7 +202,7 @@ export class AppMessagesConverter { u, msg: message.text, ts, - _updatedAt, + _updatedAt: message.updatedAt, ...(editedBy && { editedBy }), ...('editedAt' in message && { editedAt: message.editedAt }), ...('emoji' in message && { emoji: message.emoji }),