Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: broken error messages on room.saveInfo & missing CF validations on omni/contact api #28367

Merged
merged 20 commits into from
May 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/blue-cougars-wave.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@rocket.chat/meteor": patch
---

fix: broken error messages on room.saveInfo & missing CF validations on omni/contact api
8 changes: 7 additions & 1 deletion apps/meteor/app/livechat/server/api/v1/room.ts
Original file line number Diff line number Diff line change
Expand Up @@ -448,7 +448,13 @@ API.v1.addRoute(
delete guestData.phone;
}

await Promise.allSettled([Livechat.saveGuest(guestData, this.userId), Livechat.saveRoomInfo(roomData)]);
// We want this both operations to be concurrent, so we have to go with Promise.allSettled
const result = await Promise.allSettled([Livechat.saveGuest(guestData, this.userId), Livechat.saveRoomInfo(roomData)]);

const firstError = result.find((item) => item.status === 'rejected');
if (firstError) {
throw new Error((firstError as PromiseRejectedResult).reason.error);
}

await callbacks.run('livechat.saveInfo', await LivechatRooms.findOneById(roomData._id), {
user: this.user,
Expand Down
42 changes: 33 additions & 9 deletions apps/meteor/app/livechat/server/lib/Contacts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import type { MatchKeysAndValues, OnlyFieldsOfType } from 'mongodb';
import { LivechatVisitors, Users, LivechatRooms, LivechatCustomField, LivechatInquiry, Rooms, Subscriptions } from '@rocket.chat/models';
import type { ILivechatCustomField, ILivechatVisitor, IOmnichannelRoom } from '@rocket.chat/core-typings';

import { trim } from '../../../../lib/utils/stringUtils';
import { i18n } from '../../../utils/lib/i18n';

type RegisterContactProps = {
_id?: string;
token: string;
Expand Down Expand Up @@ -68,16 +71,37 @@ export const Contacts = {
}
}

const allowedCF = await LivechatCustomField.findByScope<Pick<ILivechatCustomField, '_id'>>('visitor', { projection: { _id: 1 } })
.map(({ _id }) => _id)
.toArray();
const allowedCF = LivechatCustomField.findByScope<Pick<ILivechatCustomField, '_id' | 'label' | 'regexp' | 'required'>>('visitor', {
projection: { _id: 1, label: 1, regexp: 1, required: 1 },
});

const livechatData: Record<string, string> = {};

const livechatData = Object.keys(customFields)
.filter((key) => allowedCF.includes(key) && customFields[key] !== '' && customFields[key] !== undefined)
.reduce((obj: Record<string, unknown | string>, key) => {
obj[key] = customFields[key];
return obj;
}, {});
for await (const cf of allowedCF) {
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 }));
}
}

livechatData[cf._id] = cfValue;
}

const updateUser: { $set: MatchKeysAndValues<ILivechatVisitor>; $unset?: OnlyFieldsOfType<ILivechatVisitor> } = {
$set: {
Expand Down
71 changes: 70 additions & 1 deletion apps/meteor/tests/end-to-end/api/livechat/00-rooms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { LivechatPriorityWeight } from '@rocket.chat/core-typings';
import type { Response } from 'supertest';
import faker from '@faker-js/faker';

import { getCredentials, api, request, credentials } from '../../../data/api-data';
import { getCredentials, api, request, credentials, methodCall } from '../../../data/api-data';
import {
createVisitor,
createLivechatRoom,
Expand Down Expand Up @@ -1600,6 +1600,75 @@ describe('LIVECHAT - rooms', function () {
.expect('Content-Type', 'application/json')
.expect(400);
});
(IS_EE ? it : it.skip)('should throw an error if a valid custom field fails the check', async () => {
await request
.post(methodCall('livechat:saveCustomField'))
.set(credentials)
.send({
message: JSON.stringify({
method: 'livechat:saveCustomField',
params: [
null,
{
field: 'intfield',
label: 'intfield',
scope: 'room',
visibility: 'visible',
regexp: '\\d+',
searchable: true,
type: 'input',
required: false,
defaultValue: '0',
options: '',
public: false,
},
],
id: 'id',
msg: 'method',
}),
})
.expect(200);
const newVisitor = await createVisitor();
const newRoom = await createLivechatRoom(newVisitor.token);

const response = await request
.post(api('livechat/room.saveInfo'))
.set(credentials)
.send({
roomData: {
_id: newRoom._id,
livechatData: { intfield: 'asdasd' },
},
guestData: {
_id: newVisitor._id,
},
})
.expect('Content-Type', 'application/json')
.expect(400);
expect(response.body).to.have.property('success', false);
expect(response.body).to.have.property('error', 'Invalid value for intfield field');
});
(IS_EE ? it : it.skip)('should not throw an error if a valid custom field passes the check', async () => {
const newVisitor = await createVisitor();
const newRoom = await createLivechatRoom(newVisitor.token);

const response2 = await request
.post(api('livechat/room.saveInfo'))
.set(credentials)
.send({
roomData: {
_id: newRoom._id,
livechatData: { intfield: '1' },
},
guestData: {
_id: newVisitor._id,
},
})
.expect('Content-Type', 'application/json')
.expect(200);
expect(response2.body).to.have.property('success', true);
});

(IS_EE ? it : it.skip)('should update room priority', async () => {
await addPermissions({
'save-others-livechat-room-info': ['admin', 'livechat-manager'],
Expand Down