Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
490de6e
wip
d-gubert Feb 3, 2025
e0159f4
wip #2
d-gubert Feb 4, 2025
d5d1882
Merge remote-tracking branch 'origin/develop' into fix/apps-prevent-w…
d-gubert Feb 5, 2025
a8ea0ce
Merge remote-tracking branch 'origin/develop' into fix/apps-prevent-w…
d-gubert Feb 6, 2025
73285ca
Merge remote-tracking branch 'origin/develop' into fix/apps-prevent-w…
d-gubert Feb 6, 2025
0716349
Merge remote-tracking branch 'origin/develop' into fix/apps-prevent-w…
d-gubert Feb 10, 2025
1fd03a0
Merge remote-tracking branch 'origin/develop' into fix/apps-prevent-w…
d-gubert Feb 10, 2025
08a5768
Merge remote-tracking branch 'origin/develop' into fix/apps-prevent-w…
d-gubert Feb 11, 2025
c609e5d
Merge remote-tracking branch 'origin/develop' into fix/apps-prevent-w…
d-gubert Feb 11, 2025
f1f9126
Set editor when creating a message builder from the modifier
d-gubert Feb 11, 2025
f3de95f
Adapt room and message converters
d-gubert Feb 11, 2025
fafeb03
Adapt message and room builders
d-gubert Feb 11, 2025
0ac3118
Adapt bridge methods
d-gubert Feb 11, 2025
5e0c8ca
Merge remote-tracking branch 'origin/develop' into fix/apps-prevent-w…
d-gubert Feb 11, 2025
a7441ee
Fix types
d-gubert Feb 11, 2025
fec0bad
Merge remote-tracking branch 'origin/develop' into fix/apps-prevent-w…
d-gubert Feb 12, 2025
978be3b
Merge remote-tracking branch 'origin/develop' into fix/apps-prevent-w…
d-gubert Feb 13, 2025
12cc084
Merge remote-tracking branch 'origin/develop' into fix/apps-prevent-w…
d-gubert Feb 13, 2025
e369046
Fix tests
d-gubert Feb 13, 2025
59eb65e
Merge branch 'develop' into fix/apps-prevent-whole-update
d-gubert Feb 13, 2025
24c926d
Fix tests
d-gubert Feb 13, 2025
d53e99d
Merge branch 'develop' into fix/apps-prevent-whole-update
d-gubert Feb 13, 2025
dd802e2
No only
d-gubert Feb 13, 2025
b48912b
Merge branch 'develop' into fix/apps-prevent-whole-update
d-gubert Feb 14, 2025
5db8d27
Merge branch 'develop' into fix/apps-prevent-whole-update
d-gubert Feb 14, 2025
effafac
Merge branch 'develop' into fix/apps-prevent-whole-update
d-gubert Feb 14, 2025
46ee12c
Merge branch 'develop' into fix/apps-prevent-whole-update
d-gubert Feb 14, 2025
50aa8ef
Merge branch 'develop' into fix/apps-prevent-whole-update
d-gubert Feb 14, 2025
b3e9d22
Add room converter tests
d-gubert Feb 14, 2025
48978a6
Update apps/meteor/app/apps/server/converters/messages.js
d-gubert Feb 14, 2025
dec1123
Add changeset
d-gubert Feb 17, 2025
e31f110
Apply suggestions from code review
d-gubert Feb 17, 2025
5cc644a
Merge branch 'develop' into fix/apps-prevent-whole-update
d-gubert Feb 17, 2025
e33d7c4
Merge branch 'develop' into fix/apps-prevent-whole-update
KevLehman Feb 19, 2025
024d37f
Fix lint
d-gubert Feb 19, 2025
248e469
Apply review suggestion
d-gubert Feb 19, 2025
576329a
Merge branch 'develop' into fix/apps-prevent-whole-update
d-gubert Feb 19, 2025
af4a541
Merge branch 'develop' into fix/apps-prevent-whole-update
kodiakhq[bot] Feb 19, 2025
3d1c334
Merge branch 'develop' into fix/apps-prevent-whole-update
kodiakhq[bot] Feb 20, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions .changeset/five-cherries-hang.md
Original file line number Diff line number Diff line change
@@ -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
---

Fixes behavior of app updates that would save undesired field changes to documents
8 changes: 2 additions & 6 deletions apps/meteor/app/apps/server/bridges/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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 = await this.orch.getConverters()?.get('messages').convertAppMessage(message, true);
const editor = await Users.findOneById(message.editor.id);

if (!editor) {
Expand Down
12 changes: 6 additions & 6 deletions apps/meteor/app/apps/server/bridges/rooms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,13 +163,13 @@ export class AppRoomBridge extends RoomBridge {
protected async update(room: IRoom, members: Array<string> = [], appId: string): Promise<void> {
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<ICoreRoom> });
if (!updateResult.matchedCount) {
throw new Error('Room id not found');
}

for await (const username of members) {
const member = await Users.findOneByUsername(username, {});
Expand All @@ -178,7 +178,7 @@ export class AppRoomBridge extends RoomBridge {
continue;
}

await addUserToRoom(rm._id, member);
await addUserToRoom(room.id, member);
}
}

Expand Down
47 changes: 37 additions & 10 deletions apps/meteor/app/apps/server/converters/messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -137,19 +137,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, { projection: { _id: 1 } });
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) {
Expand Down Expand Up @@ -178,14 +182,27 @@ export class AppMessagesConverter {

const attachments = this._convertAppAttachments(message.attachments);

let _id = message.id;
let ts = message.createdAt;

if (!isPartial) {
if (!message.id) {
_id = Random.id();
}

if (!message.createdAt) {
ts = 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: message.updatedAt,
...(editedBy && { editedBy }),
...('editedAt' in message && { editedAt: message.editedAt }),
...('emoji' in message && { emoji: message.emoji }),
Expand All @@ -200,7 +217,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) {
Expand Down
43 changes: 37 additions & 6 deletions apps/meteor/app/apps/server/converters/rooms.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export class AppRoomsConverter {
return this.convertRoom(room);
}

async convertAppRoom(room) {
async convertAppRoom(room, isPartial = false) {
if (!room) {
return undefined;
}
Expand Down Expand Up @@ -88,24 +88,44 @@ 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,
name: room.slugifiedName,
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,
Expand All @@ -122,7 +142,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) {
Expand Down Expand Up @@ -245,6 +275,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) => {
Expand Down
28 changes: 27 additions & 1 deletion apps/meteor/tests/unit/app/apps/server/messages.tests.js
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -134,13 +134,39 @@ 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);

expect(rocketchatMessage).not.to.have.property('_unmappedProperties_');
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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Loading
Loading