Skip to content

User mentor extension update#1577

Open
sumanvpacewisdom wants to merge 7 commits intodevelopfrom
USER_MENTOR_EXTENSION_UPDATE
Open

User mentor extension update#1577
sumanvpacewisdom wants to merge 7 commits intodevelopfrom
USER_MENTOR_EXTENSION_UPDATE

Conversation

@sumanvpacewisdom
Copy link
Copy Markdown
Collaborator

@sumanvpacewisdom sumanvpacewisdom commented Feb 17, 2026

Release Notes

Key Changes

  • Partition Key Protection: Updated updateMentorExtension and updateRoleExtension functions to remove tenant_code from update payloads, preventing accidental updates to Citus partition keys
  • Data Integrity: Modified removeMentorDetails to exclude organization_code and tenant_code from nullification to prevent NOT NULL constraint violations
  • Session Query Enhancement: Updated getSessionsAssignedToMentor to accept tenantCode parameter and properly filter results by tenant
  • Circular Dependency Resolution: Introduced new organizationCache wrapper module to break circular dependency between cacheHelper and user request handlers
  • Admin Service Corrections: Fixed multiple issues in admin service including:
    • Corrected getSessionsMapping calls to pass three arguments instead of two
    • Removed invalid join with non-existent request_session_mapping table
    • Updated notification parameter naming to use singular tenantCode instead of tenantCodes
    • Improved data flow for user detail retrieval and session mapping aggregation
  • User Extension Protection: Updated updateMenteeExtension to remove partition key columns (user_id, tenant_code) before update operations

Lines Changed by Author

Author Lines Added Lines Removed
sumanvpacewisdom 116 31

sumanvpacewisdom and others added 7 commits February 17, 2026 08:09
… codes, and introduce organization caching functionality. Updated session retrieval logic to ensure tenant code consistency and added caching methods for organization details.
…hancing request filtering for user sessions.
…ries with tenant code. Updated AdminService to correctly handle parameters and removed erroneous JOINs, ensuring proper query execution and notification functionality.
…pdates, ensuring compliance with Citus constraints. Updated mentorExtension.js, roleExtentions.js, and userExtension.js to maintain data integrity by preventing nullification of required fields.
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Feb 17, 2026

Walkthrough

This PR refactors database queries to prevent partition key updates in Citus, introduces a new cache wrapper module to resolve circular dependencies between modules, updates session query signatures to propagate tenant codes, and corrects multiple data flow issues and parameter misalignments in the admin service affecting user deletion, session mapping, and notifications.

Changes

Cohort / File(s) Summary
Partition Key Sanitization
src/database/queries/mentorExtension.js, src/database/queries/roleExtentions.js, src/database/queries/userExtension.js
Removes tenant_code and user_id from update payloads in mentor/role/mentee update functions to prevent partition key column modifications (Citus limitation). Also excludes organization-related fields from nullification to preserve data integrity.
Session Query Enhancement
src/database/queries/sessions.js
Updates getSessionsAssignedToMentor signature to accept tenantCode parameter and adjusts SQL filtering to include tenant-code-based distribution and WHERE conditions.
Circular Dependency Resolution
src/helpers/organizationCache.js, src/requests/user.js
Introduces new wrapper module organizationCache.js that delegates to cacheHelper.organizations methods, breaking the circular dependency loop (cacheHelper → getDefaultOrgId → user.js). Replaces all direct cacheHelper cache calls in user.js with organizationCache wrapper.
Admin Service Data Flow Fixes
src/services/admin.js
Corrects user detail retrieval from array to scalar variable, updates getSessionsMapping calls to include tenantCode parameter, removes invalid table join, and aligns notification payloads to use singular tenantCode instead of tenantCodes.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • Regression fix3.2 #1348: Directly modifies getSessionsAssignedToMentor function signature and query logic in sessions.js, indicating overlapping changes to the same query function.
  • update multiple entity at same time #1401: Modifies updateMentorExtension and updateMenteeExtension functions with partition key and meta-field handling, sharing identical target functions for refactoring.
  • Email notificatios issue #1289: Changes session query functions and admin notification flows including session mapping and tenant-code propagation, directly overlapping with this PR's admin.js corrections.

Poem

