Skip to content
Snippets Groups Projects
Unverified Commit 0570f674 authored by Kevin Aleman's avatar Kevin Aleman Committed by GitHub
Browse files

fix: `livechat/visitor` not updating custom fields some times (#31890)

parent 469afbd4
No related branches found
No related tags found
No related merge requests found
---
"@rocket.chat/meteor": patch
"@rocket.chat/model-typings": patch
---
Changed logic that process custom fields from visitors when updating its data, making the process more reliable and faster.
import type { ILivechatVisitor, IRoom } from '@rocket.chat/core-typings';
import type { ILivechatCustomField, ILivechatVisitor, IRoom } from '@rocket.chat/core-typings';
import { LivechatVisitors as VisitorsRaw, LivechatCustomField, LivechatRooms } from '@rocket.chat/models';
import { Match, check } from 'meteor/check';
import { Meteor } from 'meteor/meteor';
......@@ -68,16 +68,46 @@ API.v1.addRoute('livechat/visitor', {
);
}
if (customFields && Array.isArray(customFields)) {
for await (const field of customFields) {
const customField = await LivechatCustomField.findOneById(field.key);
if (!customField) {
continue;
}
const { key, value, overwrite } = field;
if (customField.scope === 'visitor' && !(await VisitorsRaw.updateLivechatDataByToken(token, key, value, overwrite))) {
return API.v1.failure();
}
if (customFields && Array.isArray(customFields) && customFields.length > 0) {
const keys = customFields.map((field) => field.key);
const errors: string[] = [];
const processedKeys = await Promise.all(
await LivechatCustomField.findByIdsAndScope<Pick<ILivechatCustomField, '_id'>>(keys, 'visitor', {
projection: { _id: 1 },
})
.map(async (field) => {
const customField = customFields.find((f) => f.key === field._id);
if (!customField) {
return;
}
const { key, value, overwrite } = customField;
// TODO: Change this to Bulk update
if (!(await VisitorsRaw.updateLivechatDataByToken(token, key, value, overwrite))) {
errors.push(key);
}
return key;
})
.toArray(),
);
if (processedKeys.length !== keys.length) {
LivechatTyped.logger.warn({
msg: 'Some custom fields were not processed',
visitorId,
missingKeys: keys.filter((key) => !processedKeys.includes(key)),
});
}
if (errors.length > 0) {
LivechatTyped.logger.error({
msg: 'Error updating custom fields',
visitorId,
errors,
});
throw new Error('error-updating-custom-fields');
}
visitor = await VisitorsRaw.findOneEnabledById(visitorId, {});
......
import type { ILivechatCustomField, RocketChatRecordDeleted } from '@rocket.chat/core-typings';
import type { ILivechatCustomFieldModel } from '@rocket.chat/model-typings';
import type { Db, Collection, IndexDescription, FindOptions, FindCursor } from 'mongodb';
import type { Db, Collection, IndexDescription, FindOptions, FindCursor, Document } from 'mongodb';
import { BaseRaw } from './BaseRaw';
......@@ -73,4 +73,12 @@ export class LivechatCustomFieldRaw extends BaseRaw<ILivechatCustomField> implem
return record;
}
findByIdsAndScope<T extends Document = ILivechatCustomField>(
ids: ILivechatCustomField['_id'][],
scope: ILivechatCustomField['scope'],
options?: FindOptions<ILivechatCustomField>,
): FindCursor<T> {
return this.find<T>({ _id: { $in: ids }, scope }, options);
}
}
......@@ -115,6 +115,105 @@ describe('LIVECHAT - visitors', function () {
expect(body2.visitor).to.have.property('phone');
expect(body2.visitor.phone[0].phoneNumber).to.equal(phone);
});
it('should update a visitor custom fields when customFields key is provided', async () => {
const token = `${new Date().getTime()}-test`;
const customFieldName = `new_custom_field_${Date.now()}`;
await createCustomField({
searchable: true,
field: customFieldName,
label: customFieldName,
defaultValue: 'test_default_address',
scope: 'visitor',
visibility: 'public',
regexp: '',
});
const { body } = await request.post(api('livechat/visitor')).send({
visitor: {
token,
customFields: [{ key: customFieldName, value: 'Not a real address :)', overwrite: true }],
},
});
expect(body).to.have.property('success', true);
expect(body).to.have.property('visitor');
expect(body.visitor).to.have.property('token', token);
expect(body.visitor).to.have.property('livechatData');
expect(body.visitor.livechatData).to.have.property(customFieldName, 'Not a real address :)');
});
it('should not update a custom field when it does not exists', async () => {
const token = `${new Date().getTime()}-test`;
const customFieldName = `new_custom_field_${Date.now()}`;
const { body } = await request.post(api('livechat/visitor')).send({
visitor: {
token,
customFields: [{ key: customFieldName, value: 'Not a real address :)', overwrite: true }],
},
});
expect(body).to.have.property('success', true);
expect(body).to.have.property('visitor');
expect(body.visitor).to.have.property('token', token);
expect(body.visitor).to.not.have.property('livechatData');
});
it('should not update a custom field when the scope of it is not visitor', async () => {
const token = `${new Date().getTime()}-test`;
const customFieldName = `new_custom_field_${Date.now()}`;
await createCustomField({
searchable: true,
field: customFieldName,
label: customFieldName,
defaultValue: 'test_default_address',
scope: 'room',
visibility: 'public',
regexp: '',
});
const { body } = await request.post(api('livechat/visitor')).send({
visitor: {
token,
customFields: [{ key: customFieldName, value: 'Not a real address :)', overwrite: true }],
},
});
expect(body).to.have.property('success', true);
expect(body).to.have.property('visitor');
expect(body.visitor).to.have.property('token', token);
expect(body.visitor).to.not.have.property('livechatData');
});
it('should not update a custom field whe the overwrite flag is false', async () => {
const token = `${new Date().getTime()}-test`;
const customFieldName = `new_custom_field_${Date.now()}`;
await createCustomField({
searchable: true,
field: customFieldName,
label: customFieldName,
defaultValue: 'test_default_address',
scope: 'visitor',
visibility: 'public',
regexp: '',
});
await request.post(api('livechat/visitor')).send({
visitor: {
token,
customFields: [{ key: customFieldName, value: 'Not a real address :)', overwrite: true }],
},
});
const { body } = await request.post(api('livechat/visitor')).send({
visitor: {
token,
customFields: [{ key: customFieldName, value: 'This should not change!', overwrite: false }],
},
});
expect(body).to.have.property('success', true);
expect(body).to.have.property('visitor');
expect(body.visitor).to.have.property('token', token);
expect(body.visitor).to.have.property('livechatData');
expect(body.visitor.livechatData).to.have.property(customFieldName, 'Not a real address :)');
});
});
describe('livechat/visitors.info', () => {
......
......@@ -34,4 +34,9 @@ export interface ILivechatCustomFieldModel extends IBaseModel<ILivechatCustomFie
visibility: ILivechatCustomField['visibility'],
extraData: any,
): Promise<ILivechatCustomField>;
findByIdsAndScope<T extends Document = ILivechatCustomField>(
ids: ILivechatCustomField['_id'][],
scope: ILivechatCustomField['scope'],
options?: FindOptions<ILivechatCustomField>,
): FindCursor<T>;
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment