Skip to content

Commit

Permalink
feat: update contact endpoint (#32729)
Browse files Browse the repository at this point in the history
  • Loading branch information
tapiarafael committed Sep 3, 2024
1 parent 304bc6d commit 9eaefdc
Show file tree
Hide file tree
Showing 11 changed files with 499 additions and 18 deletions.
6 changes: 6 additions & 0 deletions .changeset/heavy-snails-help.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@rocket.chat/meteor": minor
"@rocket.chat/rest-typings": minor
---

Implemented "omnichannel/contacts.update" endpoint to update contacts
4 changes: 4 additions & 0 deletions apps/meteor/app/authorization/server/constant/permissions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,10 @@ export const permissions = [
_id: 'create-livechat-contact',
roles: ['livechat-manager', 'livechat-monitor', 'livechat-agent', 'admin'],
},
{
_id: 'update-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',
Expand Down
19 changes: 17 additions & 2 deletions apps/meteor/app/livechat/server/api/v1/contact.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { LivechatCustomField, LivechatVisitors } from '@rocket.chat/models';
import { isPOSTOmnichannelContactsProps } from '@rocket.chat/rest-typings';
import { isPOSTOmnichannelContactsProps, isPOSTUpdateOmnichannelContactsProps } 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 } from '../../lib/Contacts';
import { Contacts, createContact, updateContact } from '../../lib/Contacts';

API.v1.addRoute(
'omnichannel/contact',
Expand Down Expand Up @@ -101,3 +101,18 @@ API.v1.addRoute(
},
},
);
API.v1.addRoute(
'omnichannel/contacts.update',
{ authRequired: true, permissionsRequired: ['update-livechat-contact'], validateParams: isPOSTUpdateOmnichannelContactsProps },
{
async post() {
if (!process.env.TEST_MODE) {
throw new Meteor.Error('error-not-allowed', 'This endpoint is only allowed in test mode');
}

const contact = await updateContact({ ...this.bodyParams });

return API.v1.success({ contact });
},
},
);
61 changes: 56 additions & 5 deletions apps/meteor/app/livechat/server/lib/Contacts.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import type { ILivechatContactChannel, ILivechatCustomField, ILivechatVisitor, IOmnichannelRoom, IUser } from '@rocket.chat/core-typings';
import type {
ILivechatContact,
ILivechatContactChannel,
ILivechatCustomField,
ILivechatVisitor,
IOmnichannelRoom,
IUser,
} from '@rocket.chat/core-typings';
import {
LivechatVisitors,
Users,
Expand Down Expand Up @@ -45,6 +52,16 @@ type CreateContactParams = {
channels?: ILivechatContactChannel[];
};

type UpdateContactParams = {
contactId: string;
name?: string;
emails?: string[];
phones?: string[];
customFields?: Record<string, unknown>;
contactManager?: string;
channels?: ILivechatContactChannel[];
};

export const Contacts = {
async registerContact({
token,
Expand Down Expand Up @@ -189,10 +206,7 @@ export async function createContact(params: CreateContactParams): Promise<string
const { name, emails, phones, customFields = {}, contactManager, channels, unknown } = params;

if (contactManager) {
const contactManagerUser = await Users.findOneAgentById<Pick<IUser, 'roles'>>(contactManager, { projection: { roles: 1 } });
if (!contactManagerUser) {
throw new Error('error-contact-manager-not-found');
}
await validateContactManager(contactManager);
}

const allowedCustomFields = await getAllowedCustomFields();
Expand All @@ -211,6 +225,29 @@ export async function createContact(params: CreateContactParams): Promise<string
return insertedId;
}

export async function updateContact(params: UpdateContactParams): Promise<ILivechatContact> {
const { contactId, name, emails, phones, customFields, contactManager, channels } = params;

const contact = await LivechatContacts.findOneById<Pick<ILivechatContact, '_id'>>(contactId, { projection: { _id: 1 } });

if (!contact) {
throw new Error('error-contact-not-found');
}

if (contactManager) {
await validateContactManager(contactManager);
}

if (customFields) {
const allowedCustomFields = await getAllowedCustomFields();
validateCustomFields(allowedCustomFields, customFields);
}

const updatedContact = await LivechatContacts.updateContact(contactId, { name, emails, phones, contactManager, channels, customFields });

return updatedContact;
}

async function getAllowedCustomFields(): Promise<ILivechatCustomField[]> {
return LivechatCustomField.findByScope(
'visitor',
Expand Down Expand Up @@ -245,4 +282,18 @@ export function validateCustomFields(allowedCustomFields: ILivechatCustomField[]
}
}
}

const allowedCustomFieldIds = new Set(allowedCustomFields.map((cf) => cf._id));
for (const key in customFields) {
if (!allowedCustomFieldIds.has(key)) {
throw new Error(i18n.t('error-custom-field-not-allowed', { key }));
}
}
}

export async function validateContactManager(contactManagerUserId: string) {
const contactManagerUser = await Users.findOneAgentById<Pick<IUser, '_id'>>(contactManagerUserId, { projection: { _id: 1 } });
if (!contactManagerUser) {
throw new Error('error-contact-manager-not-found');
}
}
9 changes: 9 additions & 0 deletions apps/meteor/server/models/raw/LivechatContacts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,13 @@ export class LivechatContactsRaw extends BaseRaw<ILivechatContact> implements IL
constructor(db: Db, trash?: Collection<RocketChatRecordDeleted<ILivechatContact>>) {
super(db, 'livechat_contact', trash);
}

async updateContact(contactId: string, data: Partial<ILivechatContact>): Promise<ILivechatContact> {
const updatedValue = await this.findOneAndUpdate(
{ _id: contactId },
{ $set: { ...data, unknown: false } },
{ returnDocument: 'after' },
);
return updatedValue.value as ILivechatContact;
}
}
Loading

0 comments on commit 9eaefdc

Please sign in to comment.