Skip to content

Commit

Permalink
feat: create contact endpoint (#32693)
Browse files Browse the repository at this point in the history
  • Loading branch information
tapiarafael committed Aug 23, 2024
1 parent 27f9249 commit 927710d
Show file tree
Hide file tree
Showing 15 changed files with 555 additions and 4 deletions.
9 changes: 9 additions & 0 deletions .changeset/sixty-spoons-own.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"@rocket.chat/meteor": minor
"@rocket.chat/core-typings": minor
"@rocket.chat/model-typings": minor
"@rocket.chat/models": minor
"@rocket.chat/rest-typings": minor
---

Introduced "create contacts" endpoint to omnichannel
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 @@ -93,6 +93,10 @@ export const permissions = [
_id: 'view-l-room',
roles: ['livechat-manager', 'livechat-monitor', 'livechat-agent', 'admin'],
},
{
_id: 'create-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
23 changes: 21 additions & 2 deletions apps/meteor/app/livechat/server/api/v1/contact.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
import { LivechatCustomField, LivechatVisitors } from '@rocket.chat/models';
import { isPOSTOmnichannelContactsProps } 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 } from '../../lib/Contacts';
import { Contacts, createContact } from '../../lib/Contacts';

API.v1.addRoute(
'omnichannel/contact',
{ authRequired: true, permissionsRequired: ['view-l-room'] },
{
authRequired: true,
permissionsRequired: ['view-l-room'],
},
{
async post() {
check(this.bodyParams, {
Expand Down Expand Up @@ -82,3 +86,18 @@ API.v1.addRoute(
},
},
);

API.v1.addRoute(
'omnichannel/contacts',
{ authRequired: true, permissionsRequired: ['create-livechat-contact'], validateParams: isPOSTOmnichannelContactsProps },
{
async post() {
if (!process.env.TEST_MODE) {
throw new Meteor.Error('error-not-allowed', 'This endpoint is only allowed in test mode');
}
const contactId = await createContact({ ...this.bodyParams, unknown: false });

return API.v1.success({ contactId });
},
},
);
85 changes: 83 additions & 2 deletions apps/meteor/app/livechat/server/lib/Contacts.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
import type { ILivechatCustomField, ILivechatVisitor, IOmnichannelRoom } from '@rocket.chat/core-typings';
import { LivechatVisitors, Users, LivechatRooms, LivechatCustomField, LivechatInquiry, Rooms, Subscriptions } from '@rocket.chat/models';
import type { ILivechatContactChannel, ILivechatCustomField, ILivechatVisitor, IOmnichannelRoom, IUser } from '@rocket.chat/core-typings';
import {
LivechatVisitors,
Users,
LivechatRooms,
LivechatCustomField,
LivechatInquiry,
Rooms,
Subscriptions,
LivechatContacts,
} from '@rocket.chat/models';
import { check } from 'meteor/check';
import { Meteor } from 'meteor/meteor';
import type { MatchKeysAndValues, OnlyFieldsOfType } from 'mongodb';
Expand All @@ -26,6 +35,16 @@ type RegisterContactProps = {
};
};

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

export const Contacts = {
async registerContact({
token,
Expand Down Expand Up @@ -165,3 +184,65 @@ export const Contacts = {
return contactId;
},
};

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');
}
}

const allowedCustomFields = await getAllowedCustomFields();
validateCustomFields(allowedCustomFields, customFields);

const { insertedId } = await LivechatContacts.insertOne({
name,
emails,
phones,
contactManager,
channels,
customFields,
unknown,
});

return insertedId;
}

async function getAllowedCustomFields(): Promise<ILivechatCustomField[]> {
return LivechatCustomField.findByScope(
'visitor',
{
projection: { _id: 1, label: 1, regexp: 1, required: 1 },
},
false,
).toArray();
}

export function validateCustomFields(allowedCustomFields: ILivechatCustomField[], customFields: Record<string, string | unknown>) {
for (const cf of allowedCustomFields) {
if (!customFields.hasOwnProperty(cf._id)) {
if (cf.required) {
throw new Error(i18n.t('error-invalid-custom-field-value', { field: cf.label }));
}
continue;
}
const cfValue: string = trim(customFields[cf._id]);

if (!cfValue || typeof cfValue !== 'string') {
if (cf.required) {
throw new Error(i18n.t('error-invalid-custom-field-value', { field: cf.label }));
}
continue;
}

if (cf.regexp) {
const regex = new RegExp(cf.regexp);
if (!regex.test(cfValue)) {
throw new Error(i18n.t('error-invalid-custom-field-value', { field: cf.label }));
}
}
}
}
6 changes: 6 additions & 0 deletions apps/meteor/server/models/LivechatContacts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { registerModel } from '@rocket.chat/models';

import { db } from '../database/utils';
import { LivechatContactsRaw } from './raw/LivechatContacts';

registerModel('ILivechatContactsModel', new LivechatContactsRaw(db));
11 changes: 11 additions & 0 deletions apps/meteor/server/models/raw/LivechatContacts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import type { ILivechatContact, RocketChatRecordDeleted } from '@rocket.chat/core-typings';
import type { ILivechatContactsModel } from '@rocket.chat/model-typings';
import type { Collection, Db } from 'mongodb';

import { BaseRaw } from './BaseRaw';

export class LivechatContactsRaw extends BaseRaw<ILivechatContact> implements ILivechatContactsModel {
constructor(db: Db, trash?: Collection<RocketChatRecordDeleted<ILivechatContact>>) {
super(db, 'livechat_contact', trash);
}
}
1 change: 1 addition & 0 deletions apps/meteor/server/models/startup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import './Integrations';
import './Invites';
import './LivechatAgentActivity';
import './LivechatBusinessHours';
import './LivechatContacts';
import './LivechatCustomField';
import './LivechatDepartment';
import './LivechatDepartmentAgents';
Expand Down
Loading

0 comments on commit 927710d

Please sign in to comment.