From 313f3e766164f12efa00898b075d5d3cef71e07d Mon Sep 17 00:00:00 2001 From: Rafael Tapia Date: Tue, 13 Aug 2024 16:41:35 -0300 Subject: [PATCH 1/5] feat: add view contact permission --- apps/meteor/app/authorization/server/constant/permissions.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/meteor/app/authorization/server/constant/permissions.ts b/apps/meteor/app/authorization/server/constant/permissions.ts index e5e8f7fb05dd..46d40713bad1 100644 --- a/apps/meteor/app/authorization/server/constant/permissions.ts +++ b/apps/meteor/app/authorization/server/constant/permissions.ts @@ -101,6 +101,10 @@ export const permissions = [ _id: 'update-livechat-contact', roles: ['livechat-manager', 'livechat-monitor', 'livechat-agent', 'admin'], }, + { + _id: 'view-livechat-contact', + roles: ['livechat-manager', 'livechat-monitor', 'livechat-agent', 'admin'], + }, { _id: 'view-livechat-manager', roles: ['livechat-manager', 'livechat-monitor', 'admin'] }, { _id: 'view-omnichannel-contact-center', From 8772870e612606ee7aa0db3a3d396695c6812286 Mon Sep 17 00:00:00 2001 From: Rafael Tapia Date: Tue, 13 Aug 2024 16:41:56 -0300 Subject: [PATCH 2/5] feat: implement new get by id endpoint --- .../app/livechat/server/api/v1/contact.ts | 21 +++++++++++++++++-- .../app/livechat/server/lib/Contacts.ts | 4 ++++ packages/rest-typings/src/v1/omnichannel.ts | 18 ++++++++++++++++ 3 files changed, 41 insertions(+), 2 deletions(-) diff --git a/apps/meteor/app/livechat/server/api/v1/contact.ts b/apps/meteor/app/livechat/server/api/v1/contact.ts index 7e9457d2f185..3db077e7062e 100644 --- a/apps/meteor/app/livechat/server/api/v1/contact.ts +++ b/apps/meteor/app/livechat/server/api/v1/contact.ts @@ -1,11 +1,15 @@ import { LivechatCustomField, LivechatVisitors } from '@rocket.chat/models'; -import { isPOSTOmnichannelContactsProps, isPOSTUpdateOmnichannelContactsProps } from '@rocket.chat/rest-typings'; +import { + isPOSTOmnichannelContactsProps, + isPOSTUpdateOmnichannelContactsProps, + isGETOmnichannelContactsProps, +} from '@rocket.chat/rest-typings'; import { escapeRegExp } from '@rocket.chat/string-helpers'; import { Match, check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; import { API } from '../../../../api/server'; -import { Contacts, createContact, updateContact } from '../../lib/Contacts'; +import { Contacts, createContact, updateContact, getContactById } from '../../lib/Contacts'; API.v1.addRoute( 'omnichannel/contact', @@ -101,6 +105,7 @@ API.v1.addRoute( }, }, ); + API.v1.addRoute( 'omnichannel/contacts.update', { authRequired: true, permissionsRequired: ['update-livechat-contact'], validateParams: isPOSTUpdateOmnichannelContactsProps }, @@ -116,3 +121,15 @@ API.v1.addRoute( }, }, ); + +API.v1.addRoute( + 'omnichannel/contacts.get', + { authRequired: true, permissionsRequired: ['view-livechat-contact'], validateParams: isGETOmnichannelContactsProps }, + { + async get() { + const contact = await getContactById(this.queryParams.contactId); + + return API.v1.success({ contact }); + }, + }, +); diff --git a/apps/meteor/app/livechat/server/lib/Contacts.ts b/apps/meteor/app/livechat/server/lib/Contacts.ts index f6f812ce8af8..1fea0bb2f5e2 100644 --- a/apps/meteor/app/livechat/server/lib/Contacts.ts +++ b/apps/meteor/app/livechat/server/lib/Contacts.ts @@ -248,6 +248,10 @@ export async function updateContact(params: UpdateContactParams): Promise { + return LivechatContacts.findOneById(contactId); +} + async function getAllowedCustomFields(): Promise { return LivechatCustomField.findByScope( 'visitor', diff --git a/packages/rest-typings/src/v1/omnichannel.ts b/packages/rest-typings/src/v1/omnichannel.ts index 1ed249f5dd55..a1c714c013b8 100644 --- a/packages/rest-typings/src/v1/omnichannel.ts +++ b/packages/rest-typings/src/v1/omnichannel.ts @@ -1304,6 +1304,21 @@ const POSTUpdateOmnichannelContactsSchema = { export const isPOSTUpdateOmnichannelContactsProps = ajv.compile(POSTUpdateOmnichannelContactsSchema); +type GETOmnichannelContactsProps = { contactId: string }; + +const GETOmnichannelContactsSchema = { + type: 'object', + properties: { + contactId: { + type: 'string', + }, + }, + required: ['contactId'], + additionalProperties: false, +}; + +export const isGETOmnichannelContactsProps = ajv.compile(GETOmnichannelContactsSchema); + type GETOmnichannelContactProps = { contactId: string }; const GETOmnichannelContactSchema = { @@ -3748,6 +3763,9 @@ export type OmnichannelEndpoints = { '/v1/omnichannel/contacts.update': { POST: (params: POSTUpdateOmnichannelContactsProps) => { contact: ILivechatContact }; }; + '/v1/omnichannel/contacts.get': { + GET: (params: GETOmnichannelContactsProps) => { contact: ILivechatContact | null }; + }; '/v1/omnichannel/contact.search': { GET: (params: GETOmnichannelContactSearchProps) => { contact: ILivechatVisitor | null }; From 3279158137901f4f15c3c50cd10283753d0297aa Mon Sep 17 00:00:00 2001 From: Rafael Tapia Date: Tue, 13 Aug 2024 16:42:18 -0300 Subject: [PATCH 3/5] test: ensure that endpoint is working as expected --- .../tests/end-to-end/api/livechat/contacts.ts | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/apps/meteor/tests/end-to-end/api/livechat/contacts.ts b/apps/meteor/tests/end-to-end/api/livechat/contacts.ts index 957d22ba92ae..c33ef255c25c 100644 --- a/apps/meteor/tests/end-to-end/api/livechat/contacts.ts +++ b/apps/meteor/tests/end-to-end/api/livechat/contacts.ts @@ -569,4 +569,67 @@ describe('LIVECHAT - contacts', () => { }); }); }); + + describe('[GET] omnichannel/contacts.get', () => { + let contactId: string; + const contact = { + name: faker.person.fullName(), + emails: [faker.internet.email().toLowerCase()], + phones: [faker.phone.number()], + contactManager: agentUser?._id, + }; + + before(async () => { + await updatePermission('view-livechat-contact', ['admin']); + const { body } = await request + .post(api('omnichannel/contacts')) + .set(credentials) + .send({ ...contact }); + contactId = body.contactId; + }); + + after(async () => { + await restorePermissionToRoles('view-livechat-contact'); + }); + + it('should be able get a contact by id', async () => { + const res = await request.get(api(`omnichannel/contacts.get`)).set(credentials).query({ contactId }); + + expect(res.status).to.be.equal(200); + expect(res.body).to.have.property('success', true); + expect(res.body.contact._id).to.be.equal(contactId); + expect(res.body.contact.name).to.be.equal(contact.name); + expect(res.body.contact.emails).to.be.deep.equal(contact.emails); + expect(res.body.contact.phones).to.be.deep.equal(contact.phones); + expect(res.body.contact.contactManager).to.be.equal(contact.contactManager); + }); + + it('should return null if contact does not exist', async () => { + const res = await request.get(api(`omnichannel/contacts.get`)).set(credentials).query({ contactId: 'invalid' }); + + expect(res.status).to.be.equal(200); + expect(res.body).to.have.property('success', true); + expect(res.body.contact).to.be.null; + }); + + it("should return an error if user doesn't have 'view-livechat-contact' permission", async () => { + await removePermissionFromAllRoles('view-livechat-contact'); + + const res = await request.get(api(`omnichannel/contacts.get`)).set(credentials).query({ contactId }); + + expect(res.body).to.have.property('success', false); + expect(res.body.error).to.be.equal('User does not have the permissions required for this action [error-unauthorized]'); + + await restorePermissionToRoles('view-livechat-contact'); + }); + + it('should return an error if contactId is missing', async () => { + const res = await request.get(api(`omnichannel/contacts.get`)).set(credentials); + + expect(res.body).to.have.property('success', false); + expect(res.body).to.have.property('error'); + expect(res.body.error).to.be.equal("must have required property 'contactId' [invalid-params]"); + expect(res.body.errorType).to.be.equal('invalid-params'); + }); + }); }); From cb5e7d0bf7c7911ea3d214dee4b701879e04b9db Mon Sep 17 00:00:00 2001 From: Rafael Tapia Date: Fri, 6 Sep 2024 15:15:16 -0300 Subject: [PATCH 4/5] fix: only allow endpoint in test mode --- apps/meteor/app/livechat/server/api/v1/contact.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/meteor/app/livechat/server/api/v1/contact.ts b/apps/meteor/app/livechat/server/api/v1/contact.ts index 3db077e7062e..3f3eafad997b 100644 --- a/apps/meteor/app/livechat/server/api/v1/contact.ts +++ b/apps/meteor/app/livechat/server/api/v1/contact.ts @@ -127,6 +127,9 @@ API.v1.addRoute( { authRequired: true, permissionsRequired: ['view-livechat-contact'], validateParams: isGETOmnichannelContactsProps }, { async get() { + if (process.env.TEST_MODE?.toUpperCase() !== 'TRUE') { + throw new Meteor.Error('error-not-allowed', 'This endpoint is only allowed in test mode'); + } const contact = await getContactById(this.queryParams.contactId); return API.v1.success({ contact }); From 81d6c8782eb44368cfed380b8031872c216ea1d4 Mon Sep 17 00:00:00 2001 From: Rafael Tapia Date: Wed, 11 Sep 2024 15:37:13 -0300 Subject: [PATCH 5/5] refactor: call model directly --- apps/meteor/app/livechat/server/api/v1/contact.ts | 6 +++--- apps/meteor/app/livechat/server/lib/Contacts.ts | 4 ---- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/apps/meteor/app/livechat/server/api/v1/contact.ts b/apps/meteor/app/livechat/server/api/v1/contact.ts index 3f3eafad997b..f3fec80b23fe 100644 --- a/apps/meteor/app/livechat/server/api/v1/contact.ts +++ b/apps/meteor/app/livechat/server/api/v1/contact.ts @@ -1,4 +1,4 @@ -import { LivechatCustomField, LivechatVisitors } from '@rocket.chat/models'; +import { LivechatContacts, LivechatCustomField, LivechatVisitors } from '@rocket.chat/models'; import { isPOSTOmnichannelContactsProps, isPOSTUpdateOmnichannelContactsProps, @@ -9,7 +9,7 @@ import { Match, check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; import { API } from '../../../../api/server'; -import { Contacts, createContact, updateContact, getContactById } from '../../lib/Contacts'; +import { Contacts, createContact, updateContact } from '../../lib/Contacts'; API.v1.addRoute( 'omnichannel/contact', @@ -130,7 +130,7 @@ API.v1.addRoute( if (process.env.TEST_MODE?.toUpperCase() !== 'TRUE') { throw new Meteor.Error('error-not-allowed', 'This endpoint is only allowed in test mode'); } - const contact = await getContactById(this.queryParams.contactId); + const contact = await LivechatContacts.findOneById(this.queryParams.contactId); return API.v1.success({ contact }); }, diff --git a/apps/meteor/app/livechat/server/lib/Contacts.ts b/apps/meteor/app/livechat/server/lib/Contacts.ts index 1fea0bb2f5e2..f6f812ce8af8 100644 --- a/apps/meteor/app/livechat/server/lib/Contacts.ts +++ b/apps/meteor/app/livechat/server/lib/Contacts.ts @@ -248,10 +248,6 @@ export async function updateContact(params: UpdateContactParams): Promise { - return LivechatContacts.findOneById(contactId); -} - async function getAllowedCustomFields(): Promise { return LivechatCustomField.findByScope( 'visitor',