🐰 Partition keys no longer slip through updates so swift,
Circular paths unwind with a cache-wrapper gift,
Tenant codes flow true through sessions and states,
Admin notifies correctly through all the data gates! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Title check ⚠️ Warning The title is only tangentially related to the changeset. While it mentions 'mentor extension update', the PR contains fixes across multiple database query files, circular dependency resolution, and substantial admin service corrections affecting user management, notifications, and session mapping—far broader than just mentor extension updates. Revise the title to reflect the full scope, such as: 'Fix partition key handling and circular dependencies across database queries and admin services' or 'Refactor mentor/user extensions and resolve caching circular dependency'.
Merge Conflict Detection ⚠️ Warning ❌ Merge conflicts detected (6 files):

⚔️ src/database/queries/mentorExtension.js (content)
⚔️ src/database/queries/roleExtentions.js (content)
⚔️ src/database/queries/sessions.js (content)
⚔️ src/database/queries/userExtension.js (content)
⚔️ src/requests/user.js (content)
⚔️ src/services/admin.js (content)

These conflicts must be resolved before merging into develop.
Resolve conflicts locally and push changes to this branch.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ 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 USER_MENTOR_EXTENSION_UPDATE
⚔️ Resolve merge conflicts (beta)
  • Auto-commit resolved conflicts to branch USER_MENTOR_EXTENSION_UPDATE
  • Create stacked PR with resolved conflicts
  • Post resolved changes as copyable diffs in a comment

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.

Copy link
Copy Markdown

@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.

Caution

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

⚠️ Outside diff range comments (6)
src/services/admin.js (2)

667-709: ⚠️ Potential issue | 🟠 Major

Mismatched property names passed to sendSessionNotification.

Lines 687-688 pass orgsCode and tenantsCode as property names, but sendSessionNotification (line 82) destructures orgCodes and tenantCodes. These mismatched names mean the notification handler receives undefined for both, which may affect template lookup fallback behavior.

🐛 Proposed fix
 			const notificationResult = await NotificationHelper.sendSessionNotification({
 				sessions: removedSessionsDetail,
 				templateCode: process.env.MENTOR_SESSION_DELETION_EMAIL_CODE,
 				orgCode,
 				recipientField: 'attendees',
 				addionalData: { nameOfTheSession: '{sessionName}' },
 				tenantCode,
-				orgsCode,
-				tenantsCode,
+				orgCodes,
+				tenantCodes,
 			})
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/services/admin.js` around lines 667 - 709, The call in
unenrollAndNotifySessionAttendees to NotificationHelper.sendSessionNotification
passes wrong property names (orgsCode and tenantsCode) that do not match the
expected destructured params (orgCodes and tenantCodes); update the payload to
use orgCodes and tenantCodes (e.g., set orgCodes: orgsCode and tenantCodes:
tenantsCode or rename the local variables) so the notification handler receives
the correct values and template lookup works as intended; ensure you update the
object passed to NotificationHelper.sendSessionNotification in
unenrollAndNotifySessionAttendees accordingly.

168-196: ⚠️ Potential issue | 🔴 Critical

Critical bug: userInfo is never assigned in the admin (isAdmin) branch, so admin-initiated deletions always return USER_NOT_FOUND.

In the isAdmin path (lines 178-182), the code sets getUserDetails (an undeclared variable — will be an implicit global or throw in strict mode) and never assigns userInfo. Since userInfo stays null (line 173), the check on line 189 always triggers the failure response.

Additionally, line 200 passes { token, userId, userTenantCode } to fetchUserDetails, but that function destructures { token, userId, tenantCode } — so tenantCode will be undefined inside the function.

🐛 Proposed fix for the admin path
 		if (isAdmin) {
 			// Admin deleting any user - no tenant code restriction
 			const userDetail = await menteeQueries.getMenteeExtensionById(userId, [], true)
 			userTenantCode = userDetail?.tenant_code
-			getUserDetails = userDetail ? [userDetail] : []
+			userInfo = userDetail || null
 		} else {
 			// Regular user deleting themselves - use tenant code from token (optimized path)
 			const getUserDetails = await menteeQueries.getUsersByUserIds([userId], {}, tenantCode)
 			userInfo = getUserDetails?.[0]
 		}

And fix the fetchUserDetails call:

-		const getUserDetailById = await userRequests.fetchUserDetails({ token, userId, userTenantCode })
+		const getUserDetailById = await userRequests.fetchUserDetails({ token, userId, tenantCode: userTenantCode })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/services/admin.js` around lines 168 - 196, In userDelete, the admin
branch never assigns userInfo and uses an undeclared getUserDetails; update the
isAdmin branch to properly capture the fetched record (assign userInfo =
userDetail or userInfo = userDetail ? userDetail : null) and avoid creating an
implicit global (don’t use getUserDetails there), and ensure userTenantCode is
set from userDetail?.tenant_code; also when calling fetchUserDetails pass the
expected key name tenantCode (i.e. { token, userId, tenantCode: userTenantCode
}) so the function that destructures { token, userId, tenantCode } receives the
correct value.
src/requests/user.js (2)

