Skip to content

Conversation

@abhinavkrin
Copy link
Member

@abhinavkrin abhinavkrin commented Jan 19, 2026

Proposed changes (including videos or screenshots)

This PR fixes an issue where the Contact Manager field could not be cleared (set to empty) for a contact, either via the UI or through the API.

The PR brings changes which will allow clearing the contactManager field by broviding an empty string for the 'contactManager' field in the payload for the endpoint omnichannel/contacts.update.

Issue(s)

Steps to test or reproduce

Further comments

SUP-954

Summary by CodeRabbit

  • Bug Fixes

    • Endpoints now allow clearing the contact manager field when an empty or missing value is provided (applies to update and conflict-resolution flows).
    • Improved error handling when a contact cannot be found during update/conflict resolution.
  • Tests

    • Added end-to-end tests confirming contact manager can be removed via update and during conflict resolution.

✏️ Tip: You can customize this high-level summary in your review settings.

Signed-off-by: Abhinav Kumar <abhinav@avitechlab.com>
Signed-off-by: Abhinav Kumar <abhinav@avitechlab.com>
@abhinavkrin abhinavkrin requested review from a team as code owners January 19, 2026 19:43
@dionisio-bot
Copy link
Contributor

dionisio-bot bot commented Jan 19, 2026

Looks like this PR is not ready to merge, because of the following issues:

  • This PR is missing the 'stat: QA assured' label
  • This PR is targeting the wrong base branch. It should target 8.2.0, but it targets 8.1.0

Please fix the issues and try again

If you have any trouble, please check the PR guidelines

@changeset-bot
Copy link

changeset-bot bot commented Jan 19, 2026

🦋 Changeset detected

Latest commit: 264eaa1

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 40 packages
Name Type
@rocket.chat/models Patch
@rocket.chat/meteor Patch
@rocket.chat/core-services Patch
@rocket.chat/cron Patch
@rocket.chat/instance-status Patch
@rocket.chat/omni-core Patch
@rocket.chat/ui-client Patch
@rocket.chat/account-service Patch
@rocket.chat/authorization-service Patch
@rocket.chat/ddp-streamer Patch
@rocket.chat/omnichannel-transcript Patch
@rocket.chat/presence-service Patch
@rocket.chat/queue-worker Patch
@rocket.chat/abac Patch
@rocket.chat/federation-matrix Patch
@rocket.chat/media-calls Patch
@rocket.chat/omni-core-ee Patch
@rocket.chat/omnichannel-services Patch
@rocket.chat/presence Patch
rocketchat-services Patch
@rocket.chat/network-broker Patch
@rocket.chat/core-typings Patch
@rocket.chat/rest-typings Patch
@rocket.chat/uikit-playground Patch
@rocket.chat/api-client Patch
@rocket.chat/apps Patch
@rocket.chat/ddp-client Patch
@rocket.chat/fuselage-ui-kit Patch
@rocket.chat/gazzodown Patch
@rocket.chat/http-router Patch
@rocket.chat/livechat Patch
@rocket.chat/model-typings Patch
@rocket.chat/ui-avatar Patch
@rocket.chat/ui-contexts Patch
@rocket.chat/ui-voip Patch
@rocket.chat/web-ui-registration Patch
@rocket.chat/license Patch
@rocket.chat/pdf-worker Patch
@rocket.chat/mock-providers Patch
@rocket.chat/ui-video-conf Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 19, 2026

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Replaces contact update flow with a patch-style API that applies $set and conditional $unset (enabling clearing of contactManager), renames updateContactpatchContact in models and typings, updates server handlers and conflict resolution to use the patch API, and adds end-to-end tests plus a changeset.

Changes

Cohort / File(s) Summary
Changeset
\.changeset/thick-ties-hunt.md
Adds a changeset documenting patch bumps and notes about fixes to clearing contactManager.
Model typings
packages/model-typings/src/models/ILivechatContactsModel.ts
Replaces updateContact(...) signature with patchContact(contactId, { set?, unset? }, options?) returning `ILivechatContact
Models implementation
packages/models/src/models/LivechatContacts.ts
Adds patchContact that accepts { set, unset }, returns early if no changes, builds $set (including unknown and derived preRegistration) and conditional $unset, and uses findOneAndUpdate to return the updated contact or null.
Server handlers
apps/meteor/app/livechat/server/lib/contacts/updateContact.ts, apps/meteor/app/livechat/server/lib/contacts/resolveContactConflicts.ts
Switches callers to patchContact; sends { set: ..., unset: [...] }; uses 'contactManager' in params to decide unsetting; throws error-contact-not-found when patch returns null.
Tests
apps/meteor/tests/end-to-end/api/livechat/contacts.ts
Adds end-to-end tests verifying clearing contactManager via omnichannel/contacts.update and during omnichannel/contacts.conflicts, asserting the field is removed.

Sequence Diagram(s)

sequenceDiagram
    participant Client as Client (UI/API)
    participant Server as Omnichannel API
    participant Model as LivechatContacts.model
    participant DB as MongoDB
    participant Post as Post-update flows

    rect rgba(200,220,255,0.5)
    Client->>Server: POST omnichannel/contacts.update (contactId, contactManager: "")
    end

    rect rgba(200,255,200,0.5)
    Server->>Model: patchContact(contactId, { set: {...}, unset: ['contactManager']? })
    Model->>DB: findOneAndUpdate({ _id, enabled: { $ne: false } }, { $set, $unset? })
    DB-->>Model: updatedContact | null
    Model-->>Server: updatedContact | null
    end

    rect rgba(255,230,200,0.5)
    Server->>Post: trigger name/room/subscription/inquiry updates using updatedContact
    Server-->>Client: HTTP 200 { success: true, contact: updatedContact }
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • test: Fix ignored unit tests #38288 — Touches livechat contacts update/resolveContactConflicts codepaths and relates to moving update → patch and tests around contact functions.

Suggested labels

stat: ready to merge, stat: QA assured

Suggested reviewers

  • ggazzo

Poem

🐰 I hopped to the DB with a wish and a wink,
Cleared the manager field quicker than you'd think.
With $set and $unset in tidy array rows,
Empty strings vanish — off the contact goes! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title accurately summarizes the main change: fixing the inability to clear the contact manager field, which aligns with the primary objective.
Linked Issues check ✅ Passed The PR implements the required functionality from SUP-954: allowing users to clear the Contact Manager field via the omnichannel/contacts.update endpoint by accepting an empty string.
Out of Scope Changes check ✅ Passed All changes are directly related to the objective of clearing the contact manager field, including API updates, model changes, and end-to-end tests.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/clear-contact-manager-field

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@abhinavkrin abhinavkrin changed the title Fix/clear contact manager field fix: unable to clear contact manager field Jan 19, 2026
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 4 files

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/models/src/models/LivechatContacts.ts (1)

117-133: Remove the inline comment on line 122 to follow coding guidelines.

Per the coding guidelines for TypeScript files, avoid code comments in implementation. The comment explaining the contactManager unsetting intent is unnecessary—the code logic is clear from the conditional check and operation.

The MongoDB behavior regarding empty $unset objects is handled correctly for the driver versions in use (6.10.0+). However, the main issue is the inline comment that violates the stated guideline.

🧹 Nitpick comments (1)
apps/meteor/app/livechat/server/lib/contacts/updateContact.ts (1)

74-82: Logic is correct for enabling contactManager clearing.

The use of 'contactManager' in params correctly detects key presence regardless of value, enabling the model layer to handle empty strings as unset operations. The validation at lines 39-41 appropriately only triggers for truthy values.

However, as per coding guidelines for TypeScript files, consider removing the inline comment on line 78. The behavior is self-documenting through the 'in' operator usage, and the model layer's handling can be documented in the model itself if needed.

🔧 Suggested change
 	const updatedContact = await LivechatContacts.updateContact(contactId, {
 		name,
 		...(emails && { emails: emails?.map((address) => ({ address })) }),
 		...(phones && { phones: phones?.map((phoneNumber) => ({ phoneNumber })) }),
-		...('contactManager' in params && { contactManager }), // A contactManager of empty string or undefined will be unset in the model method
+		...('contactManager' in params && { contactManager }),
 		...(channels && { channels }),
 		...(customFieldsToUpdate && { customFields: customFieldsToUpdate }),
 		...(wipeConflicts && { conflictingFields: [] }),
 	});

@codecov
Copy link

codecov bot commented Jan 19, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 70.76%. Comparing base (90243eb) to head (264eaa1).
⚠️ Report is 19 commits behind head on develop.

Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff             @@
##           develop   #38265      +/-   ##
===========================================
+ Coverage    70.70%   70.76%   +0.05%     
===========================================
  Files         3139     3142       +3     
  Lines       108744   108926     +182     
  Branches     19560    19622      +62     
===========================================
+ Hits         76887    77078     +191     
+ Misses       29856    29847       -9     
  Partials      2001     2001              
Flag Coverage Δ
e2e 60.32% <ø> (+0.01%) ⬆️
e2e-api 48.03% <ø> (+0.07%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions
Copy link
Contributor

github-actions bot commented Jan 19, 2026

📦 Docker Image Size Report

➡️ Changes

Service Current Baseline Change Percent
sum of all images 0B 0B 0B
account-service 0B 0B 0B
authorization-service 0B 0B 0B
ddp-streamer-service 0B 0B 0B
omnichannel-transcript-service 0B 0B 0B
presence-service 0B 0B 0B
queue-worker-service 0B 0B 0B
rocketchat 0B 0B 0B

📊 Historical Trend

---
config:
  theme: "dark"
  xyChart:
    width: 900
    height: 400
---
xychart
  title "Image Size Evolution by Service (Last 30 Days + This PR)"
  x-axis ["11/18 22:53", "11/19 23:02", "11/21 16:49", "11/24 17:34", "11/27 22:32", "11/28 19:05", "12/01 23:01", "12/02 21:57", "12/03 21:00", "12/04 18:17", "12/05 21:56", "12/08 20:15", "12/09 22:17", "12/10 23:26", "12/11 21:56", "12/12 22:45", "12/13 01:34", "12/15 22:31", "12/16 22:18", "12/17 21:04", "12/18 23:12", "12/19 23:27", "12/20 21:03", "12/22 18:54", "12/23 16:16", "12/24 19:38", "12/25 17:51", "12/26 13:18", "12/29 19:01", "12/30 20:52", "01/22 17:42 (PR)"]
  y-axis "Size (GB)" 0 --> 0.5
  line "account-service" [0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.00]
  line "authorization-service" [0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.00]
  line "ddp-streamer-service" [0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.00]
  line "omnichannel-transcript-service" [0.14, 0.14, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.00]
  line "presence-service" [0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.00]
  line "queue-worker-service" [0.14, 0.14, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.00]
  line "rocketchat" [0.35, 0.35, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.00]
Loading

Statistics (last 30 days):

  • 📊 Average: 1.5GiB
  • ⬇️ Minimum: 1.4GiB
  • ⬆️ Maximum: 1.6GiB
  • 🎯 Current PR: 0B
ℹ️ About this report

This report compares Docker image sizes from this build against the develop baseline.

  • Tag: pr-38265
  • Baseline: develop
  • Timestamp: 2026-01-22 17:42:13 UTC
  • Historical data points: 30

Updated: Thu, 22 Jan 2026 17:42:14 GMT

Copy link
Contributor

@ricardogarim ricardogarim left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, might the same issue exist in resolveContactConflicts.ts for the omnichannel/contacts.conflicts endpoint? It also uses a truthy check which would prevent clearing the contactManager field.

Comment on lines 118 to 119
const $set = { ...data, unknown: false, ...(data.channels && { preRegistration: !data.channels.length }) };
const $unset: Partial<Record<keyof ILivechatContact, ''>> = {};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WDYT on moving the set/unset decision logic from the model layer (LivechatContacts.ts) to the business layer (updateContact.ts)? The model should act as a pure database repository, receiving explicit $set and $unset data, while the business layer decides what to set/unset based on rules like contactManager === null || ''.

Signed-off-by: Abhinav Kumar <abhinav@avitechlab.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/meteor/app/livechat/server/lib/contacts/resolveContactConflicts.ts (1)

46-69: Contact-manager conflicts won’t clear when contactManager is emptied.

Clearing contactManager now uses unset, but fieldsToRemove only removes manager when contactManager is truthy. With an empty string, the conflict remains. Align this with the same condition used for unset.

🐛 Proposed fix
-		const fieldsToRemove = new Set<string>(
-			[
-				name && 'name',
-				contactManager && 'manager',
+		const fieldsToRemove = new Set<string>(
+			[
+				name && 'name',
+				('contactManager' in params) && 'manager',
 				...(customFields ? Object.keys(customFields).map((key) => `customFields.${key}`) : []),
 			].filter((field): field is string => !!field),
 		);
🤖 Fix all issues with AI agents
In `@apps/meteor/app/livechat/server/lib/contacts/updateContact.ts`:
- Around line 74-85: The patchContact call is including name even when
undefined, which can overwrite existing names; update the
LivechatContacts.patchContact invocation in updateContact.ts to only add name to
the set object when params.name is explicitly provided (e.g., check for
params.hasOwnProperty('name') or typeof name !== 'undefined') so that the set
payload omits name when not present; keep the other conditional spreads (emails,
phones, contactManager, channels, customFieldsToUpdate, wipeConflicts) as-is and
retain the existing unset logic for contactManager.
🧹 Nitpick comments (1)
apps/meteor/tests/end-to-end/api/livechat/contacts.ts (1)

795-836: Drop inline comments in test body.

Guideline asks to avoid code comments in implementation; these can be removed without losing intent.

♻️ Proposed cleanup
-			// Create a conflict
 			await request.post(api('livechat/custom.field')).send({ token, key: 'cf1', value: '111', overwrite: true }).expect(200);

 			await request.post(api('livechat/custom.field')).send({ token, key: 'cf1', value: '222', overwrite: false }).expect(200);

-			// Verify the contact has a contact manager and conflicts
 			const contactBefore = await request.get(api('omnichannel/contacts.get')).set(credentials).query({ contactId }).expect(200);
As per coding guidelines, avoid code comments in implementation.

Signed-off-by: Abhinav Kumar <abhinav@avitechlab.com>
Signed-off-by: Abhinav Kumar <abhinav@avitechlab.com>
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 1 file (changes from recent commits).

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="apps/meteor/app/livechat/server/lib/contacts/updateContact.ts">

<violation number="1" location="apps/meteor/app/livechat/server/lib/contacts/updateContact.ts:76">
P2: Using a truthy check here skips updates when `name` is an empty string, but downstream logic still treats `name` as a change and updates rooms/subscriptions. This can leave the contact name unchanged while related data is cleared. Prefer checking for undefined so empty strings are persisted consistently.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.


const $set = {
...set,
unknown: false,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

even if set is empty (no updates) this will issue a query to make the contact known (and if the contact is known, then it would issue a no-op query)

abhinavkrin and others added 3 commits January 22, 2026 22:54
Signed-off-by: Abhinav Kumar <abhinav@avitechlab.com>
Signed-off-by: Abhinav Kumar <abhinav@avitechlab.com>
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
Comment on lines +127 to +129
if (Object.keys(set).length === 0 && unset.length === 0) {
return this.findOne({ _id: contactId, enabled: { $ne: false } });
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd suggest doing this before calling patchContact 👀

@alfredodelfabro alfredodelfabro added this to the 8.2.0 milestone Jan 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants