-
Notifications
You must be signed in to change notification settings - Fork 10.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: Allow to use the token from
room.v
when requesting transcript …
…instead of finding visitor (#33211)
- Loading branch information
Showing
5 changed files
with
67 additions
and
25 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"@rocket.chat/meteor": patch | ||
--- | ||
|
||
Allow to use the token from `room.v` when requesting transcript instead of visitor token. Visitors may change their tokens at any time, rendering old conversations impossible to access for them (or for APIs depending on token) as the visitor token won't match the `room.v` token. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -283,6 +283,27 @@ describe('LIVECHAT - Utils', () => { | |
.send({ token: visitor.token, rid: room._id, email: '[email protected]' }); | ||
expect(body).to.have.property('success', true); | ||
}); | ||
it('should allow a visitor to get a transcript even if token changed by using an old token that matches room.v', async () => { | ||
const visitor = await createVisitor(); | ||
const room = await createLivechatRoom(visitor.token); | ||
await closeOmnichannelRoom(room._id); | ||
const visitor2 = await createVisitor(undefined, undefined, visitor.visitorEmails?.[0].address); | ||
const room2 = await createLivechatRoom(visitor2.token); | ||
await closeOmnichannelRoom(room2._id); | ||
|
||
expect(visitor.token !== visitor2.token).to.be.true; | ||
const { body } = await request | ||
.post(api('livechat/transcript')) | ||
.set(credentials) | ||
.send({ token: visitor.token, rid: room._id, email: '[email protected]' }); | ||
expect(body).to.have.property('success', true); | ||
|
||
const { body: body2 } = await request | ||
.post(api('livechat/transcript')) | ||
.set(credentials) | ||
.send({ token: visitor2.token, rid: room2._id, email: '[email protected]' }); | ||
expect(body2).to.have.property('success', true); | ||
}); | ||
}); | ||
|
||
describe('livechat/transcript/:rid', () => { | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,9 +6,6 @@ const modelsMock = { | |
LivechatRooms: { | ||
findOneById: sinon.stub(), | ||
}, | ||
LivechatVisitors: { | ||
getVisitorByToken: sinon.stub(), | ||
}, | ||
Messages: { | ||
findLivechatClosingMessage: sinon.stub(), | ||
findVisibleByRoomIdNotContainingTypesBeforeTs: sinon.stub(), | ||
|
@@ -75,7 +72,6 @@ describe('Send transcript', () => { | |
beforeEach(() => { | ||
checkMock.reset(); | ||
modelsMock.LivechatRooms.findOneById.reset(); | ||
modelsMock.LivechatVisitors.getVisitorByToken.reset(); | ||
modelsMock.Messages.findLivechatClosingMessage.reset(); | ||
modelsMock.Messages.findVisibleByRoomIdNotContainingTypesBeforeTs.reset(); | ||
modelsMock.Users.findOneById.reset(); | ||
|
@@ -87,11 +83,9 @@ describe('Send transcript', () => { | |
await expect(sendTranscript({})).to.be.rejectedWith(Error); | ||
}); | ||
it('should throw error when visitor not found', async () => { | ||
modelsMock.LivechatVisitors.getVisitorByToken.resolves(null); | ||
await expect(sendTranscript({ rid: 'rid', email: 'email', logger: mockLogger })).to.be.rejectedWith(Error); | ||
}); | ||
it('should attempt to send an email when params are valid using default subject', async () => { | ||
modelsMock.LivechatVisitors.getVisitorByToken.resolves({ language: null }); | ||
modelsMock.LivechatRooms.findOneById.resolves({ t: 'l', v: { token: 'token' } }); | ||
modelsMock.Messages.findVisibleByRoomIdNotContainingTypesBeforeTs.resolves([]); | ||
tStub.returns('Conversation Transcript'); | ||
|
@@ -117,7 +111,6 @@ describe('Send transcript', () => { | |
).to.be.true; | ||
}); | ||
it('should use provided subject', async () => { | ||
modelsMock.LivechatVisitors.getVisitorByToken.resolves({ language: null }); | ||
modelsMock.LivechatRooms.findOneById.resolves({ t: 'l', v: { token: 'token' } }); | ||
modelsMock.Messages.findVisibleByRoomIdNotContainingTypesBeforeTs.resolves([]); | ||
|
||
|
@@ -143,7 +136,6 @@ describe('Send transcript', () => { | |
).to.be.true; | ||
}); | ||
it('should use subject from setting (when configured) when no subject provided', async () => { | ||
modelsMock.LivechatVisitors.getVisitorByToken.resolves({ language: null }); | ||
modelsMock.LivechatRooms.findOneById.resolves({ t: 'l', v: { token: 'token' } }); | ||
modelsMock.Messages.findVisibleByRoomIdNotContainingTypesBeforeTs.resolves([]); | ||
mockSettingValues.Livechat_transcript_email_subject = 'A custom subject obtained from setting.get'; | ||
|
@@ -170,36 +162,63 @@ describe('Send transcript', () => { | |
}); | ||
it('should fail if room provided is invalid', async () => { | ||
modelsMock.LivechatRooms.findOneById.resolves(null); | ||
modelsMock.LivechatVisitors.getVisitorByToken.resolves({ language: null }); | ||
|
||
await expect(sendTranscript({ rid: 'rid', email: 'email', logger: mockLogger })).to.be.rejectedWith(Error); | ||
}); | ||
|
||
it('should fail if room provided is of different type', async () => { | ||
modelsMock.LivechatRooms.findOneById.resolves({ t: 'c' }); | ||
modelsMock.LivechatVisitors.getVisitorByToken.resolves({ language: null }); | ||
|
||
await expect(sendTranscript({ rid: 'rid', email: 'email' })).to.be.rejectedWith(Error); | ||
}); | ||
|
||
it('should fail if room is of valid type, but doesnt doesnt have `v` property', async () => { | ||
modelsMock.LivechatVisitors.getVisitorByToken.resolves({ language: null }); | ||
modelsMock.LivechatRooms.findOneById.resolves({ t: 'l' }); | ||
|
||
await expect(sendTranscript({ rid: 'rid', email: 'email' })).to.be.rejectedWith(Error); | ||
}); | ||
|
||
it('should fail if room is of valid type, has `v` prop, but it doesnt contain `token`', async () => { | ||
modelsMock.LivechatVisitors.getVisitorByToken.resolves({ language: null }); | ||
modelsMock.LivechatRooms.findOneById.resolves({ t: 'l', v: { otherProp: 'xxx' } }); | ||
|
||
await expect(sendTranscript({ rid: 'rid', email: 'email' })).to.be.rejectedWith(Error); | ||
}); | ||
|
||
it('should fail if room is of valid type, has `v.token`, but its different from the one on param (room from another visitor)', async () => { | ||
modelsMock.LivechatVisitors.getVisitorByToken.resolves({ language: null }); | ||
modelsMock.LivechatRooms.findOneById.resolves({ t: 'l', v: { token: 'xxx' } }); | ||
|
||
await expect(sendTranscript({ rid: 'rid', email: 'email', token: 'xveasdf' })).to.be.rejectedWith(Error); | ||
}); | ||
|
||
it('should throw an error when token is not the one on room.v', async () => { | ||
modelsMock.LivechatRooms.findOneById.resolves({ t: 'l', v: { token: 'xxx' } }); | ||
|
||
await expect(sendTranscript({ rid: 'rid', email: 'email', token: 'xveasdf' })).to.be.rejectedWith(Error); | ||
}); | ||
it('should work when token matches room.v', async () => { | ||
modelsMock.LivechatRooms.findOneById.resolves({ t: 'l', v: { token: 'token-123' } }); | ||
modelsMock.Messages.findVisibleByRoomIdNotContainingTypesBeforeTs.resolves([]); | ||
delete mockSettingValues.Livechat_transcript_email_subject; | ||
tStub.returns('Conversation Transcript'); | ||
|
||
await sendTranscript({ | ||
rid: 'rid', | ||
token: 'token-123', | ||
email: 'email', | ||
user: { _id: 'x', name: 'x', utcOffset: '-6', username: 'x' }, | ||
}); | ||
|
||
expect(getTimezoneMock.calledWith({ _id: 'x', name: 'x', utcOffset: '-6', username: 'x' })).to.be.true; | ||
expect(modelsMock.Messages.findLivechatClosingMessage.calledWith('rid', { projection: { ts: 1 } })).to.be.true; | ||
expect(modelsMock.Messages.findVisibleByRoomIdNotContainingTypesBeforeTs.called).to.be.true; | ||
expect( | ||
mailerMock.calledWith({ | ||
to: 'email', | ||
from: '[email protected]', | ||
subject: 'Conversation Transcript', | ||
replyTo: '[email protected]', | ||
html: '<div> <hr></div>', | ||
}), | ||
).to.be.true; | ||
}); | ||
}); |