843-845: ⚠️ Potential issue | 🟠 Major

Bug: error is called as a function instead of being thrown.

throw error(error) tries to invoke the caught Error object as a function, which will throw a TypeError: error is not a function at runtime, masking the original error.

🐛 Proposed fix
 		} catch (error) {
-			throw error(error)
+			throw error
 		}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/requests/user.js` around lines 843 - 845, The catch block currently does
"throw error(error)" which attempts to call the caught Error as a function;
change it to rethrow the original error by using "throw error" (i.e., remove the
erroneous function call) so the original exception is preserved and not masked
by a TypeError in the catch block that contains that line.

168-182: ⚠️ Potential issue | 🔴 Critical

Bug: tenantCode query parameter is appended twice to profileUrl.

When tenantCode is truthy, isTenantScoped (line 170) is true, so line 178 appends ?tenant_code=…. Then lines 180-182 also fire (since tenantCode is truthy) and append a second ?tenant_code=…, producing a malformed URL like:

.../userId?tenant_code=X?tenant_code=X

Remove the duplicate block:

🐛 Proposed fix
 		if (isTenantScoped) profileUrl += `?tenant_code=${encodeURIComponent(tenantCode)}`
 
-		if (tenantCode) {
-			profileUrl += `?tenant_code=${tenantCode}`
-		}
-
 		const isInternalTokenRequired = true
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/requests/user.js` around lines 168 - 182, The fetchUserDetails function
is appending the tenant_code query parameter twice: the isTenantScoped branch
already appends `?tenant_code=${encodeURIComponent(tenantCode)}` to profileUrl,
so remove the duplicate `if (tenantCode) { profileUrl +=
\`?tenant_code=${tenantCode}\` }` block; keep the encoded append in the
isTenantScoped branch (and, if you later need to append additional query params,
ensure you use '&' instead of a second '?').
src/database/queries/sessions.js (1)

1074-1134: ⚠️ Potential issue | 🟡 Minor

Remove the duplicate export and verify the semantic change to the query filter.

The function getSessionsAssignedToMentor is defined twice (lines 1074-1100 and 1108-1134), with the second definition silently overwriting the first. Remove the dead code at lines 1074-1100.

Additionally, the new version removes the AND s.created_by = :mentorUserId filter from the WHERE clause. This broadens the query to return all sessions assigned to the mentor, not just those they created. Verify this semantic change is intentional — confirm whether updateSessionsWithAssignedMentor() in admin.js should operate on all assigned sessions or only those created by the mentor.

Remove dead code
-exports.getSessionsAssignedToMentor = async (mentorUserId, tenantCode) => {
-	try {
-		const query = `
-				SELECT s.*, sa.mentee_id
-				FROM ${Session.tableName} s
-				LEFT JOIN session_attendees sa ON s.id = sa.session_id
-				WHERE s.mentor_id = :mentorUserId 
-				AND s.tenant_code = :tenantCode
-				AND s.start_date > :currentTime
-				AND s.deleted_at IS NULL
-				AND s.created_by = :mentorUserId
-			`
-
-		const sessionsToDelete = await Sequelize.query(query, {
-			type: QueryTypes.SELECT,
-			replacements: {
-				mentorUserId,
-				currentTime: Math.floor(Date.now() / 1000),
-				tenantCode: tenantCode,
-			},
-		})
-
-		return sessionsToDelete
-	} catch (error) {
-		throw error
-	}
-}
-
 // FIX: Added tenantCode parameter and included it in JOIN and WHERE clauses
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/database/queries/sessions.js` around lines 1074 - 1134, There are two
definitions of getSessionsAssignedToMentor; remove the dead/duplicate export
(the first occurrence) so the function is defined only once, and confirm the
intended semantics: either keep the added tenant_code JOIN change (s.tenant_code
= sa.tenant_code) and leave out the s.created_by = :mentorUserId filter, or if
the original intent was to restrict to sessions created by the mentor, add back
AND s.created_by = :mentorUserId to the remaining function; also search for
updateSessionsWithAssignedMentor in admin.js and ensure it expects the broader
“all assigned sessions” result or adjust that caller if the filter should be
preserved.
src/database/queries/userExtension.js (1)

243-285: ⚠️ Potential issue | 🔴 Critical

Fix removeMenteeDetails to exclude organization_code and tenant_code from nullification

The method attempts to nullify these NOT NULL fields, which will throw a notNull Violation error. The parallel removeMentorDetails method was already corrected to exclude both fields—apply the same fix here.

Proposed fix
 			if (
 				attribute.primaryKey ||
 				key === 'user_id' ||
 				key === 'organization_id' || // required field
+				key === 'organization_code' || // required NOT NULL field - cannot be nullified
+				key === 'tenant_code' || // required NOT NULL field - cannot be nullified
 				key === 'created_at' ||
 				key === 'updated_at' ||
 				key === 'is_mentor' // has default value
 			) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/database/queries/userExtension.js` around lines 243 - 285, The
