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', diff --git a/apps/meteor/app/livechat/server/api/v1/contact.ts b/apps/meteor/app/livechat/server/api/v1/contact.ts index 7e9457d2f185..f3fec80b23fe 100644 --- a/apps/meteor/app/livechat/server/api/v1/contact.ts +++ b/apps/meteor/app/livechat/server/api/v1/contact.ts @@ -1,5 +1,9 @@ -import { LivechatCustomField, LivechatVisitors } from '@rocket.chat/models'; -import { isPOSTOmnichannelContactsProps, isPOSTUpdateOmnichannelContactsProps } from '@rocket.chat/rest-typings'; +import { LivechatContacts, LivechatCustomField, LivechatVisitors } from '@rocket.chat/models'; +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'; @@ -101,6 +105,7 @@ API.v1.addRoute( }, }, ); + API.v1.addRoute( 'omnichannel/contacts.update', { authRequired: true, permissionsRequired: ['update-livechat-contact'], validateParams: isPOSTUpdateOmnichannelContactsProps }, @@ -116,3 +121,18 @@ API.v1.addRoute( }, }, ); + +API.v1.addRoute( + 'omnichannel/contacts.get', + { 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 LivechatContacts.findOneById(this.queryParams.contactId); + + return API.v1.success({ contact }); + }, + }, +); 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'); + }); + }); }); 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 };