From 42a176f2dcd38a6afae33764fda021929af0fabc Mon Sep 17 00:00:00 2001 From: Abhinav Kumar Date: Wed, 14 Aug 2024 17:08:28 +0530 Subject: [PATCH 1/7] fix: customFields ignored in livechat room creation Signed-off-by: Abhinav Kumar --- .../app/livechat-enterprise/server/hooks/beforeNewRoom.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/apps/meteor/ee/app/livechat-enterprise/server/hooks/beforeNewRoom.ts b/apps/meteor/ee/app/livechat-enterprise/server/hooks/beforeNewRoom.ts index 35219fc6e03b..5c5eb209de3f 100644 --- a/apps/meteor/ee/app/livechat-enterprise/server/hooks/beforeNewRoom.ts +++ b/apps/meteor/ee/app/livechat-enterprise/server/hooks/beforeNewRoom.ts @@ -10,9 +10,11 @@ callbacks.add( return roomInfo; } - const { sla: searchTerm } = extraData; + const { sla: searchTerm, customFields } = extraData; + const roomInfoWithExtraData = { ...roomInfo, ...(!!customFields && { customFields }) }; + if (!searchTerm) { - return roomInfo; + return roomInfoWithExtraData; } const sla = await OmnichannelServiceLevelAgreements.findOneByIdOrName(searchTerm); @@ -23,7 +25,7 @@ callbacks.add( } const { _id: slaId } = sla; - return { ...roomInfo, slaId }; + return { ...roomInfoWithExtraData, slaId }; }, callbacks.priority.MEDIUM, 'livechat-before-new-room', From 982228d1dc68268965b912247fc75d1649aacf56 Mon Sep 17 00:00:00 2001 From: Abhinav Kumar Date: Wed, 14 Aug 2024 18:09:42 +0530 Subject: [PATCH 2/7] added changeset Signed-off-by: Abhinav Kumar --- .changeset/twelve-windows-train.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/twelve-windows-train.md diff --git a/.changeset/twelve-windows-train.md b/.changeset/twelve-windows-train.md new file mode 100644 index 000000000000..4c6ef548e650 --- /dev/null +++ b/.changeset/twelve-windows-train.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Fixed: Custom fields in extraData now correctly added to extraRoomInfo by livechat.beforeRoom callback during livechat room creation. From 9c8d53f2c0e599c149c3efd679053e9156633837 Mon Sep 17 00:00:00 2001 From: Abhinav Kumar Date: Thu, 15 Aug 2024 02:12:45 +0530 Subject: [PATCH 3/7] added test Signed-off-by: Abhinav Kumar --- apps/meteor/jest.config.ts | 5 +++++ .../server/hooks/beforeNewRoom.spec.ts | 21 +++++++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 apps/meteor/tests/unit/app/livechat/server/hooks/beforeNewRoom.spec.ts diff --git a/apps/meteor/jest.config.ts b/apps/meteor/jest.config.ts index 8dc7fa033e03..cfcdab76f0b9 100644 --- a/apps/meteor/jest.config.ts +++ b/apps/meteor/jest.config.ts @@ -32,11 +32,16 @@ export default { testMatch: [ '/app/livechat/server/business-hour/**/*.spec.ts?(x)', + '/tests/unit/app/livechat/server/hooks/beforeNewRoom.spec.ts', '/app/livechat/server/api/**/*.spec.ts', '/ee/app/authorization/server/validateUserRoles.spec.ts', '/app/cloud/server/functions/supportedVersionsToken/**.spec.ts', '/app/utils/lib/**.spec.ts', ], + + moduleNameMapper: { + '^meteor/(.*)': '/tests/mocks/client/meteor.ts', + }, }, ], } satisfies Config; diff --git a/apps/meteor/tests/unit/app/livechat/server/hooks/beforeNewRoom.spec.ts b/apps/meteor/tests/unit/app/livechat/server/hooks/beforeNewRoom.spec.ts new file mode 100644 index 000000000000..003d3fe44848 --- /dev/null +++ b/apps/meteor/tests/unit/app/livechat/server/hooks/beforeNewRoom.spec.ts @@ -0,0 +1,21 @@ +import { expect } from 'chai'; +import proxyquire from 'proxyquire'; + +import { callbacks } from '../../../../../../lib/callbacks'; + +proxyquire.noCallThru().load('../../../../../../ee/app/livechat-enterprise/server/hooks/beforeNewRoom.ts', { + 'meteor/meteor': { + Meteor: { + Error, + }, + }, +}); + +describe('livechat.beforeRoom', () => { + it('should return roomInfo with customFields when provided', async () => { + const roomInfo = { name: 'test' }; + const extraData = { customFields: { test: 'test' } }; + const result = await callbacks.run('livechat.beforeRoom', roomInfo, extraData); + expect(result).to.deep.equal({ ...roomInfo, customFields: extraData.customFields }); + }); +}); From 48cfbd0b1bd3c90f2198adc753783b3cf267afed Mon Sep 17 00:00:00 2001 From: Abhinav Kumar Date: Fri, 16 Aug 2024 18:02:10 +0530 Subject: [PATCH 4/7] added check for type of customFields Signed-off-by: Abhinav Kumar --- .../ee/app/livechat-enterprise/server/hooks/beforeNewRoom.ts | 3 ++- apps/meteor/lib/utils/isPlainObject.ts | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 apps/meteor/lib/utils/isPlainObject.ts diff --git a/apps/meteor/ee/app/livechat-enterprise/server/hooks/beforeNewRoom.ts b/apps/meteor/ee/app/livechat-enterprise/server/hooks/beforeNewRoom.ts index 5c5eb209de3f..4b0db6814bf2 100644 --- a/apps/meteor/ee/app/livechat-enterprise/server/hooks/beforeNewRoom.ts +++ b/apps/meteor/ee/app/livechat-enterprise/server/hooks/beforeNewRoom.ts @@ -2,6 +2,7 @@ import { OmnichannelServiceLevelAgreements } from '@rocket.chat/models'; import { Meteor } from 'meteor/meteor'; import { callbacks } from '../../../../../lib/callbacks'; +import { isPlainObject } from '../../../../../lib/utils/isPlainObject'; callbacks.add( 'livechat.beforeRoom', @@ -11,7 +12,7 @@ callbacks.add( } const { sla: searchTerm, customFields } = extraData; - const roomInfoWithExtraData = { ...roomInfo, ...(!!customFields && { customFields }) }; + const roomInfoWithExtraData = { ...roomInfo, ...(isPlainObject(customFields) && { customFields }) }; if (!searchTerm) { return roomInfoWithExtraData; diff --git a/apps/meteor/lib/utils/isPlainObject.ts b/apps/meteor/lib/utils/isPlainObject.ts new file mode 100644 index 000000000000..a2bcf15cc590 --- /dev/null +++ b/apps/meteor/lib/utils/isPlainObject.ts @@ -0,0 +1,3 @@ +export function isPlainObject(value: unknown) { + return value !== null && typeof value === 'object' && !Array.isArray(value); +} From ee2ea09e0b29cfa84cb3069763ee0884ea508092 Mon Sep 17 00:00:00 2001 From: Abhinav Kumar Date: Fri, 16 Aug 2024 19:05:28 +0530 Subject: [PATCH 5/7] updated tests for livechat.beforeRoom Signed-off-by: Abhinav Kumar --- apps/meteor/jest.config.ts | 5 --- .../server/hooks/beforeNewRoom.spec.ts | 21 ---------- .../livechat/hooks/beforeNewRoom.spec.ts | 42 +++++++++++++++++++ 3 files changed, 42 insertions(+), 26 deletions(-) delete mode 100644 apps/meteor/tests/unit/app/livechat/server/hooks/beforeNewRoom.spec.ts create mode 100644 apps/meteor/tests/unit/server/livechat/hooks/beforeNewRoom.spec.ts diff --git a/apps/meteor/jest.config.ts b/apps/meteor/jest.config.ts index cfcdab76f0b9..8dc7fa033e03 100644 --- a/apps/meteor/jest.config.ts +++ b/apps/meteor/jest.config.ts @@ -32,16 +32,11 @@ export default { testMatch: [ '/app/livechat/server/business-hour/**/*.spec.ts?(x)', - '/tests/unit/app/livechat/server/hooks/beforeNewRoom.spec.ts', '/app/livechat/server/api/**/*.spec.ts', '/ee/app/authorization/server/validateUserRoles.spec.ts', '/app/cloud/server/functions/supportedVersionsToken/**.spec.ts', '/app/utils/lib/**.spec.ts', ], - - moduleNameMapper: { - '^meteor/(.*)': '/tests/mocks/client/meteor.ts', - }, }, ], } satisfies Config; diff --git a/apps/meteor/tests/unit/app/livechat/server/hooks/beforeNewRoom.spec.ts b/apps/meteor/tests/unit/app/livechat/server/hooks/beforeNewRoom.spec.ts deleted file mode 100644 index 003d3fe44848..000000000000 --- a/apps/meteor/tests/unit/app/livechat/server/hooks/beforeNewRoom.spec.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { expect } from 'chai'; -import proxyquire from 'proxyquire'; - -import { callbacks } from '../../../../../../lib/callbacks'; - -proxyquire.noCallThru().load('../../../../../../ee/app/livechat-enterprise/server/hooks/beforeNewRoom.ts', { - 'meteor/meteor': { - Meteor: { - Error, - }, - }, -}); - -describe('livechat.beforeRoom', () => { - it('should return roomInfo with customFields when provided', async () => { - const roomInfo = { name: 'test' }; - const extraData = { customFields: { test: 'test' } }; - const result = await callbacks.run('livechat.beforeRoom', roomInfo, extraData); - expect(result).to.deep.equal({ ...roomInfo, customFields: extraData.customFields }); - }); -}); diff --git a/apps/meteor/tests/unit/server/livechat/hooks/beforeNewRoom.spec.ts b/apps/meteor/tests/unit/server/livechat/hooks/beforeNewRoom.spec.ts new file mode 100644 index 000000000000..769cf55564f9 --- /dev/null +++ b/apps/meteor/tests/unit/server/livechat/hooks/beforeNewRoom.spec.ts @@ -0,0 +1,42 @@ +import { expect } from 'chai'; +import { describe, it } from 'mocha'; +import proxyquire from 'proxyquire'; +import sinon from 'sinon'; + +import { callbacks } from '../../../../../lib/callbacks'; + +proxyquire.noCallThru().load('../../../../../ee/app/livechat-enterprise/server/hooks/beforeNewRoom.ts', { + 'meteor/meteor': { + Meteor: { + Error, + }, + }, + '@rocket.chat/models': { + OmnichannelServiceLevelAgreements: { + findOneByIdOrName: sinon.stub().returns({ _id: 'slaId' }), + }, + }, +}); + +describe('livechat.beforeRoom', () => { + it('should return roomInfo with customFields when provided', async () => { + const roomInfo = { name: 'test' }; + const extraData = { customFields: { test: 'test' } }; + const result = await callbacks.run('livechat.beforeRoom', roomInfo, extraData); + expect(result).to.deep.equal({ ...roomInfo, customFields: extraData.customFields }); + }); + + it('should not include field in roomInfo when extraData has field other than customFields, sla', async () => { + const roomInfo = { name: 'test' }; + const extraData = { customFields: { test: 'test' }, sla: 'high' }; + const result = await callbacks.run('livechat.beforeRoom', roomInfo, extraData); + expect(result).to.deep.equal({ ...roomInfo, customFields: extraData.customFields, slaId: 'slaId' }); + }); + + it('should return roomInfo with empty customFields when customFields is not an object', async () => { + const roomInfo = { name: 'test' }; + const extraData = { customFields: 'not an object' }; + const result = await callbacks.run('livechat.beforeRoom', roomInfo, extraData); + expect(result).to.deep.equal({ ...roomInfo }); + }); +}); From 375eef88d0b65d0e3ac4bc0564f3272932369b5f Mon Sep 17 00:00:00 2001 From: Abhinav Kumar Date: Fri, 16 Aug 2024 19:49:21 +0530 Subject: [PATCH 6/7] improved tests Signed-off-by: Abhinav Kumar --- .../server/livechat/hooks/beforeNewRoom.spec.ts | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/apps/meteor/tests/unit/server/livechat/hooks/beforeNewRoom.spec.ts b/apps/meteor/tests/unit/server/livechat/hooks/beforeNewRoom.spec.ts index 769cf55564f9..10bf43ba234e 100644 --- a/apps/meteor/tests/unit/server/livechat/hooks/beforeNewRoom.spec.ts +++ b/apps/meteor/tests/unit/server/livechat/hooks/beforeNewRoom.spec.ts @@ -13,7 +13,12 @@ proxyquire.noCallThru().load('../../../../../ee/app/livechat-enterprise/server/h }, '@rocket.chat/models': { OmnichannelServiceLevelAgreements: { - findOneByIdOrName: sinon.stub().returns({ _id: 'slaId' }), + findOneByIdOrName: sinon.stub().callsFake((id) => { + if (id === 'high') { + return Promise.resolve({ _id: 'high' }); + } + return Promise.resolve(null); + }), }, }, }); @@ -26,14 +31,20 @@ describe('livechat.beforeRoom', () => { expect(result).to.deep.equal({ ...roomInfo, customFields: extraData.customFields }); }); + it('should throw an error when provided with an invalid sla', async () => { + const roomInfo = { name: 'test' }; + const extraData = { customFields: { test: 'test' }, sla: 'invalid' }; + await expect(callbacks.run('livechat.beforeRoom', roomInfo, extraData)).to.be.rejectedWith(Error); + }); + it('should not include field in roomInfo when extraData has field other than customFields, sla', async () => { const roomInfo = { name: 'test' }; const extraData = { customFields: { test: 'test' }, sla: 'high' }; const result = await callbacks.run('livechat.beforeRoom', roomInfo, extraData); - expect(result).to.deep.equal({ ...roomInfo, customFields: extraData.customFields, slaId: 'slaId' }); + expect(result).to.deep.equal({ ...roomInfo, customFields: extraData.customFields, slaId: 'high' }); }); - it('should return roomInfo with empty customFields when customFields is not an object', async () => { + it('should return roomInfo with no customFields when customFields is not an object', async () => { const roomInfo = { name: 'test' }; const extraData = { customFields: 'not an object' }; const result = await callbacks.run('livechat.beforeRoom', roomInfo, extraData); From cbcd5c54df6be6c1110bd12a9f46456009aba2de Mon Sep 17 00:00:00 2001 From: Abhinav Kumar Date: Fri, 16 Aug 2024 20:33:49 +0530 Subject: [PATCH 7/7] improved tests Signed-off-by: Abhinav Kumar --- .../server/livechat/hooks/beforeNewRoom.spec.ts | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/apps/meteor/tests/unit/server/livechat/hooks/beforeNewRoom.spec.ts b/apps/meteor/tests/unit/server/livechat/hooks/beforeNewRoom.spec.ts index 10bf43ba234e..9ba9ae73fe57 100644 --- a/apps/meteor/tests/unit/server/livechat/hooks/beforeNewRoom.spec.ts +++ b/apps/meteor/tests/unit/server/livechat/hooks/beforeNewRoom.spec.ts @@ -1,10 +1,12 @@ import { expect } from 'chai'; -import { describe, it } from 'mocha'; +import { describe, it, beforeEach } from 'mocha'; import proxyquire from 'proxyquire'; import sinon from 'sinon'; import { callbacks } from '../../../../../lib/callbacks'; +const findStub = sinon.stub(); + proxyquire.noCallThru().load('../../../../../ee/app/livechat-enterprise/server/hooks/beforeNewRoom.ts', { 'meteor/meteor': { Meteor: { @@ -13,17 +15,14 @@ proxyquire.noCallThru().load('../../../../../ee/app/livechat-enterprise/server/h }, '@rocket.chat/models': { OmnichannelServiceLevelAgreements: { - findOneByIdOrName: sinon.stub().callsFake((id) => { - if (id === 'high') { - return Promise.resolve({ _id: 'high' }); - } - return Promise.resolve(null); - }), + findOneByIdOrName: findStub, }, }, }); describe('livechat.beforeRoom', () => { + beforeEach(() => findStub.withArgs('high').resolves({ _id: 'high' }).withArgs('invalid').resolves(null)); + it('should return roomInfo with customFields when provided', async () => { const roomInfo = { name: 'test' }; const extraData = { customFields: { test: 'test' } }; @@ -34,7 +33,7 @@ describe('livechat.beforeRoom', () => { it('should throw an error when provided with an invalid sla', async () => { const roomInfo = { name: 'test' }; const extraData = { customFields: { test: 'test' }, sla: 'invalid' }; - await expect(callbacks.run('livechat.beforeRoom', roomInfo, extraData)).to.be.rejectedWith(Error); + await expect(callbacks.run('livechat.beforeRoom', roomInfo, extraData)).to.be.rejectedWith(Error, 'error-invalid-sla'); }); it('should not include field in roomInfo when extraData has field other than customFields, sla', async () => {