removeMenteeDetails function is currently attempting to nullify NOT NULL fields
causing notNull violations; update its loop that builds fieldsToNullify (in the
method removeMenteeDetails on MenteeExtension) to also skip keys
'organization_code' and 'tenant_code' (same pattern as the existing skips for
'organization_id' and 'user_id'), ensuring those two fields are not added to
fieldsToNullify before calling MenteeExtension.update.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@src/database/queries/sessions.js`:
- Around line 1074-1134: There are two definitions of
getSessionsAssignedToMentor; remove the dead/duplicate export (the first
occurrence) so the function is defined only once, and confirm the intended
semantics: either keep the added tenant_code JOIN change (s.tenant_code =
sa.tenant_code) and leave out the s.created_by = :mentorUserId filter, or if the
original intent was to restrict to sessions created by the mentor, add back AND
s.created_by = :mentorUserId to the remaining function; also search for
updateSessionsWithAssignedMentor in admin.js and ensure it expects the broader
“all assigned sessions” result or adjust that caller if the filter should be
preserved.

In `@src/database/queries/userExtension.js`:
- Around line 243-285: The removeMenteeDetails function is currently attempting
to nullify NOT NULL fields causing notNull violations; update its loop that
builds fieldsToNullify (in the method removeMenteeDetails on MenteeExtension) to
also skip keys 'organization_code' and 'tenant_code' (same pattern as the
existing skips for 'organization_id' and 'user_id'), ensuring those two fields
are not added to fieldsToNullify before calling MenteeExtension.update.

In `@src/requests/user.js`:
- Around line 843-845: The catch block currently does "throw error(error)" which
attempts to call the caught Error as a function; change it to rethrow the
original error by using "throw error" (i.e., remove the erroneous function call)
so the original exception is preserved and not masked by a TypeError in the
catch block that contains that line.
- Around line 168-182: The fetchUserDetails function is appending the
tenant_code query parameter twice: the isTenantScoped branch already appends
`?tenant_code=${encodeURIComponent(tenantCode)}` to profileUrl, so remove the
duplicate `if (tenantCode) { profileUrl += \`?tenant_code=${tenantCode}\` }`
block; keep the encoded append in the isTenantScoped branch (and, if you later
need to append additional query params, ensure you use '&' instead of a second
'?').

In `@src/services/admin.js`:
- Around line 667-709: The call in unenrollAndNotifySessionAttendees to
NotificationHelper.sendSessionNotification passes wrong property names (orgsCode
and tenantsCode) that do not match the expected destructured params (orgCodes
and tenantCodes); update the payload to use orgCodes and tenantCodes (e.g., set
orgCodes: orgsCode and tenantCodes: tenantsCode or rename the local variables)
so the notification handler receives the correct values and template lookup
works as intended; ensure you update the object passed to
NotificationHelper.sendSessionNotification in unenrollAndNotifySessionAttendees
accordingly.
- Around line 168-196: In userDelete, the admin branch never assigns userInfo
and uses an undeclared getUserDetails; update the isAdmin branch to properly
capture the fetched record (assign userInfo = userDetail or userInfo =
userDetail ? userDetail : null) and avoid creating an implicit global (don’t use
getUserDetails there), and ensure userTenantCode is set from
userDetail?.tenant_code; also when calling fetchUserDetails pass the expected
key name tenantCode (i.e. { token, userId, tenantCode: userTenantCode }) so the
function that destructures { token, userId, tenantCode } receives the correct
value.

Comment on lines +80 to +82
if (updateData.tenant_code) {
delete updateData['tenant_code']
}
Copy link
Copy Markdown
Collaborator

@nevil-mathew nevil-mathew Feb 17, 2026

Choose a reason for hiding this comment

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

This will be removed in the validator right if added in the req body?

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.

2 participants