chore: use updater in Livechat Contacts' `updateContactChannel` (#33980)

pull/34124/head^2
Matheus Barbosa Silva 1 year ago committed by GitHub
parent f62326080d
commit e1bb39d710
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 2
      apps/meteor/app/livechat/server/lib/Helper.ts
  2. 2
      apps/meteor/ee/app/livechat-enterprise/server/api/lib/contacts.ts
  3. 18
      apps/meteor/ee/server/patches/verifyContactChannel.ts
  4. 55
      apps/meteor/ee/tests/unit/apps/livechat-enterprise/server/lib/verifyContactChannel.spec.ts
  5. 4
      apps/meteor/server/models/raw/BaseRaw.ts
  6. 45
      apps/meteor/server/models/raw/LivechatContacts.ts
  7. 10
      packages/model-typings/src/models/ILivechatContactsModel.ts

@ -97,7 +97,7 @@ export const createLivechatRoom = async (
const source = extraRoomInfo.source || roomInfo.source;
if (settings.get<string>('Livechat_Require_Contact_Verification') === 'always') {
await LivechatContacts.updateContactChannel({ visitorId: _id, source }, { verified: false });
await LivechatContacts.setChannelVerifiedStatus({ visitorId: _id, source }, false);
}
const contactId = await migrateVisitorIfMissingContact(_id, source);

@ -6,7 +6,7 @@ import { Livechat } from '../../../../../../app/livechat/server/lib/LivechatType
import { i18n } from '../../../../../../server/lib/i18n';
export async function changeContactBlockStatus({ block, visitor }: { visitor: ILivechatContactVisitorAssociation; block: boolean }) {
const result = await LivechatContacts.updateContactChannel(visitor, { blocked: block });
const result = await LivechatContacts.setChannelBlockStatus(visitor, block);
if (!result.modifiedCount) {
throw new Error('error-contact-not-found');

@ -29,20 +29,10 @@ async function _verifyContactChannel(
session.startTransaction();
logger.debug({ msg: 'Start verifying contact channel', contactId, visitorId, roomId });
await LivechatContacts.updateContactChannel(
{
visitorId,
source: room.source,
},
{
verified: true,
verifiedAt: new Date(),
field,
value: value.toLowerCase(),
},
{},
{ session },
);
const updater = LivechatContacts.getUpdater();
LivechatContacts.setVerifiedUpdateQuery(true, updater);
LivechatContacts.setFieldAndValueUpdateQuery(field, value.toLowerCase(), updater);
await LivechatContacts.updateFromUpdaterByAssociation({ visitorId, source: room.source }, updater, { session });
await LivechatRooms.update({ _id: roomId }, { $set: { verified: true } }, { session });
logger.debug({ msg: 'Merging contacts', contactId, visitorId, roomId });

@ -4,7 +4,10 @@ import sinon from 'sinon';
const modelsMock = {
LivechatContacts: {
updateContactChannel: sinon.stub(),
getUpdater: sinon.stub(),
setVerifiedUpdateQuery: sinon.stub(),
setFieldAndValueUpdateQuery: sinon.stub(),
updateFromUpdaterByAssociation: sinon.stub(),
},
LivechatRooms: {
update: sinon.stub(),
@ -44,7 +47,10 @@ const { runVerifyContactChannel } = proxyquire.noCallThru().load('../../../../..
describe('verifyContactChannel', () => {
beforeEach(() => {
modelsMock.LivechatContacts.updateContactChannel.reset();
modelsMock.LivechatContacts.getUpdater.reset();
modelsMock.LivechatContacts.setVerifiedUpdateQuery.reset();
modelsMock.LivechatContacts.setFieldAndValueUpdateQuery.reset();
modelsMock.LivechatContacts.updateFromUpdaterByAssociation.reset();
modelsMock.LivechatRooms.update.reset();
modelsMock.LivechatInquiry.findOneByRoomId.reset();
modelsMock.LivechatRooms.findOneById.reset();
@ -55,6 +61,8 @@ describe('verifyContactChannel', () => {
mergeContactsStub.reset();
queueManager.processNewInquiry.reset();
queueManager.verifyInquiry.reset();
modelsMock.LivechatContacts.getUpdater.returns({});
});
afterEach(() => {
@ -68,24 +76,23 @@ describe('verifyContactChannel', () => {
await runVerifyContactChannel(() => undefined, {
contactId: 'contactId',
field: 'field',
value: 'value',
value: 'Value',
visitorId: 'visitorId',
roomId: 'roomId',
});
expect(modelsMock.LivechatContacts.getUpdater.calledOnce).to.be.true;
expect(modelsMock.LivechatContacts.setVerifiedUpdateQuery.calledOnceWith(true, {})).to.be.true;
expect(modelsMock.LivechatContacts.setFieldAndValueUpdateQuery.calledOnceWith('field', 'value', {})).to.be.true;
expect(
modelsMock.LivechatContacts.updateContactChannel.calledOnceWith(
modelsMock.LivechatContacts.updateFromUpdaterByAssociation.calledOnceWith(
sinon.match({
visitorId: 'visitorId',
source: sinon.match({
type: 'sms',
}),
}),
sinon.match({
verified: true,
field: 'field',
value: 'value',
}),
{},
),
).to.be.true;
expect(modelsMock.LivechatRooms.update.calledOnceWith({ _id: 'roomId' }, { $set: { verified: true } })).to.be.true;
@ -116,21 +123,21 @@ describe('verifyContactChannel', () => {
roomId: 'roomId',
});
expect(modelsMock.LivechatContacts.getUpdater.calledOnce).to.be.true;
expect(modelsMock.LivechatContacts.setVerifiedUpdateQuery.calledOnceWith(true, {})).to.be.true;
expect(modelsMock.LivechatContacts.setFieldAndValueUpdateQuery.calledOnceWith('field', 'value', {})).to.be.true;
expect(
modelsMock.LivechatContacts.updateContactChannel.calledOnceWith(
modelsMock.LivechatContacts.updateFromUpdaterByAssociation.calledOnceWith(
sinon.match({
visitorId: 'visitorId',
source: sinon.match({
type: 'sms',
}),
}),
sinon.match({
verified: true,
field: 'field',
value: 'value',
}),
{},
),
).to.be.true;
expect(modelsMock.LivechatRooms.update.calledOnceWith({ _id: 'roomId' }, { $set: { verified: true } })).to.be.true;
expect(
mergeContactsStub.calledOnceWith(
@ -160,7 +167,11 @@ describe('verifyContactChannel', () => {
}),
).to.be.rejectedWith('error-invalid-room');
expect(modelsMock.LivechatContacts.updateContactChannel.notCalled).to.be.true;
expect(modelsMock.LivechatContacts.getUpdater.notCalled).to.be.true;
expect(modelsMock.LivechatContacts.setVerifiedUpdateQuery.notCalled).to.be.true;
expect(modelsMock.LivechatContacts.setFieldAndValueUpdateQuery.notCalled).to.be.true;
expect(modelsMock.LivechatContacts.updateFromUpdaterByAssociation.notCalled).to.be.true;
expect(modelsMock.LivechatRooms.update.notCalled).to.be.true;
expect(mergeContactsStub.notCalled).to.be.true;
expect(queueManager.verifyInquiry.notCalled).to.be.true;
@ -180,21 +191,21 @@ describe('verifyContactChannel', () => {
}),
).to.be.rejectedWith('error-invalid-inquiry');
expect(modelsMock.LivechatContacts.getUpdater.calledOnce).to.be.true;
expect(modelsMock.LivechatContacts.setVerifiedUpdateQuery.calledOnceWith(true, {})).to.be.true;
expect(modelsMock.LivechatContacts.setFieldAndValueUpdateQuery.calledOnceWith('field', 'value', {})).to.be.true;
expect(
modelsMock.LivechatContacts.updateContactChannel.calledOnceWith(
modelsMock.LivechatContacts.updateFromUpdaterByAssociation.calledOnceWith(
sinon.match({
visitorId: 'visitorId',
source: sinon.match({
type: 'sms',
}),
}),
sinon.match({
verified: true,
field: 'field',
value: 'value',
}),
{},
),
).to.be.true;
expect(modelsMock.LivechatRooms.update.calledOnceWith({ _id: 'roomId' }, { $set: { verified: true } })).to.be.true;
expect(
mergeContactsStub.calledOnceWith(

@ -124,9 +124,9 @@ export abstract class BaseRaw<
return new UpdaterImpl<T>();
}
public updateFromUpdater(query: Filter<T>, updater: Updater<T>): Promise<UpdateResult> {
public updateFromUpdater(query: Filter<T>, updater: Updater<T>, options: UpdateOptions = {}): Promise<UpdateResult> {
const updateFilter = updater.getUpdateFilter();
return this.updateOne(query, updateFilter).catch((e) => {
return this.updateOne(query, updateFilter, options).catch((e) => {
console.warn(e, updateFilter);
return Promise.reject(e);
});

@ -6,7 +6,7 @@ import type {
ILivechatVisitor,
RocketChatRecordDeleted,
} from '@rocket.chat/core-typings';
import type { FindPaginated, ILivechatContactsModel, InsertionModel } from '@rocket.chat/model-typings';
import type { FindPaginated, ILivechatContactsModel, InsertionModel, Updater } from '@rocket.chat/model-typings';
import { escapeRegExp } from '@rocket.chat/string-helpers';
import type {
Document,
@ -198,24 +198,37 @@ export class LivechatContactsRaw extends BaseRaw<ILivechatContact> implements IL
return Boolean(await this.findOne(this.makeQueryForVisitor(visitor, { blocked: true }), { projection: { _id: 1 } }));
}
async updateContactChannel(
setChannelBlockStatus(visitor: ILivechatContactVisitorAssociation, blocked: boolean): Promise<UpdateResult> {
return this.updateOne(this.makeQueryForVisitor(visitor), { $set: { 'channels.$.blocked': blocked } });
}
setChannelVerifiedStatus(visitor: ILivechatContactVisitorAssociation, verified: boolean): Promise<UpdateResult> {
return this.updateOne(this.makeQueryForVisitor(visitor), {
$set: {
'channels.$.verified': verified,
...(verified && { 'channels.$.verifiedAt': new Date() }),
},
});
}
setVerifiedUpdateQuery(verified: boolean, contactUpdater: Updater<ILivechatContact>): Updater<ILivechatContact> {
if (verified) {
contactUpdater.set('channels.$.verifiedAt', new Date());
}
return contactUpdater.set('channels.$.verified', verified);
}
setFieldAndValueUpdateQuery(field: string, value: string, contactUpdater: Updater<ILivechatContact>): Updater<ILivechatContact> {
contactUpdater.set('channels.$.field', field);
return contactUpdater.set('channels.$.value', value);
}
updateFromUpdaterByAssociation(
visitor: ILivechatContactVisitorAssociation,
data: Partial<ILivechatContactChannel>,
contactData?: Partial<Omit<ILivechatContact, 'channels'>>,
contactUpdater: Updater<ILivechatContact>,
options: UpdateOptions = {},
): Promise<UpdateResult> {
return this.updateOne(
this.makeQueryForVisitor(visitor),
{
$set: {
...contactData,
...(Object.fromEntries(
Object.keys(data).map((key) => [`channels.$.${key}`, data[key as keyof ILivechatContactChannel]]),
) as UpdateFilter<ILivechatContact>['$set']),
},
},
options,
);
return this.updateFromUpdater(this.makeQueryForVisitor(visitor), contactUpdater, options);
}
async findSimilarVerifiedContacts(

@ -7,6 +7,7 @@ import type {
} from '@rocket.chat/core-typings';
import type { Document, FindCursor, FindOneAndUpdateOptions, FindOptions, UpdateFilter, UpdateOptions, UpdateResult } from 'mongodb';
import type { Updater } from '../updater';
import type { FindPaginated, IBaseModel, InsertionModel } from './IBaseModel';
export interface ILivechatContactsModel extends IBaseModel<ILivechatContact> {
@ -31,10 +32,9 @@ export interface ILivechatContactsModel extends IBaseModel<ILivechatContact> {
options?: FindOptions<ILivechatContact>,
): Promise<T | null>;
isChannelBlocked(visitor: ILivechatContactVisitorAssociation): Promise<boolean>;
updateContactChannel(
updateFromUpdaterByAssociation(
visitor: ILivechatContactVisitorAssociation,
data: Partial<ILivechatContactChannel>,
contactData?: Partial<Omit<ILivechatContact, 'channels'>>,
contactUpdater: Updater<ILivechatContact>,
options?: UpdateOptions,
): Promise<UpdateResult>;
findSimilarVerifiedContacts(
@ -44,4 +44,8 @@ export interface ILivechatContactsModel extends IBaseModel<ILivechatContact> {
): Promise<ILivechatContact[]>;
findAllByVisitorId(visitorId: string): FindCursor<ILivechatContact>;
addEmail(contactId: string, email: string): Promise<ILivechatContact | null>;
setChannelBlockStatus(visitor: ILivechatContactVisitorAssociation, blocked: boolean): Promise<UpdateResult>;
setChannelVerifiedStatus(visitor: ILivechatContactVisitorAssociation, verified: boolean): Promise<UpdateResult>;
setVerifiedUpdateQuery(verified: boolean, contactUpdater: Updater<ILivechatContact>): Updater<ILivechatContact>;
setFieldAndValueUpdateQuery(field: string, value: string, contactUpdater: Updater<ILivechatContact>): Updater<ILivechatContact>;
}

Loading…
Cancel
Save