diff --git a/packages/rocketchat-api/server/v1/chat.js b/packages/rocketchat-api/server/v1/chat.js index d6efe9b1af52..2787ec2620ef 100644 --- a/packages/rocketchat-api/server/v1/chat.js +++ b/packages/rocketchat-api/server/v1/chat.js @@ -342,3 +342,35 @@ RocketChat.API.v1.addRoute('chat.ignoreUser', { authRequired: true }, { return RocketChat.API.v1.success(); }, }); + +RocketChat.API.v1.addRoute('chat.getDeletedMessages', { authRequired: true }, { + get() { + const { roomId, since } = this.queryParams; + const { offset, count } = this.getPaginationItems(); + + if (!roomId) { + throw new Meteor.Error('The required "roomId" query param is missing.'); + } + + if (!since) { + throw new Meteor.Error('The required "since" query param is missing.'); + } else if (isNaN(Date.parse(since))) { + throw new Meteor.Error('The "since" query parameter must be a valid date.'); + } + const cursor = RocketChat.models.Messages.trashFindDeleted(new Date(since), { rid: roomId }, { + skip: offset, + limit: count, + }); + + const total = cursor.count(); + + const messages = cursor.fetch(); + + return RocketChat.API.v1.success({ + messages, + count: messages.length, + offset, + total, + }); + }, +}); diff --git a/packages/rocketchat-lib/server/models/_Base.js b/packages/rocketchat-lib/server/models/_Base.js index 5183d1f0e4ed..d09b91760b07 100644 --- a/packages/rocketchat-lib/server/models/_Base.js +++ b/packages/rocketchat-lib/server/models/_Base.js @@ -128,6 +128,10 @@ class ModelsBase { return this._db.trashFindDeletedAfter(...args); } + trashFindDeleted(...args) { + return this._db.trashFindDeleted(...args); + } + processQueryOptionsOnResult(result, options = {}) { if (result === undefined || result === null) { return undefined; diff --git a/packages/rocketchat-lib/server/models/_BaseDb.js b/packages/rocketchat-lib/server/models/_BaseDb.js index 4b739f8a1bf1..3c97bf1a5749 100644 --- a/packages/rocketchat-lib/server/models/_BaseDb.js +++ b/packages/rocketchat-lib/server/models/_BaseDb.js @@ -376,6 +376,15 @@ class ModelsBaseDb extends EventEmitter { return trash.find(query, options); } + + trashFindDeleted(deletedAt, query = {}, options) { + query.__collection__ = this.name; + query._deletedAt = { + $gte: deletedAt, + }; + + return trash.find(query, options); + } } export default ModelsBaseDb; diff --git a/tests/data/chat.helper.js b/tests/data/chat.helper.js new file mode 100644 index 000000000000..c25f6c1f4d62 --- /dev/null +++ b/tests/data/chat.helper.js @@ -0,0 +1,32 @@ +import { api, credentials, request } from './api-data'; + +export const sendSimpleMessage = ({ roomId, text = 'test message' }) => { + if (!roomId) { + throw new Error('"roomId" is required in "sendSimpleMessage" test helper'); + } + + return request.post(api('chat.sendMessage')) + .set(credentials) + .send({ + message: { + rid: roomId, + text, + }, + }); +}; + +export const deleteMessage = ({ roomId, msgId }) => { + if (!roomId) { + throw new Error('"roomId" is required in "deleteMessage" test helper'); + } + if (!msgId) { + throw new Error('"msgId" is required in "deleteMessage" test helper'); + } + + return request.post(api('chat.delete')) + .set(credentials) + .send({ + roomId, + msgId, + }); +}; diff --git a/tests/end-to-end/api/05-chat.js b/tests/end-to-end/api/05-chat.js index c13772f7e6ee..4dc52880cd15 100644 --- a/tests/end-to-end/api/05-chat.js +++ b/tests/end-to-end/api/05-chat.js @@ -6,6 +6,8 @@ import { message, } from '../../data/api-data.js'; import { password } from '../../data/user'; +import { createRoom } from '../../data/rooms.helper.js'; +import { sendSimpleMessage, deleteMessage } from '../../data/chat.helper.js'; describe('[Chat]', function() { this.retries(0); @@ -664,4 +666,127 @@ describe('[Chat]', function() { }); }); + describe('[/chat.getDeletedMessages]', () => { + let roomId; + before((done) => { + createRoom({ + type: 'c', + name: `channel.test.${ Date.now() }`, + }).end((err, res) => { + roomId = res.body.channel._id; + sendSimpleMessage({ roomId }) + .end((err, res) => { + const msgId = res.body.message._id; + deleteMessage({ roomId, msgId }).end(done); + }); + }); + }); + + describe('when execute successfully', () => { + it('should return a list of deleted messages', (done) => { + request.get(api('chat.getDeletedMessages')) + .set(credentials) + .query({ + roomId, + since: new Date('20 December 2018 17:51 UTC').toISOString(), + }) + .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'); + expect(res.body.messages.length).to.be.equal(1); + }) + .end(done); + }); + it('should return a list of deleted messages when the user sets count query parameter', (done) => { + request.get(api('chat.getDeletedMessages')) + .set(credentials) + .query({ + roomId, + since: new Date('20 December 2018 17:51 UTC').toISOString(), + count: 1, + }) + .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'); + expect(res.body.messages.length).to.be.equal(1); + }) + .end(done); + }); + it('should return a list of deleted messages when the user sets count and offset query parameters', (done) => { + request.get(api('chat.getDeletedMessages')) + .set(credentials) + .query({ + roomId, + since: new Date('20 December 2018 17:51 UTC').toISOString(), + count: 1, + offset: 0, + }) + .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'); + expect(res.body.messages.length).to.be.equal(1); + }) + .end(done); + }); + }); + + describe('when an error occurs', () => { + it('should return statusCode 400 and an error when "roomId" is not provided', (done) => { + request.get(api('chat.getDeletedMessages')) + .set(credentials) + .query({ + since: new Date('20 December 2018 17:51 UTC').toISOString(), + count: 1, + offset: 0, + }) + .expect('Content-Type', 'application/json') + .expect(400) + .expect((res) => { + expect(res.body).to.have.property('success', false); + expect(res.body.errorType).to.be.equal('The required "roomId" query param is missing.'); + }) + .end(done); + }); + it('should return statusCode 400 and an error when "since" is not provided', (done) => { + request.get(api('chat.getDeletedMessages')) + .set(credentials) + .query({ + roomId, + count: 1, + offset: 0, + }) + .expect('Content-Type', 'application/json') + .expect(400) + .expect((res) => { + expect(res.body).to.have.property('success', false); + expect(res.body.errorType).to.be.equal('The required "since" query param is missing.'); + }) + .end(done); + }); + it('should return statusCode 400 and an error when "since" is provided but it is invalid ISODate', (done) => { + request.get(api('chat.getDeletedMessages')) + .set(credentials) + .query({ + roomId, + since: 'InvalidaDate', + count: 1, + offset: 0, + }) + .expect('Content-Type', 'application/json') + .expect(400) + .expect((res) => { + expect(res.body).to.have.property('success', false); + expect(res.body.errorType).to.be.equal('The "since" query parameter must be a valid date.'); + }) + .end(done); + }); + }); + }); + });