Skip to content
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
7 changes: 7 additions & 0 deletions .changeset/rich-steaks-promise.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@rocket.chat/meteor": patch
"@rocket.chat/model-typings": patch
"@rocket.chat/models": patch
---

Fixed contacts being marked as `known` after editing a custom field, or resolving conflicts by adding a new model function that only updates the `customFields` or `conflictingFields` prop.
2 changes: 1 addition & 1 deletion apps/meteor/app/livechat/server/lib/custom-fields.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export async function updateContactsCustomFields(contact: ILivechatContact, key:
contact.conflictingFields.push({ field: `customFields.${key}`, value });
}

await LivechatContacts.updateContact(contact._id, {
await LivechatContacts.updateContactCustomFields(contact._id, {
...(shouldUpdateCustomFields && { customFields: contact.customFields }),
...(contact.conflictingFields && { conflictingFields: contact.conflictingFields }),
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import sinon from 'sinon';

const modelsMock = {
LivechatContacts: {
updateContact: sinon.stub(),
updateContactCustomFields: sinon.stub(),
},
};

Expand All @@ -15,7 +15,7 @@ const { updateContactsCustomFields } = proxyquire.noCallThru().load('../../../..

describe('[Custom Fields] updateContactsCustomFields', () => {
beforeEach(() => {
modelsMock.LivechatContacts.updateContact.reset();
modelsMock.LivechatContacts.updateContactCustomFields.reset();
});

it('should not add conflictingFields to the update data when its nullish', async () => {
Expand All @@ -26,13 +26,13 @@ describe('[Custom Fields] updateContactsCustomFields', () => {
},
};

modelsMock.LivechatContacts.updateContact.resolves({ ...contact, customFields: { customField: 'newValue' } });
modelsMock.LivechatContacts.updateContactCustomFields.resolves({ ...contact, customFields: { customField: 'newValue' } });

await updateContactsCustomFields(contact, 'customField', 'newValue', true);

expect(modelsMock.LivechatContacts.updateContact.calledOnce).to.be.true;
expect(modelsMock.LivechatContacts.updateContact.getCall(0).args[0]).to.be.equal('contactId');
expect(modelsMock.LivechatContacts.updateContact.getCall(0).args[1]).to.deep.equal({
expect(modelsMock.LivechatContacts.updateContactCustomFields.calledOnce).to.be.true;
expect(modelsMock.LivechatContacts.updateContactCustomFields.getCall(0).args[0]).to.be.equal('contactId');
expect(modelsMock.LivechatContacts.updateContactCustomFields.getCall(0).args[1]).to.deep.equal({
customFields: { customField: 'newValue' },
});
});
Expand All @@ -45,16 +45,16 @@ describe('[Custom Fields] updateContactsCustomFields', () => {
},
};

modelsMock.LivechatContacts.updateContact.resolves({
modelsMock.LivechatContacts.updateContactCustomFields.resolves({
...contact,
conflictingFields: [{ field: 'customFields.customField', value: 'newValue' }],
});

await updateContactsCustomFields(contact, 'customField', 'newValue', false);

expect(modelsMock.LivechatContacts.updateContact.calledOnce).to.be.true;
expect(modelsMock.LivechatContacts.updateContact.getCall(0).args[0]).to.be.equal('contactId');
expect(modelsMock.LivechatContacts.updateContact.getCall(0).args[1]).to.deep.equal({
expect(modelsMock.LivechatContacts.updateContactCustomFields.calledOnce).to.be.true;
expect(modelsMock.LivechatContacts.updateContactCustomFields.getCall(0).args[0]).to.be.equal('contactId');
expect(modelsMock.LivechatContacts.updateContactCustomFields.getCall(0).args[1]).to.deep.equal({
conflictingFields: [{ field: 'customFields.customField', value: 'newValue' }],
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export interface ILivechatContactsModel extends IBaseModel<ILivechatContact> {
): Promise<ILivechatContact['_id']>;
updateContact(contactId: string, data: Partial<ILivechatContact>, options?: FindOneAndUpdateOptions): Promise<ILivechatContact>;
updateById(contactId: string, update: UpdateFilter<ILivechatContact>, options?: UpdateOptions): Promise<Document | UpdateResult>;
updateContactCustomFields(contactId: string, data: Partial<ILivechatContact>, options?: UpdateOptions): Promise<ILivechatContact | null>;
addChannel(contactId: string, channel: ILivechatContactChannel): Promise<void>;
findPaginatedContacts(
search: { searchText?: string; unknown?: boolean },
Expand Down
19 changes: 19 additions & 0 deletions packages/models/src/models/LivechatContacts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type {
AtLeast,
ILivechatContact,
ILivechatContactChannel,
ILivechatContactConflictingField,
ILivechatContactVisitorAssociation,
ILivechatVisitor,
RocketChatRecordDeleted,
Expand Down Expand Up @@ -126,6 +127,24 @@ export class LivechatContactsRaw extends BaseRaw<ILivechatContact> implements IL
return this.updateOne({ _id: contactId }, update, options);
}

async updateContactCustomFields(
contactId: string,
dataToUpdate: { customFields: Record<string, unknown>; conflictingFields: ILivechatContactConflictingField[] },
options?: FindOneAndUpdateOptions,
): Promise<ILivechatContact | null> {
if (!dataToUpdate.customFields && !dataToUpdate.conflictingFields) {
throw new Error('At least one of customFields or conflictingFields must be provided');
}

return this.findOneAndUpdate(
{ _id: contactId },
{
$set: { ...dataToUpdate },
},
{ returnDocument: 'after', ...options },
);
}

findPaginatedContacts(
search: { searchText?: string; unknown?: boolean },
options?: FindOptions,
Expand Down
Loading