From 63f7e8fea6ef939455193e8e8661ea1197d410f9 Mon Sep 17 00:00:00 2001 From: Marcos Spessatto Defendi Date: Sat, 20 Oct 2018 16:31:56 -0300 Subject: [PATCH] [BREAK] Update `lastMessage` rooms property and convert the "starred" property, to the same format (#12266) * Update room last message object with reaction, star, pin and snippet * Add same field format: "starred" in all responses that includes "messages" or "lastMessage" objects * Add tests cases for message format --- packages/rocketchat-api/package.js | 1 + .../helpers/composeRoomWithLastMessage.js | 6 + packages/rocketchat-api/server/v1/channels.js | 55 ++++--- packages/rocketchat-api/server/v1/chat.js | 17 +- packages/rocketchat-api/server/v1/groups.js | 26 ++-- packages/rocketchat-api/server/v1/im.js | 8 +- packages/rocketchat-api/server/v1/rooms.js | 5 +- packages/rocketchat-lib/package.js | 2 + .../functions/composeMessageObjectWithUser.js | 21 +++ .../server/functions/isTheLastMessage.js | 1 + .../server/methods/getChannelHistory.js | 17 +- packages/rocketchat-message-pin/package.js | 1 + .../server/models/Rooms.js | 13 ++ .../server/pinMessage.js | 9 +- .../rocketchat-message-snippet/package.js | 1 + .../server/methods/snippetMessage.js | 4 + .../server/models/Rooms.js | 17 ++ packages/rocketchat-message-star/package.js | 1 + .../server/models/Rooms.js | 21 +++ .../server/starMessage.js | 4 + packages/rocketchat-reactions/package.js | 1 + .../server/models/Rooms.js | 7 + packages/rocketchat-reactions/setReaction.js | 10 +- tests/end-to-end/api/02-channels.js | 147 ++++++++++++++++-- tests/end-to-end/api/03-groups.js | 146 +++++++++++++++-- tests/end-to-end/api/04-direct-message.js | 106 ++++++++++++- 26 files changed, 541 insertions(+), 106 deletions(-) create mode 100644 packages/rocketchat-api/server/helpers/composeRoomWithLastMessage.js create mode 100644 packages/rocketchat-lib/server/functions/composeMessageObjectWithUser.js create mode 100644 packages/rocketchat-lib/server/functions/isTheLastMessage.js create mode 100644 packages/rocketchat-message-pin/server/models/Rooms.js create mode 100644 packages/rocketchat-message-snippet/server/models/Rooms.js create mode 100644 packages/rocketchat-message-star/server/models/Rooms.js create mode 100644 packages/rocketchat-reactions/server/models/Rooms.js diff --git a/packages/rocketchat-api/package.js b/packages/rocketchat-api/package.js index cd9231aeae45..7101ecc7bf10 100644 --- a/packages/rocketchat-api/package.js +++ b/packages/rocketchat-api/package.js @@ -16,6 +16,7 @@ Package.onUse(function(api) { api.addFiles('server/settings.js', 'server'); // Register helpers + api.addFiles('server/helpers/composeRoomWithLastMessage.js', 'server'); api.addFiles('server/helpers/requestParams.js', 'server'); api.addFiles('server/helpers/getPaginationItems.js', 'server'); api.addFiles('server/helpers/getUserFromParams.js', 'server'); diff --git a/packages/rocketchat-api/server/helpers/composeRoomWithLastMessage.js b/packages/rocketchat-api/server/helpers/composeRoomWithLastMessage.js new file mode 100644 index 000000000000..34f38bb675a5 --- /dev/null +++ b/packages/rocketchat-api/server/helpers/composeRoomWithLastMessage.js @@ -0,0 +1,6 @@ +RocketChat.API.helperMethods.set('composeRoomWithLastMessage', function _composeRoomWithLastMessage(room, userId) { + if (room.lastMessage) { + room.lastMessage = RocketChat.composeMessageObjectWithUser(room.lastMessage, userId); + } + return room; +}); diff --git a/packages/rocketchat-api/server/v1/channels.js b/packages/rocketchat-api/server/v1/channels.js index 784a763bfb3f..2a6416650b8c 100644 --- a/packages/rocketchat-api/server/v1/channels.js +++ b/packages/rocketchat-api/server/v1/channels.js @@ -1,7 +1,7 @@ import _ from 'underscore'; // Returns the channel IF found otherwise it will return the failure of why it didn't. Check the `statusCode` property -function findChannelByIdOrName({ params, checkedArchived = true }) { +function findChannelByIdOrName({ params, checkedArchived = true, userId }) { if ((!params.roomId || !params.roomId.trim()) && (!params.roomName || !params.roomName.trim())) { throw new Meteor.Error('error-roomid-param-not-provided', 'The parameter "roomId" or "roomName" is required'); } @@ -22,6 +22,9 @@ function findChannelByIdOrName({ params, checkedArchived = true }) { if (checkedArchived && room.archived) { throw new Meteor.Error('error-room-archived', `The channel, ${ room.name }, is archived`); } + if (userId && room.lastMessage) { + room.lastMessage = RocketChat.composeMessageObjectWithUser(room.lastMessage, userId); + } return room; } @@ -35,7 +38,7 @@ RocketChat.API.v1.addRoute('channels.addAll', { authRequired: true }, { }); return RocketChat.API.v1.success({ - channel: RocketChat.models.Rooms.findOneById(findResult._id, { fields: RocketChat.API.v1.defaultFieldsToExclude }), + channel: findChannelByIdOrName({ params: this.requestParams(), userId: this.userId }), }); }, }); @@ -175,14 +178,10 @@ function createChannelValidator(params) { function createChannel(userId, params) { const readOnly = typeof params.readOnly !== 'undefined' ? params.readOnly : false; - - let id; - Meteor.runAsUser(userId, () => { - id = Meteor.call('createChannel', params.name, params.members ? params.members : [], readOnly, params.customFields); - }); + const id = Meteor.runAsUser(userId, () => Meteor.call('createChannel', params.name, params.members ? params.members : [], readOnly, params.customFields)); return { - channel: RocketChat.models.Rooms.findOneById(id.rid, { fields: RocketChat.API.v1.defaultFieldsToExclude }), + channel: findChannelByIdOrName({ params: { roomId: id.rid }, userId: this.userId }), }; } @@ -236,9 +235,7 @@ RocketChat.API.v1.addRoute('channels.delete', { authRequired: true }, { Meteor.call('eraseRoom', findResult._id); }); - return RocketChat.API.v1.success({ - channel: findResult, - }); + return RocketChat.API.v1.success(); }, }); @@ -367,10 +364,12 @@ RocketChat.API.v1.addRoute('channels.history', { authRequired: true }, { RocketChat.API.v1.addRoute('channels.info', { authRequired: true }, { get() { - const findResult = findChannelByIdOrName({ params: this.requestParams(), checkedArchived: false }); - return RocketChat.API.v1.success({ - channel: RocketChat.models.Rooms.findOneById(findResult._id, { fields: RocketChat.API.v1.defaultFieldsToExclude }), + channel: findChannelByIdOrName({ + params: this.requestParams(), + checkedArchived: false, + userId: this.userId, + }), }); }, }); @@ -386,7 +385,7 @@ RocketChat.API.v1.addRoute('channels.invite', { authRequired: true }, { }); return RocketChat.API.v1.success({ - channel: RocketChat.models.Rooms.findOneById(findResult._id, { fields: RocketChat.API.v1.defaultFieldsToExclude }), + channel: findChannelByIdOrName({ params: this.requestParams(), userId: this.userId }), }); }, }); @@ -400,7 +399,7 @@ RocketChat.API.v1.addRoute('channels.join', { authRequired: true }, { }); return RocketChat.API.v1.success({ - channel: RocketChat.models.Rooms.findOneById(findResult._id, { fields: RocketChat.API.v1.defaultFieldsToExclude }), + channel: findChannelByIdOrName({ params: this.requestParams(), userId: this.userId }), }); }, }); @@ -416,7 +415,7 @@ RocketChat.API.v1.addRoute('channels.kick', { authRequired: true }, { }); return RocketChat.API.v1.success({ - channel: RocketChat.models.Rooms.findOneById(findResult._id, { fields: RocketChat.API.v1.defaultFieldsToExclude }), + channel: findChannelByIdOrName({ params: this.requestParams(), userId: this.userId }), }); }, }); @@ -430,7 +429,7 @@ RocketChat.API.v1.addRoute('channels.leave', { authRequired: true }, { }); return RocketChat.API.v1.success({ - channel: RocketChat.models.Rooms.findOneById(findResult._id, { fields: RocketChat.API.v1.defaultFieldsToExclude }), + channel: findChannelByIdOrName({ params: this.requestParams(), userId: this.userId }), }); }, }); @@ -465,7 +464,7 @@ RocketChat.API.v1.addRoute('channels.list', { authRequired: true }, { const rooms = cursor.fetch(); return RocketChat.API.v1.success({ - channels: rooms, + channels: rooms.map((room) => this.composeRoomWithLastMessage(room, this.userId)), count: rooms.length, offset, total, @@ -491,7 +490,7 @@ RocketChat.API.v1.addRoute('channels.list.joined', { authRequired: true }, { const rooms = cursor.fetch(); return RocketChat.API.v1.success({ - channels: rooms, + channels: rooms.map((room) => this.composeRoomWithLastMessage(room, this.userId)), offset, count: rooms.length, total: totalCount, @@ -526,7 +525,7 @@ RocketChat.API.v1.addRoute('channels.members', { authRequired: true }, { const users = RocketChat.models.Users.find({ _id: { $in: members } }, { fields: { _id: 1, username: 1, name: 1, status: 1, utcOffset: 1 }, - sort: { username: sort.username != null ? sort.username : 1 }, + sort: { username: sort.username != null ? sort.username : 1 }, }).fetch(); return RocketChat.API.v1.success({ @@ -568,7 +567,7 @@ RocketChat.API.v1.addRoute('channels.messages', { authRequired: true }, { const messages = cursor.fetch(); return RocketChat.API.v1.success({ - messages, + messages: messages.map((record) => RocketChat.composeMessageObjectWithUser(record, this.userId)), count: messages.length, offset, total, @@ -703,7 +702,7 @@ RocketChat.API.v1.addRoute('channels.rename', { authRequired: true }, { }); return RocketChat.API.v1.success({ - channel: RocketChat.models.Rooms.findOneById(findResult._id, { fields: RocketChat.API.v1.defaultFieldsToExclude }), + channel: findChannelByIdOrName({ params: { roomId: this.bodyParams.roomId }, userId: this.userId }), }); }, }); @@ -721,7 +720,7 @@ RocketChat.API.v1.addRoute('channels.setCustomFields', { authRequired: true }, { }); return RocketChat.API.v1.success({ - channel: RocketChat.models.Rooms.findOneById(findResult._id, { fields: RocketChat.API.v1.defaultFieldsToExclude }), + channel: findChannelByIdOrName({ params: this.requestParams(), userId: this.userId }), }); }, }); @@ -743,7 +742,7 @@ RocketChat.API.v1.addRoute('channels.setDefault', { authRequired: true }, { }); return RocketChat.API.v1.success({ - channel: RocketChat.models.Rooms.findOneById(findResult._id, { fields: RocketChat.API.v1.defaultFieldsToExclude }), + channel: findChannelByIdOrName({ params: this.requestParams(), userId: this.userId }), }); }, }); @@ -783,7 +782,7 @@ RocketChat.API.v1.addRoute('channels.setJoinCode', { authRequired: true }, { }); return RocketChat.API.v1.success({ - channel: RocketChat.models.Rooms.findOneById(findResult._id, { fields: RocketChat.API.v1.defaultFieldsToExclude }), + channel: findChannelByIdOrName({ params: this.requestParams(), userId: this.userId }), }); }, }); @@ -827,7 +826,7 @@ RocketChat.API.v1.addRoute('channels.setReadOnly', { authRequired: true }, { }); return RocketChat.API.v1.success({ - channel: RocketChat.models.Rooms.findOneById(findResult._id, { fields: RocketChat.API.v1.defaultFieldsToExclude }), + channel: findChannelByIdOrName({ params: this.requestParams(), userId: this.userId }), }); }, }); @@ -889,7 +888,7 @@ RocketChat.API.v1.addRoute('channels.setType', { authRequired: true }, { }); return RocketChat.API.v1.success({ - channel: RocketChat.models.Rooms.findOneById(findResult._id, { fields: RocketChat.API.v1.defaultFieldsToExclude }), + channel: this.composeRoomWithLastMessage(RocketChat.models.Rooms.findOneById(findResult._id, { fields: RocketChat.API.v1.defaultFieldsToExclude }), this.userId), }); }, }); diff --git a/packages/rocketchat-api/server/v1/chat.js b/packages/rocketchat-api/server/v1/chat.js index 059d6058eca9..1d3e8621cad5 100644 --- a/packages/rocketchat-api/server/v1/chat.js +++ b/packages/rocketchat-api/server/v1/chat.js @@ -58,7 +58,10 @@ RocketChat.API.v1.addRoute('chat.syncMessages', { authRequired: true }, { } return RocketChat.API.v1.success({ - result, + result: { + updated: result.updated.map((message) => RocketChat.composeMessageObjectWithUser(message, this.userId)), + deleted: result.deleted.map((message) => RocketChat.composeMessageObjectWithUser(message, this.userId)), + }, }); }, }); @@ -79,7 +82,7 @@ RocketChat.API.v1.addRoute('chat.getMessage', { authRequired: true }, { } return RocketChat.API.v1.success({ - message: msg, + message: RocketChat.composeMessageObjectWithUser(msg, this.userId), }); }, }); @@ -100,7 +103,7 @@ RocketChat.API.v1.addRoute('chat.pinMessage', { authRequired: true }, { Meteor.runAsUser(this.userId, () => pinnedMessage = Meteor.call('pinMessage', msg)); return RocketChat.API.v1.success({ - message: pinnedMessage, + message: RocketChat.composeMessageObjectWithUser(pinnedMessage, this.userId), }); }, }); @@ -116,7 +119,7 @@ RocketChat.API.v1.addRoute('chat.postMessage', { authRequired: true }, { return RocketChat.API.v1.success({ ts: Date.now(), channel: messageReturn.channel, - message: messageReturn.message, + message: RocketChat.composeMessageObjectWithUser(messageReturn.message, this.userId), }); }, }); @@ -138,7 +141,7 @@ RocketChat.API.v1.addRoute('chat.search', { authRequired: true }, { Meteor.runAsUser(this.userId, () => result = Meteor.call('messageSearch', searchText, roomId, count).message.docs); return RocketChat.API.v1.success({ - messages: result, + messages: result.map((message) => RocketChat.composeMessageObjectWithUser(message, this.userId)), }); }, }); @@ -156,7 +159,7 @@ RocketChat.API.v1.addRoute('chat.sendMessage', { authRequired: true }, { Meteor.runAsUser(this.userId, () => message = Meteor.call('sendMessage', this.bodyParams.message)); return RocketChat.API.v1.success({ - message, + message: RocketChat.composeMessageObjectWithUser(message, this.userId), }); }, }); @@ -248,7 +251,7 @@ RocketChat.API.v1.addRoute('chat.update', { authRequired: true }, { }); return RocketChat.API.v1.success({ - message: RocketChat.models.Messages.findOneById(msg._id), + message: RocketChat.composeMessageObjectWithUser(RocketChat.models.Messages.findOneById(msg._id), this.userId), }); }, }); diff --git a/packages/rocketchat-api/server/v1/groups.js b/packages/rocketchat-api/server/v1/groups.js index 2944e8f1c1f6..bfb6c67fc96b 100644 --- a/packages/rocketchat-api/server/v1/groups.js +++ b/packages/rocketchat-api/server/v1/groups.js @@ -33,7 +33,7 @@ RocketChat.API.v1.addRoute('groups.addAll', { authRequired: true }, { }); return RocketChat.API.v1.success({ - group: RocketChat.models.Rooms.findOneById(findResult.rid, { fields: RocketChat.API.v1.defaultFieldsToExclude }), + group: this.composeRoomWithLastMessage(RocketChat.models.Rooms.findOneById(findResult.rid, { fields: RocketChat.API.v1.defaultFieldsToExclude }), this.userId), }); }, }); @@ -202,7 +202,7 @@ RocketChat.API.v1.addRoute('groups.create', { authRequired: true }, { }); return RocketChat.API.v1.success({ - group: RocketChat.models.Rooms.findOneById(id.rid, { fields: RocketChat.API.v1.defaultFieldsToExclude }), + group: this.composeRoomWithLastMessage(RocketChat.models.Rooms.findOneById(id.rid, { fields: RocketChat.API.v1.defaultFieldsToExclude }), this.userId), }); }, }); @@ -215,9 +215,7 @@ RocketChat.API.v1.addRoute('groups.delete', { authRequired: true }, { Meteor.call('eraseRoom', findResult.rid); }); - return RocketChat.API.v1.success({ - group: RocketChat.models.Rooms.findOneById(findResult.rid, { fields: RocketChat.API.v1.defaultFieldsToExclude }), - }); + return RocketChat.API.v1.success(); }, }); @@ -331,7 +329,7 @@ RocketChat.API.v1.addRoute('groups.info', { authRequired: true }, { const findResult = findPrivateGroupByIdOrName({ params: this.requestParams(), userId: this.userId, checkedArchived: false }); return RocketChat.API.v1.success({ - group: RocketChat.models.Rooms.findOneById(findResult.rid, { fields: RocketChat.API.v1.defaultFieldsToExclude }), + group: this.composeRoomWithLastMessage(RocketChat.models.Rooms.findOneById(findResult.rid, { fields: RocketChat.API.v1.defaultFieldsToExclude }), this.userId), }); }, }); @@ -355,7 +353,7 @@ RocketChat.API.v1.addRoute('groups.invite', { authRequired: true }, { Meteor.runAsUser(this.userId, () => Meteor.call('addUserToRoom', { rid, username })); return RocketChat.API.v1.success({ - group: RocketChat.models.Rooms.findOneById(rid, { fields: RocketChat.API.v1.defaultFieldsToExclude }), + group: this.composeRoomWithLastMessage(RocketChat.models.Rooms.findOneById(rid, { fields: RocketChat.API.v1.defaultFieldsToExclude }), this.userId), }); }, }); @@ -405,7 +403,7 @@ RocketChat.API.v1.addRoute('groups.list', { authRequired: true }, { return RocketChat.API.v1.success({ - groups: rooms, + groups: rooms.map((room) => this.composeRoomWithLastMessage(room, this.userId)), offset, count: rooms.length, total: totalCount, @@ -434,7 +432,7 @@ RocketChat.API.v1.addRoute('groups.listAll', { authRequired: true }, { }); return RocketChat.API.v1.success({ - groups: rooms, + groups: rooms.map((room) => this.composeRoomWithLastMessage(room, this.userId)), offset, count: rooms.length, total: totalCount, @@ -495,7 +493,7 @@ RocketChat.API.v1.addRoute('groups.messages', { authRequired: true }, { }).fetch(); return RocketChat.API.v1.success({ - messages, + messages: messages.map((message) => RocketChat.composeMessageObjectWithUser(message, this.userId)), count: messages.length, offset, total: RocketChat.models.Messages.find(ourQuery).count(), @@ -608,7 +606,7 @@ RocketChat.API.v1.addRoute('groups.rename', { authRequired: true }, { }); return RocketChat.API.v1.success({ - group: RocketChat.models.Rooms.findOneById(findResult.rid, { fields: RocketChat.API.v1.defaultFieldsToExclude }), + group: this.composeRoomWithLastMessage(RocketChat.models.Rooms.findOneById(findResult.rid, { fields: RocketChat.API.v1.defaultFieldsToExclude }), this.userId), }); }, }); @@ -626,7 +624,7 @@ RocketChat.API.v1.addRoute('groups.setCustomFields', { authRequired: true }, { }); return RocketChat.API.v1.success({ - group: RocketChat.models.Rooms.findOneById(findResult.rid, { fields: RocketChat.API.v1.defaultFieldsToExclude }), + group: this.composeRoomWithLastMessage(RocketChat.models.Rooms.findOneById(findResult.rid, { fields: RocketChat.API.v1.defaultFieldsToExclude }), this.userId), }); }, }); @@ -684,7 +682,7 @@ RocketChat.API.v1.addRoute('groups.setReadOnly', { authRequired: true }, { }); return RocketChat.API.v1.success({ - group: RocketChat.models.Rooms.findOneById(findResult.rid, { fields: RocketChat.API.v1.defaultFieldsToExclude }), + group: this.composeRoomWithLastMessage(RocketChat.models.Rooms.findOneById(findResult.rid, { fields: RocketChat.API.v1.defaultFieldsToExclude }), this.userId), }); }, }); @@ -724,7 +722,7 @@ RocketChat.API.v1.addRoute('groups.setType', { authRequired: true }, { }); return RocketChat.API.v1.success({ - group: RocketChat.models.Rooms.findOneById(findResult.rid, { fields: RocketChat.API.v1.defaultFieldsToExclude }), + group: this.composeRoomWithLastMessage(RocketChat.models.Rooms.findOneById(findResult.rid, { fields: RocketChat.API.v1.defaultFieldsToExclude }), this.userId), }); }, }); diff --git a/packages/rocketchat-api/server/v1/im.js b/packages/rocketchat-api/server/v1/im.js index ff728e6e63dc..35ad3849d9db 100644 --- a/packages/rocketchat-api/server/v1/im.js +++ b/packages/rocketchat-api/server/v1/im.js @@ -219,7 +219,7 @@ RocketChat.API.v1.addRoute(['dm.messages', 'im.messages'], { authRequired: true }).fetch(); return RocketChat.API.v1.success({ - messages, + messages: messages.map((message) => RocketChat.composeMessageObjectWithUser(message, this.userId)), count: messages.length, offset, total: RocketChat.models.Messages.find(ourQuery).count(), @@ -259,7 +259,7 @@ RocketChat.API.v1.addRoute(['dm.messages.others', 'im.messages.others'], { authR }).fetch(); return RocketChat.API.v1.success({ - messages: msgs, + messages: msgs.map((message) => RocketChat.composeMessageObjectWithUser(message, this.userId)), offset, count: msgs.length, total: RocketChat.models.Messages.find(ourQuery).count(), @@ -285,7 +285,7 @@ RocketChat.API.v1.addRoute(['dm.list', 'im.list'], { authRequired: true }, { const rooms = cursor.fetch(); return RocketChat.API.v1.success({ - ims: rooms, + ims: rooms.map((room) => this.composeRoomWithLastMessage(room, this.userId)), offset, count: rooms.length, total, @@ -312,7 +312,7 @@ RocketChat.API.v1.addRoute(['dm.list.everyone', 'im.list.everyone'], { authRequi }).fetch(); return RocketChat.API.v1.success({ - ims: rooms, + ims: rooms.map((room) => this.composeRoomWithLastMessage(room, this.userId)), offset, count: rooms.length, total: RocketChat.models.Rooms.find(ourQuery).count(), diff --git a/packages/rocketchat-api/server/v1/rooms.js b/packages/rocketchat-api/server/v1/rooms.js index 0071953d414b..b758901385a3 100644 --- a/packages/rocketchat-api/server/v1/rooms.js +++ b/packages/rocketchat-api/server/v1/rooms.js @@ -46,7 +46,10 @@ RocketChat.API.v1.addRoute('rooms.get', { authRequired: true }, { }; } - return RocketChat.API.v1.success(result); + return RocketChat.API.v1.success({ + update: result.update.map((room) => this.composeRoomWithLastMessage(room, this.userId)), + remove: result.remove.map((room) => this.composeRoomWithLastMessage(room, this.userId)), + }); }, }); diff --git a/packages/rocketchat-lib/package.js b/packages/rocketchat-lib/package.js index a31e5db8fa94..7f3e15c5fd0c 100644 --- a/packages/rocketchat-lib/package.js +++ b/packages/rocketchat-lib/package.js @@ -82,11 +82,13 @@ Package.onUse(function(api) { // SERVER FUNCTIONS api.addFiles('server/functions/isDocker.js', 'server'); + api.addFiles('server/functions/isTheLastMessage.js', 'server'); api.addFiles('server/functions/addUserToDefaultChannels.js', 'server'); api.addFiles('server/functions/addUserToRoom.js', 'server'); api.addFiles('server/functions/archiveRoom.js', 'server'); api.addFiles('server/functions/checkUsernameAvailability.js', 'server'); api.addFiles('server/functions/checkEmailAvailability.js', 'server'); + api.addFiles('server/functions/composeMessageObjectWithUser.js', 'server'); api.addFiles('server/functions/createRoom.js', 'server'); api.addFiles('server/functions/cleanRoomHistory.js', 'server'); api.addFiles('server/functions/deleteMessage.js', 'server'); diff --git a/packages/rocketchat-lib/server/functions/composeMessageObjectWithUser.js b/packages/rocketchat-lib/server/functions/composeMessageObjectWithUser.js new file mode 100644 index 000000000000..25b374f57bad --- /dev/null +++ b/packages/rocketchat-lib/server/functions/composeMessageObjectWithUser.js @@ -0,0 +1,21 @@ +const UI_Use_Real_Name = RocketChat.settings.get('UI_Use_Real_Name') === true; +const getUser = (userId) => RocketChat.models.Users.findOneById(userId); + +RocketChat.composeMessageObjectWithUser = function(message, userId) { + if (message) { + if (message.starred && Array.isArray(message.starred)) { + message.starred = message.starred.find((star) => star._id === userId); + } + if (message.u && message.u._id && UI_Use_Real_Name) { + const user = getUser(message.u._id); + message.u.name = user && user.name; + } + if (message.mentions && message.mentions.length && UI_Use_Real_Name) { + message.mentions.forEach((mention) => { + const user = getUser(mention._id); + mention.name = user && user.name; + }); + } + } + return message; +}; diff --git a/packages/rocketchat-lib/server/functions/isTheLastMessage.js b/packages/rocketchat-lib/server/functions/isTheLastMessage.js new file mode 100644 index 000000000000..c2bc6d0717f2 --- /dev/null +++ b/packages/rocketchat-lib/server/functions/isTheLastMessage.js @@ -0,0 +1 @@ +RocketChat.isTheLastMessage = (room, message) => RocketChat.settings.get('Store_Last_Message') && (!room.lastMessage || room.lastMessage._id === message._id); diff --git a/packages/rocketchat-lib/server/methods/getChannelHistory.js b/packages/rocketchat-lib/server/methods/getChannelHistory.js index 440fd1e63a24..72108ae707b0 100644 --- a/packages/rocketchat-lib/server/methods/getChannelHistory.js +++ b/packages/rocketchat-lib/server/methods/getChannelHistory.js @@ -51,22 +51,7 @@ Meteor.methods({ records = RocketChat.models.Messages.findVisibleByRoomIdBetweenTimestamps(rid, oldest, latest, options).fetch(); } - const UI_Use_Real_Name = RocketChat.settings.get('UI_Use_Real_Name') === true; - - const messages = _.map(records, (message) => { - message.starred = _.findWhere(message.starred, { _id: fromUserId }); - if (message.u && message.u._id && UI_Use_Real_Name) { - const user = RocketChat.models.Users.findOneById(message.u._id); - message.u.name = user && user.name; - } - if (message.mentions && message.mentions.length && UI_Use_Real_Name) { - message.mentions.forEach((mention) => { - const user = RocketChat.models.Users.findOneById(mention._id); - mention.name = user && user.name; - }); - } - return message; - }); + const messages = records.map((record) => RocketChat.composeMessageObjectWithUser(record, fromUserId)); if (unreads) { let unreadNotLoaded = 0; diff --git a/packages/rocketchat-message-pin/package.js b/packages/rocketchat-message-pin/package.js index 5fa6da757927..a31b1efc4851 100644 --- a/packages/rocketchat-message-pin/package.js +++ b/packages/rocketchat-message-pin/package.js @@ -25,6 +25,7 @@ Package.onUse(function(api) { ], 'client'); api.addFiles([ + 'server/models/Rooms.js', 'server/settings.js', 'server/pinMessage.js', 'server/publications/pinnedMessages.js', diff --git a/packages/rocketchat-message-pin/server/models/Rooms.js b/packages/rocketchat-message-pin/server/models/Rooms.js new file mode 100644 index 000000000000..6cdf5e14c8be --- /dev/null +++ b/packages/rocketchat-message-pin/server/models/Rooms.js @@ -0,0 +1,13 @@ +RocketChat.models.Rooms.setLastMessagePinned = function(roomId, pinnedBy, pinned, pinnedAt) { + const query = { _id: roomId }; + + const update = { + $set: { + 'lastMessage.pinned': pinned, + 'lastMessage.pinnedAt': pinnedAt || new Date, + 'lastMessage.pinnedBy': pinnedBy, + }, + }; + + return this.update(query, update); +}; diff --git a/packages/rocketchat-message-pin/server/pinMessage.js b/packages/rocketchat-message-pin/server/pinMessage.js index 497662b5f0cf..2abd64bda757 100644 --- a/packages/rocketchat-message-pin/server/pinMessage.js +++ b/packages/rocketchat-message-pin/server/pinMessage.js @@ -50,7 +50,7 @@ Meteor.methods({ if (RocketChat.settings.get('Message_KeepHistory')) { RocketChat.models.Messages.cloneAndSaveAsHistoryById(message._id); } - + const room = Meteor.call('canAccessRoom', message.rid, Meteor.userId()); const me = RocketChat.models.Users.findOneById(userId); originalMessage.pinned = true; @@ -63,6 +63,9 @@ Meteor.methods({ originalMessage = RocketChat.callbacks.run('beforeSaveMessage', originalMessage); RocketChat.models.Messages.setPinnedByIdAndUserId(originalMessage._id, originalMessage.pinnedBy, originalMessage.pinned); + if (RocketChat.isTheLastMessage(room, message)) { + RocketChat.models.Rooms.setLastMessagePinned(room._id, originalMessage.pinnedBy, originalMessage.pinned); + } const attachments = []; @@ -134,6 +137,10 @@ Meteor.methods({ username: me.username, }; originalMessage = RocketChat.callbacks.run('beforeSaveMessage', originalMessage); + const room = Meteor.call('canAccessRoom', message.rid, Meteor.userId()); + if (RocketChat.isTheLastMessage(room, message)) { + RocketChat.models.Rooms.setLastMessagePinned(room._id, originalMessage.pinnedBy, originalMessage.pinned); + } return RocketChat.models.Messages.setPinnedByIdAndUserId(originalMessage._id, originalMessage.pinnedBy, originalMessage.pinned); }, diff --git a/packages/rocketchat-message-snippet/package.js b/packages/rocketchat-message-snippet/package.js index e342ecca1c3a..5292dba032cb 100644 --- a/packages/rocketchat-message-snippet/package.js +++ b/packages/rocketchat-message-snippet/package.js @@ -28,6 +28,7 @@ Package.onUse(function(api) { // Server api.addFiles([ 'server/startup/settings.js', + 'server/models/Rooms.js', 'server/methods/snippetMessage.js', 'server/requests.js', 'server/publications/snippetedMessagesByRoom.js', diff --git a/packages/rocketchat-message-snippet/server/methods/snippetMessage.js b/packages/rocketchat-message-snippet/server/methods/snippetMessage.js index bf4cf3156f43..ff7a6e0b476f 100644 --- a/packages/rocketchat-message-snippet/server/methods/snippetMessage.js +++ b/packages/rocketchat-message-snippet/server/methods/snippetMessage.js @@ -36,6 +36,10 @@ Meteor.methods({ // Create the SnippetMessage RocketChat.models.Messages.setSnippetedByIdAndUserId(message, filename, message.snippetedBy, message.snippeted, Date.now, filename); + if (RocketChat.isTheLastMessage(room, message)) { + RocketChat.models.Rooms.setLastMessageSnippeted(room._id, message, filename, message.snippetedBy, + message.snippeted, Date.now, filename); + } RocketChat.models.Messages.createWithTypeRoomIdMessageAndUser( 'message_snippeted', message.rid, '', me, { snippetId: message._id, snippetName: filename }); diff --git a/packages/rocketchat-message-snippet/server/models/Rooms.js b/packages/rocketchat-message-snippet/server/models/Rooms.js new file mode 100644 index 000000000000..907c911531df --- /dev/null +++ b/packages/rocketchat-message-snippet/server/models/Rooms.js @@ -0,0 +1,17 @@ +RocketChat.models.Rooms.setLastMessageSnippeted = function(roomId, message, snippetName, snippetedBy, snippeted, snippetedAt) { + const query = { _id: roomId }; + + const msg = `\`\`\`${ message.msg }\`\`\``; + + const update = { + $set: { + 'lastMessage.msg': msg, + 'lastMessage.snippeted': snippeted, + 'lastMessage.snippetedAt': snippetedAt || new Date, + 'lastMessage.snippetedBy': snippetedBy, + 'lastMessage.snippetName': snippetName, + }, + }; + + return this.update(query, update); +}; diff --git a/packages/rocketchat-message-star/package.js b/packages/rocketchat-message-star/package.js index edcc39d9d13f..1a8f1bbe5b78 100644 --- a/packages/rocketchat-message-star/package.js +++ b/packages/rocketchat-message-star/package.js @@ -25,6 +25,7 @@ Package.onUse(function(api) { ], 'client'); api.addFiles([ + 'server/models/Rooms.js', 'server/settings.js', 'server/starMessage.js', 'server/publications/starredMessages.js', diff --git a/packages/rocketchat-message-star/server/models/Rooms.js b/packages/rocketchat-message-star/server/models/Rooms.js new file mode 100644 index 000000000000..bd457526d6f7 --- /dev/null +++ b/packages/rocketchat-message-star/server/models/Rooms.js @@ -0,0 +1,21 @@ +RocketChat.models.Rooms.updateLastMessageStar = function(roomId, userId, starred) { + let update; + const query = { _id: roomId }; + + if (starred) { + update = { + $addToSet: { + 'lastMessage.starred': { _id: userId }, + }, + }; + } else { + update = { + $pull: { + 'lastMessage.starred': { _id: userId }, + }, + }; + } + + return this.update(query, update); +}; + diff --git a/packages/rocketchat-message-star/server/starMessage.js b/packages/rocketchat-message-star/server/starMessage.js index 1bacf3673cb9..1b64fadb9b5c 100644 --- a/packages/rocketchat-message-star/server/starMessage.js +++ b/packages/rocketchat-message-star/server/starMessage.js @@ -17,6 +17,10 @@ Meteor.methods({ if (!subscription) { return false; } + const room = Meteor.call('canAccessRoom', message.rid, Meteor.userId()); + if (RocketChat.isTheLastMessage(room, message)) { + RocketChat.models.Rooms.updateLastMessageStar(room._id, Meteor.userId(), message.starred); + } return RocketChat.models.Messages.updateUserStarById(message._id, Meteor.userId(), message.starred); }, diff --git a/packages/rocketchat-reactions/package.js b/packages/rocketchat-reactions/package.js index 78e23d96093b..03ae0e50d4dd 100644 --- a/packages/rocketchat-reactions/package.js +++ b/packages/rocketchat-reactions/package.js @@ -16,6 +16,7 @@ Package.onUse(function(api) { api.addFiles('client/init.js', 'client'); api.addFiles('server/models/Messages.js'); + api.addFiles('server/models/Rooms.js'); api.addFiles('client/methods/setReaction.js', 'client'); api.addFiles('setReaction.js', 'server'); diff --git a/packages/rocketchat-reactions/server/models/Rooms.js b/packages/rocketchat-reactions/server/models/Rooms.js new file mode 100644 index 000000000000..26e16f8b7628 --- /dev/null +++ b/packages/rocketchat-reactions/server/models/Rooms.js @@ -0,0 +1,7 @@ +RocketChat.models.Rooms.setReactionsInLastMessage = function(roomId, lastMessage) { + return this.update({ _id: roomId }, { $set: { lastMessage } }); +}; + +RocketChat.models.Rooms.unsetReactionsInLastMessage = function(roomId) { + return this.update({ _id: roomId }, { $unset: { lastMessage: { reactions: 1 } } }); +}; diff --git a/packages/rocketchat-reactions/setReaction.js b/packages/rocketchat-reactions/setReaction.js index 807ecdcb336a..f51303b7d572 100644 --- a/packages/rocketchat-reactions/setReaction.js +++ b/packages/rocketchat-reactions/setReaction.js @@ -61,9 +61,15 @@ Meteor.methods({ if (_.isEmpty(message.reactions)) { delete message.reactions; + if (RocketChat.isTheLastMessage(room, message)) { + RocketChat.models.Rooms.unsetReactionsInLastMessage(room._id); + } RocketChat.models.Messages.unsetReactions(messageId); RocketChat.callbacks.run('unsetReaction', messageId, reaction); } else { + if (RocketChat.isTheLastMessage(room, message)) { + RocketChat.models.Rooms.setReactionsInLastMessage(room._id, message); + } RocketChat.models.Messages.setReactions(messageId, message.reactions); RocketChat.callbacks.run('setReaction', messageId, reaction); } @@ -77,7 +83,9 @@ Meteor.methods({ }; } message.reactions[reaction].usernames.push(user.username); - + if (RocketChat.isTheLastMessage(room, message)) { + RocketChat.models.Rooms.setReactionsInLastMessage(room._id, message); + } RocketChat.models.Messages.setReactions(messageId, message.reactions); RocketChat.callbacks.run('setReaction', messageId, reaction); } diff --git a/tests/end-to-end/api/02-channels.js b/tests/end-to-end/api/02-channels.js index 4cbd2cddb5f9..a140bf7195e0 100644 --- a/tests/end-to-end/api/02-channels.js +++ b/tests/end-to-end/api/02-channels.js @@ -15,7 +15,7 @@ import { apiPublicChannelName, channel, } from '../../data/api-data.js'; -import { adminEmail, password } from '../../data/user.js'; +import { adminEmail, password, adminUsername } from '../../data/user.js'; import supertest from 'supertest'; function getRoomInfo(roomId) { @@ -55,22 +55,135 @@ describe('[Channels]', function() { .end(done); }); - it('/channels.info', (done) => { - request.get(api('channels.info')) - .set(credentials) - .query({ - roomId: channel._id, - }) - .expect('Content-Type', 'application/json') - .expect(200) - .expect((res) => { - expect(res.body).to.have.property('success', true); - expect(res.body).to.have.nested.property('channel._id'); - expect(res.body).to.have.nested.property('channel.name', apiPublicChannelName); - expect(res.body).to.have.nested.property('channel.t', 'c'); - expect(res.body).to.have.nested.property('channel.msgs', 0); - }) - .end(done); + describe('[/channels.info]', () => { + let testChannel = {}; + let channelMessage = {}; + it('creating new channel...', (done) => { + request.post(api('channels.create')) + .set(credentials) + .send({ + name: apiPublicChannelName, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + testChannel = res.body.channel; + }) + .end(done); + }); + it('should return channel basic structure', (done) => { + request.get(api('channels.info')) + .set(credentials) + .query({ + roomId: testChannel._id, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + expect(res.body).to.have.nested.property('channel._id'); + expect(res.body).to.have.nested.property('channel.name', apiPublicChannelName); + expect(res.body).to.have.nested.property('channel.t', 'c'); + expect(res.body).to.have.nested.property('channel.msgs', 0); + }) + .end(done); + }); + it('sending a message...', (done) => { + request.post(api('chat.sendMessage')) + .set(credentials) + .send({ + message: { + text: 'Sample message', + rid: testChannel._id, + }, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + channelMessage = res.body.message; + }) + .end(done); + }); + it('REACTing with last message', (done) => { + request.post(api('chat.react')) + .set(credentials) + .send({ + emoji: ':squid:', + messageId: channelMessage._id, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + }) + .end(done); + }); + it('STARring last message', (done) => { + request.post(api('chat.starMessage')) + .set(credentials) + .send({ + messageId: channelMessage._id, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + }) + .end(done); + }); + it('PINning last message', (done) => { + request.post(api('chat.pinMessage')) + .set(credentials) + .send({ + messageId: channelMessage._id, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + }) + .end(done); + }); + it('should return channel structure with "lastMessage" object including pin, reaction and star infos', (done) => { + request.get(api('channels.info')) + .set(credentials) + .query({ + roomId: testChannel._id, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + expect(res.body).to.have.property('channel').and.to.be.an('object'); + const { channel } = res.body; + expect(channel).to.have.property('lastMessage').and.to.be.an('object'); + expect(channel.lastMessage).to.have.property('reactions').and.to.be.an('object'); + expect(channel.lastMessage).to.have.property('pinned').and.to.be.a('boolean'); + expect(channel.lastMessage).to.have.property('pinnedAt').and.to.be.a('string'); + expect(channel.lastMessage).to.have.property('pinnedBy').and.to.be.an('object'); + expect(channel.lastMessage).to.have.property('starred').and.to.be.an('object'); + }) + .end(done); + }); + it('should return all channels messages where the last message of array should have the "star" object with USERS star ONLY', (done) => { + request.get(api('channels.messages')) + .set(credentials) + .query({ + roomId: testChannel._id, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + expect(res.body).to.have.property('messages').and.to.be.an('array'); + const { messages } = res.body; + const lastMessage = messages.filter((message) => message._id === channelMessage._id)[0]; + expect(lastMessage).to.have.property('starred').and.to.be.an('object'); + expect(lastMessage.starred._id).to.be.equal(adminUsername); + }) + .end(done); + }); }); it('/channels.invite', async(done) => { diff --git a/tests/end-to-end/api/03-groups.js b/tests/end-to-end/api/03-groups.js index 9594ca5390c6..be6672e48360 100644 --- a/tests/end-to-end/api/03-groups.js +++ b/tests/end-to-end/api/03-groups.js @@ -5,6 +5,7 @@ import { getCredentials, api, login, request, credentials, group, log, apiPrivateChannelName } from '../../data/api-data.js'; import { adminEmail, password } from '../../data/user.js'; import supertest from 'supertest'; +import { adminUsername } from '../../data/user'; function getRoomInfo(roomId) { return new Promise((resolve/* , reject*/) => { @@ -43,22 +44,135 @@ describe('[Groups]', function() { .end(done); }); - it('/groups.info', (done) => { - request.get(api('groups.info')) - .set(credentials) - .query({ - roomId: group._id, - }) - .expect('Content-Type', 'application/json') - .expect(200) - .expect((res) => { - expect(res.body).to.have.property('success', true); - expect(res.body).to.have.nested.property('group._id'); - expect(res.body).to.have.nested.property('group.name', apiPrivateChannelName); - expect(res.body).to.have.nested.property('group.t', 'p'); - expect(res.body).to.have.nested.property('group.msgs', 0); - }) - .end(done); + describe('[/groups.info]', () => { + let testGroup = {}; + let groupMessage = {}; + it('creating new group...', (done) => { + request.post(api('groups.create')) + .set(credentials) + .send({ + name: apiPrivateChannelName, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + testGroup = res.body.group; + }) + .end(done); + }); + it('should return group basic structure', (done) => { + request.get(api('groups.info')) + .set(credentials) + .query({ + roomId: testGroup._id, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + expect(res.body).to.have.nested.property('group._id'); + expect(res.body).to.have.nested.property('group.name', apiPrivateChannelName); + expect(res.body).to.have.nested.property('group.t', 'p'); + expect(res.body).to.have.nested.property('group.msgs', 0); + }) + .end(done); + }); + it('sending a message...', (done) => { + request.post(api('chat.sendMessage')) + .set(credentials) + .send({ + message: { + text: 'Sample message', + rid: testGroup._id, + }, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + groupMessage = res.body.message; + }) + .end(done); + }); + it('REACTing with last message', (done) => { + request.post(api('chat.react')) + .set(credentials) + .send({ + emoji: ':squid:', + messageId: groupMessage._id, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + }) + .end(done); + }); + it('STARring last message', (done) => { + request.post(api('chat.starMessage')) + .set(credentials) + .send({ + messageId: groupMessage._id, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + }) + .end(done); + }); + it('PINning last message', (done) => { + request.post(api('chat.pinMessage')) + .set(credentials) + .send({ + messageId: groupMessage._id, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + }) + .end(done); + }); + it('should return group structure with "lastMessage" object including pin, reaction and star infos', (done) => { + request.get(api('groups.info')) + .set(credentials) + .query({ + roomId: testGroup._id, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + expect(res.body).to.have.property('group').and.to.be.an('object'); + const { group } = res.body; + expect(group).to.have.property('lastMessage').and.to.be.an('object'); + expect(group.lastMessage).to.have.property('reactions').and.to.be.an('object'); + expect(group.lastMessage).to.have.property('pinned').and.to.be.a('boolean'); + expect(group.lastMessage).to.have.property('pinnedAt').and.to.be.a('string'); + expect(group.lastMessage).to.have.property('pinnedBy').and.to.be.an('object'); + expect(group.lastMessage).to.have.property('starred').and.to.be.an('object'); + }) + .end(done); + }); + it('should return all groups messages where the last message of array should have the "star" object with USERS star ONLY', (done) => { + request.get(api('groups.messages')) + .set(credentials) + .query({ + roomId: testGroup._id, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + expect(res.body).to.have.property('messages').and.to.be.an('array'); + const { messages } = res.body; + const lastMessage = messages.filter((message) => message._id === groupMessage._id)[0]; + expect(lastMessage).to.have.property('starred').and.to.be.an('object'); + expect(lastMessage.starred._id).to.be.equal(adminUsername); + }) + .end(done); + }); }); it('/groups.invite', async(done) => { diff --git a/tests/end-to-end/api/04-direct-message.js b/tests/end-to-end/api/04-direct-message.js index 4b4a0ea8d215..9ed70ad85b1d 100644 --- a/tests/end-to-end/api/04-direct-message.js +++ b/tests/end-to-end/api/04-direct-message.js @@ -2,9 +2,20 @@ /* globals expect */ /* eslint no-unused-vars: 0 */ -import { getCredentials, api, login, request, credentials, directMessage, log, apiUsername, apiEmail } from '../../data/api-data.js'; +import { + getCredentials, + api, + login, + request, + credentials, + directMessage, + log, + apiUsername, + apiEmail, +} from '../../data/api-data.js'; import { adminEmail, password } from '../../data/user.js'; import supertest from 'supertest'; +import { adminUsername } from '../../data/user'; describe('[Direct Messages]', function() { this.retries(0); @@ -44,6 +55,99 @@ describe('[Direct Messages]', function() { .end(done); }); + describe('Testing DM info', () => { + let testDM = {}; + let dmMessage = {}; + it('creating new DM...', (done) => { + request.post(api('im.create')) + .set(credentials) + .send({ + username: 'rocket.cat', + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + testDM = res.body.room; + }) + .end(done); + }); + it('sending a message...', (done) => { + request.post(api('chat.sendMessage')) + .set(credentials) + .send({ + message: { + text: 'Sample message', + rid: testDM._id, + }, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + dmMessage = res.body.message; + }) + .end(done); + }); + it('REACTing with last message', (done) => { + request.post(api('chat.react')) + .set(credentials) + .send({ + emoji: ':squid:', + messageId: dmMessage._id, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + }) + .end(done); + }); + it('STARring last message', (done) => { + request.post(api('chat.starMessage')) + .set(credentials) + .send({ + messageId: dmMessage._id, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + }) + .end(done); + }); + it('PINning last message', (done) => { + request.post(api('chat.pinMessage')) + .set(credentials) + .send({ + messageId: dmMessage._id, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + }) + .end(done); + }); + it('should return all DM messages where the last message of array should have the "star" object with USERS star ONLY', (done) => { + request.get(api('im.messages')) + .set(credentials) + .query({ + roomId: testDM._id, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + expect(res.body).to.have.property('messages').and.to.be.an('array'); + const { messages } = res.body; + const lastMessage = messages.filter((message) => message._id === dmMessage._id)[0]; + expect(lastMessage).to.have.property('starred').and.to.be.an('object'); + expect(lastMessage.starred._id).to.be.equal(adminUsername); + }) + .end(done); + }); + }); + it('/im.history', (done) => { request.get(api('im.history')) .set(credentials)