From f9d85fe896a23f9181b75ea49c8621aaffc51276 Mon Sep 17 00:00:00 2001 From: sumanvpacewisdom Date: Tue, 17 Feb 2026 08:09:46 +0530 Subject: [PATCH 1/4] Enhance mentor and session queries to include organization and tenant codes, and introduce organization caching functionality. Updated session retrieval logic to ensure tenant code consistency and added caching methods for organization details. --- src/database/queries/mentorExtension.js | 2 ++ src/database/queries/sessions.js | 7 ++-- src/helpers/organizationCache.js | 17 ++++++++++ src/requests/user.js | 11 ++----- src/services/admin.js | 43 ++++++++++++------------- 5 files changed, 48 insertions(+), 32 deletions(-) create mode 100644 src/helpers/organizationCache.js diff --git a/src/database/queries/mentorExtension.js b/src/database/queries/mentorExtension.js index 522a59ecb..44550e5f3 100644 --- a/src/database/queries/mentorExtension.js +++ b/src/database/queries/mentorExtension.js @@ -163,6 +163,8 @@ module.exports = class MentorExtensionQueries { attribute.primaryKey || key === 'user_id' || key === 'organization_id' || // required field + key === 'organization_code' || // required NOT NULL field + key === 'tenant_code' || // required NOT NULL field key === 'created_at' || key === 'updated_at' || key === 'is_mentor' // has default value diff --git a/src/database/queries/sessions.js b/src/database/queries/sessions.js index a648019e1..fad36a1a3 100644 --- a/src/database/queries/sessions.js +++ b/src/database/queries/sessions.js @@ -1099,15 +1099,17 @@ exports.getSessionsAssignedToMentor = async (mentorUserId, tenantCode) => { } } -exports.getSessionsAssignedToMentor = async (mentorUserId) => { +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 = sa.tenant_code + WHERE s.mentor_id = :mentorUserId AND s.start_date > :currentTime AND s.deleted_at IS NULL + AND s.tenant_code = :tenantCode ` const sessionsToDelete = await Sequelize.query(query, { @@ -1115,6 +1117,7 @@ exports.getSessionsAssignedToMentor = async (mentorUserId) => { replacements: { mentorUserId, currentTime: Math.floor(Date.now() / 1000), + tenantCode, }, }) diff --git a/src/helpers/organizationCache.js b/src/helpers/organizationCache.js new file mode 100644 index 000000000..50b497a3e --- /dev/null +++ b/src/helpers/organizationCache.js @@ -0,0 +1,17 @@ +'use strict' + +const cacheHelper = require('@generics/cacheHelper') + +/** + * Set organization details in cache + */ +exports.set = async (tenantCode, orgCode, orgId, data) => { + return await cacheHelper.organizations.set(tenantCode, orgCode, orgId, data) +} + +/** + * Get organization details from cache + */ +exports.get = async (tenantCode, orgCode, orgId) => { + return await cacheHelper.organizations.get(tenantCode, orgCode, orgId) +} diff --git a/src/requests/user.js b/src/requests/user.js index 0a3bff754..10972c248 100644 --- a/src/requests/user.js +++ b/src/requests/user.js @@ -14,12 +14,11 @@ const httpStatusCode = require('@generics/http-status') const responses = require('@helpers/responses') const common = require('@constants/common') const { Op } = require('sequelize') -const cacheHelper = require('@generics/cacheHelper') const usersHelper = require('@helpers/users') +const organizationCache = require('@helpers/organizationCache') const menteeQueries = require('@database/queries/userExtension') const organisationExtensionQueries = require('@database/queries/organisationExtension') -// Removed cacheHelper to break circular dependency with getDefaultOrgId const emailEncryption = require('@utils/emailEncryption') const _ = require('lodash') @@ -99,7 +98,7 @@ const getOrgDetails = async function ({ organizationId, tenantCode }) { // If we got the data, populate the cache for future use if (organizationDetails?.organization_code) { try { - await cacheHelper.organizations.set( + await organizationCache.set( tenantCode, organizationDetails.organization_code, organizationId, @@ -885,11 +884,7 @@ const getUserDetailedListUsingCache = async function (userIds, tenantCode, delet const cacheResults = await Promise.all( organizations.map(async (org) => ({ org, - orgCachedData: await cacheHelper.organizations.get( - tenantCode, - org.organization_code, - org.organization_id - ), + orgCachedData: await organizationCache.get(tenantCode, org.organization_code, org.organization_id), })) ) diff --git a/src/services/admin.js b/src/services/admin.js index c00050c3e..0ac1af98a 100644 --- a/src/services/admin.js +++ b/src/services/admin.js @@ -170,29 +170,29 @@ module.exports = class AdminService { let result = {} // Step 1: Fetch user details - let getUserDetails = [] + let userInfo = null let userTenantCode = tenantCode // Optimization: If admin, query directly without tenant restriction (1 query) // If regular user (self-deletion), use tenant code from token (1 query) if (isAdmin) { // Admin deleting any user - no tenant code restriction - getUserDetails = await menteeQueries.getMenteeExtensionById(userId, [], true) - userTenantCode = getUserDetails.tenant_code + // getMenteeExtensionById returns a single object, not an array + userInfo = await menteeQueries.getMenteeExtensionById(userId, [], true) + userTenantCode = userInfo?.tenant_code } else { // Regular user deleting themselves - use tenant code from token (optimized path) - getUserDetails = await menteeQueries.getUsersByUserIds([userId], {}, tenantCode) + const getUserDetails = await menteeQueries.getUsersByUserIds([userId], {}, tenantCode) + userInfo = getUserDetails?.[0] } - if (!getUserDetails || getUserDetails.length === 0) { + if (!userInfo) { return responses.failureResponse({ statusCode: httpStatusCode.bad_request, message: 'USER_NOT_FOUND', result, }) } - - const userInfo = getUserDetails[0] const isMentor = userInfo.is_mentor === true // Step 2: Check if user is a session manager @@ -1132,7 +1132,7 @@ module.exports = class AdminService { orgCode: orgCodes, templateData: { menteeName }, subjectData: { menteeName }, - tenantCodes, + tenantCode: tenantCodes, }) } @@ -1189,7 +1189,7 @@ module.exports = class AdminService { sessionTime: sessionDateTime.format('hh:mm A'), }, subjectData: { sessionName: sessionDetails.title }, - tenantCodes, + tenantCode: tenantCodes, }) } catch (error) { console.error('Error notifying mentor about private session cancellation:', error) @@ -1414,21 +1414,20 @@ module.exports = class AdminService { orgCode: orgCodes, templateData: { mentorName }, subjectData: { mentorName }, - tenantCodes, + tenantCode: tenantCodes, }) } static async getPendingSessionRequestsForMentor(mentorUserId, tenantCode) { try { + // Query session_request directly - requestee_id is already in the table const query = ` - SELECT rs.*, rm.requestee_id - FROM ${RequestSession.tableName} rs - INNER JOIN request_session_mapping rm ON rs.id = rm.request_session_id - WHERE rm.requestee_id = :mentorUserId - AND rs.status = :requestedStatus - AND rs.deleted_at IS NULL - AND rs.tenant_code = :tenantCode - AND rm.tenant_code = :tenantCode + SELECT * + FROM ${RequestSession.tableName} + WHERE requestee_id = :mentorUserId + AND status = :requestedStatus + AND deleted_at IS NULL + AND tenant_code = :tenantCode ` const pendingRequests = await sequelize.query(query, { @@ -1475,7 +1474,7 @@ module.exports = class AdminService { orgCode: orgCodes, templateData: { sessionName: request.title }, subjectData: { sessionName: request.title }, - tenantCodes, + tenantCode: tenantCodes, }) } } @@ -1531,7 +1530,7 @@ module.exports = class AdminService { orgCode: orgCodes, templateData: { mentorName, sessionList }, subjectData: { mentorName }, - tenantCodes, + tenantCode: tenantCodes, }) } }) @@ -1588,7 +1587,7 @@ module.exports = class AdminService { orgCode: orgCodes, templateData: { menteeName: menteeName, sessionList: sessionList }, subjectData: { menteeName: menteeName }, - tenantCodes, + tenantCode: tenantCodes, }) } }) @@ -1642,7 +1641,7 @@ module.exports = class AdminService { orgCode: orgCodes, templateData: { sessionName: session.title }, subjectData: { sessionName: session.title }, - tenantCodes, + tenantCode: tenantCodes, }) } } From b0886b9bbaf292579b314dbbee25a2a89ac79124 Mon Sep 17 00:00:00 2001 From: sumanvpacewisdom Date: Tue, 17 Feb 2026 08:43:07 +0530 Subject: [PATCH 2/4] Update session request mapping query to include connection status, enhancing request filtering for user sessions. --- src/services/admin.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/services/admin.js b/src/services/admin.js index 0ac1af98a..4d44ab8c9 100644 --- a/src/services/admin.js +++ b/src/services/admin.js @@ -762,7 +762,11 @@ module.exports = class AdminService { const sentRequestsData = sentRequests.rows || [] // Get requests where user is requestee (received requests) - const sessionRequestMapping = await sessionRequestMappingQueries.getSessionsMapping(userId, tenantCode) + const sessionRequestMapping = await sessionRequestMappingQueries.getSessionsMapping( + userId, + common.CONNECTIONS_STATUS.REQUESTED, + tenantCode + ) const sessionRequestIds = Array.isArray(sessionRequestMapping) ? sessionRequestMapping.map((s) => s.request_session_id) : [] From 4312f4639406d3376cb854575d829758c93f0dc3 Mon Sep 17 00:00:00 2001 From: sumanvpacewisdom Date: Tue, 17 Feb 2026 09:29:20 +0530 Subject: [PATCH 3/4] Fix circular dependency in organization cache and enhance session queries with tenant code. Updated AdminService to correctly handle parameters and removed erroneous JOINs, ensuring proper query execution and notification functionality. --- src/database/queries/sessions.js | 6 ++++++ src/helpers/organizationCache.js | 24 ++++++++++++++++++++++++ src/requests/user.js | 3 +++ src/services/admin.js | 18 +++++++++++++++++- 4 files changed, 50 insertions(+), 1 deletion(-) diff --git a/src/database/queries/sessions.js b/src/database/queries/sessions.js index fad36a1a3..4d45ffed0 100644 --- a/src/database/queries/sessions.js +++ b/src/database/queries/sessions.js @@ -1099,6 +1099,12 @@ exports.getSessionsAssignedToMentor = async (mentorUserId, tenantCode) => { } } +// FIX: Added tenantCode parameter and included it in JOIN and WHERE clauses +// ROOT CAUSE: Citus distributed database requires JOINs to include the distribution column (tenant_code). +// Original query failed with: "complex joins are only supported when all distributed tables are +// co-located and joined on their distribution columns" +// SOLUTION: Added tenant_code to both the JOIN condition (line 1108) and WHERE clause (line 1112) +// to ensure Citus can properly route the query across distributed shards. exports.getSessionsAssignedToMentor = async (mentorUserId, tenantCode) => { try { const query = ` diff --git a/src/helpers/organizationCache.js b/src/helpers/organizationCache.js index 50b497a3e..23093a9b8 100644 --- a/src/helpers/organizationCache.js +++ b/src/helpers/organizationCache.js @@ -1,5 +1,29 @@ 'use strict' +/** + * Organization Cache Helper + * + * FIX: Created this wrapper to break circular dependency + * + * ROOT CAUSE: Circular dependency chain caused "getDefaults is not a function" error: + * cacheHelper.js → getDefaultOrgId.js → user.js → cacheHelper.js + * + * When Node.js loads these modules: + * 1. cacheHelper.js starts loading, imports getDefaultOrgId.js + * 2. getDefaultOrgId.js imports user.js (for userRequests.fetchOrgDetails) + * 3. user.js imports cacheHelper.js + * 4. cacheHelper.js is not fully loaded yet, returns partial/empty exports + * 5. getDefaults function is undefined at this point + * + * SOLUTION: user.js now imports this wrapper instead of cacheHelper directly. + * This breaks the cycle because: + * cacheHelper.js → getDefaultOrgId.js → user.js → organizationCache.js → cacheHelper.js + * + * Even though there's still a path to cacheHelper, the actual cacheHelper functions + * are only called at RUNTIME (inside set/get functions), not at MODULE LOAD TIME. + * By the time these functions are called, all modules are fully loaded. + */ + const cacheHelper = require('@generics/cacheHelper') /** diff --git a/src/requests/user.js b/src/requests/user.js index 10972c248..b7e003b80 100644 --- a/src/requests/user.js +++ b/src/requests/user.js @@ -15,6 +15,9 @@ const responses = require('@helpers/responses') const common = require('@constants/common') const { Op } = require('sequelize') const usersHelper = require('@helpers/users') +// FIX: Use organizationCache wrapper instead of cacheHelper directly +// This breaks the circular dependency: cacheHelper → getDefaultOrgId → user.js → cacheHelper +// See helpers/organizationCache.js for detailed explanation const organizationCache = require('@helpers/organizationCache') const menteeQueries = require('@database/queries/userExtension') diff --git a/src/services/admin.js b/src/services/admin.js index ffd8ffbdc..7052491ad 100644 --- a/src/services/admin.js +++ b/src/services/admin.js @@ -762,6 +762,12 @@ module.exports = class AdminService { const sentRequestsData = sentRequests.rows || [] // Get requests where user is requestee (received requests) + // FIX: Added missing 'status' parameter to getSessionsMapping call + // ROOT CAUSE: Function signature is getSessionsMapping(userId, status, tenantCode) but was called + // with only (userId, tenantCode). This caused tenantCode to be passed as 'status' parameter, + // and the actual tenantCode parameter received 'undefined'. + // Error: "WHERE parameter 'tenant_code' has invalid 'undefined' value" + // SOLUTION: Pass all three parameters in correct order: userId, status, tenantCode const sessionRequestMapping = await sessionRequestMappingQueries.getSessionsMapping( userId, common.CONNECTIONS_STATUS.REQUESTED, @@ -1411,6 +1417,12 @@ module.exports = class AdminService { } } + // FIX: Changed 'tenantCodes,' to 'tenantCode: tenantCodes,' + // ROOT CAUSE: Using shorthand 'tenantCodes,' creates property {tenantCodes: tenantCodes} + // but sendGenericNotification() expects {tenantCode: ...} (singular, not plural). + // This caused template lookup to fail with undefined tenant, leading to: + // "Cannot read properties of undefined (reading 'replace')" when composing email body. + // SOLUTION: Explicitly map the parameter name: tenantCode: tenantCodes static async notifyMenteesAboutMentorDeletion(mentees, mentorName, orgCodes, tenantCodes) { return await NotificationHelper.sendGenericNotification({ recipients: mentees, @@ -1422,9 +1434,13 @@ module.exports = class AdminService { }) } + // FIX: Removed JOIN with non-existent request_session_mapping table + // ROOT CAUSE: Original query tried to JOIN with 'request_session_mapping' table that doesn't exist: + // "relation 'request_session_mapping' does not exist" + // SOLUTION: Query session_request table directly since requestee_id is already a column in it. + // The RequestSession model (line 15-18) already has requestee_id field, no mapping table needed. static async getPendingSessionRequestsForMentor(mentorUserId, tenantCode) { try { - // Query session_request directly - requestee_id is already in the table const query = ` SELECT * FROM ${RequestSession.tableName} From f0853f677bc7cfc5171f211cb1fc4653fa19a015 Mon Sep 17 00:00:00 2001 From: sumanvpacewisdom Date: Wed, 18 Feb 2026 12:43:53 +0530 Subject: [PATCH 4/4] Refactor mentor and mentee detail removal logic to use a standardized fields clearing approach. Update session queries to ensure tenant code is consistently applied, and remove unnecessary organization cache helper to streamline dependencies. --- src/database/queries/mentorExtension.js | 59 +++++++++++------------- src/database/queries/sessions.js | 6 --- src/database/queries/userExtension.js | 61 ++++++++++++------------- src/helpers/getDefaultOrgId.js | 12 ----- src/helpers/organizationCache.js | 41 ----------------- src/requests/user.js | 13 +++--- src/services/admin.js | 17 ------- 7 files changed, 63 insertions(+), 146 deletions(-) delete mode 100644 src/helpers/organizationCache.js diff --git a/src/database/queries/mentorExtension.js b/src/database/queries/mentorExtension.js index 44550e5f3..cc96e3522 100644 --- a/src/database/queries/mentorExtension.js +++ b/src/database/queries/mentorExtension.js @@ -153,40 +153,35 @@ module.exports = class MentorExtensionQueries { } static async removeMentorDetails(userId, tenantCode) { try { - const modelAttributes = MentorExtension.rawAttributes - - const fieldsToNullify = {} - - for (const [key, attribute] of Object.entries(modelAttributes)) { - // Skip primary key or explicitly excluded fields - if ( - attribute.primaryKey || - key === 'user_id' || - key === 'organization_id' || // required field - key === 'organization_code' || // required NOT NULL field - key === 'tenant_code' || // required NOT NULL field - key === 'created_at' || - key === 'updated_at' || - key === 'is_mentor' // has default value - ) { - continue - } - - // Set types accordingly - if (attribute.type.constructor.name === 'ARRAY') { - fieldsToNullify[key] = [] - } else if (attribute.type.key === 'JSON' || attribute.type.key === 'JSONB') { - fieldsToNullify[key] = {} // Or `{}` if you prefer default object - } else if (key === 'deleted_at') { - fieldsToNullify[key] = new Date() // Timestamp field - } else if (key === 'name') { - fieldsToNullify[key] = common.USER_NOT_FOUND - } else { - fieldsToNullify[key] = null - } + const fieldsToClear = { + designation: [], + area_of_expertise: [], + education_qualification: null, + rating: {}, + meta: {}, + stats: {}, + tags: [], + configs: {}, + visible_to_organizations: [], + external_session_visibility: null, + custom_entity_text: {}, + experience: null, + external_mentee_visibility: null, + mentee_visibility: null, + external_mentor_visibility: null, + mentor_visibility: null, + name: common.USER_NOT_FOUND, + email: null, + phone: null, + settings: {}, + image: null, + gender: null, + status: null, + username: null, + deleted_at: new Date(), } - return await MentorExtension.update(fieldsToNullify, { + return await MentorExtension.update(fieldsToClear, { where: { user_id: userId, tenant_code: tenantCode, diff --git a/src/database/queries/sessions.js b/src/database/queries/sessions.js index 4d45ffed0..fad36a1a3 100644 --- a/src/database/queries/sessions.js +++ b/src/database/queries/sessions.js @@ -1099,12 +1099,6 @@ exports.getSessionsAssignedToMentor = async (mentorUserId, tenantCode) => { } } -// FIX: Added tenantCode parameter and included it in JOIN and WHERE clauses -// ROOT CAUSE: Citus distributed database requires JOINs to include the distribution column (tenant_code). -// Original query failed with: "complex joins are only supported when all distributed tables are -// co-located and joined on their distribution columns" -// SOLUTION: Added tenant_code to both the JOIN condition (line 1108) and WHERE clause (line 1112) -// to ensure Citus can properly route the query across distributed shards. exports.getSessionsAssignedToMentor = async (mentorUserId, tenantCode) => { try { const query = ` diff --git a/src/database/queries/userExtension.js b/src/database/queries/userExtension.js index 0529f61a5..5b6fa24fc 100644 --- a/src/database/queries/userExtension.js +++ b/src/database/queries/userExtension.js @@ -238,38 +238,35 @@ module.exports = class MenteeExtensionQueries { static async removeMenteeDetails(userId, tenantCode) { try { - const modelAttributes = MenteeExtension.rawAttributes - - const fieldsToNullify = {} - - for (const [key, attribute] of Object.entries(modelAttributes)) { - // Skip primary key or explicitly excluded fields - if ( - attribute.primaryKey || - key === 'user_id' || - key === 'organization_id' || // required field - key === 'created_at' || - key === 'updated_at' || - key === 'is_mentor' // has default value - ) { - continue - } - - // Set types accordingly - if (attribute.type.constructor.name === 'ARRAY') { - fieldsToNullify[key] = [] - } else if (attribute.type.key === 'JSON' || attribute.type.key === 'JSONB') { - fieldsToNullify[key] = {} // Or `{}` if you prefer default object - } else if (key === 'deleted_at') { - fieldsToNullify[key] = new Date() // Timestamp field - } else if (key === 'name') { - fieldsToNullify[key] = common.USER_NOT_FOUND - } else { - fieldsToNullify[key] = null - } - } - - return await MenteeExtension.update(fieldsToNullify, { + const fieldsToClear = { + designation: [], + area_of_expertise: [], + education_qualification: null, + rating: {}, + meta: {}, + stats: {}, + tags: [], + configs: {}, + visible_to_organizations: [], + external_session_visibility: null, + custom_entity_text: {}, + experience: null, + external_mentee_visibility: null, + mentee_visibility: null, + external_mentor_visibility: null, + mentor_visibility: null, + name: common.USER_NOT_FOUND, + email: null, + phone: null, + settings: {}, + image: null, + gender: null, + status: null, + username: null, + deleted_at: new Date(), + } + + return await MenteeExtension.update(fieldsToClear, { where: { user_id: userId, tenant_code: tenantCode, diff --git a/src/helpers/getDefaultOrgId.js b/src/helpers/getDefaultOrgId.js index 8fdc9a792..140a5de1c 100644 --- a/src/helpers/getDefaultOrgId.js +++ b/src/helpers/getDefaultOrgId.js @@ -1,7 +1,5 @@ 'use strict' -const userRequests = require('@requests/user') - /** * Retrieves the default organization code. * @returns {Promise} Default organization code or null if not found. @@ -15,16 +13,6 @@ exports.getDefaults = async () => { tenantCode: DEFAULT_TENANT_CODE, } } - - const { success, data } = await userRequests.fetchOrgDetails({ - organizationCode: DEFAULT_ORGANISATION_CODE, - tenantCode: DEFAULT_TENANT_CODE, - }) - - return { - orgCode: success && data?.result?.code, - tenantCode: data?.result?.tenant_code, - } } catch (err) { console.error('Error in getDefaults:', err) return null diff --git a/src/helpers/organizationCache.js b/src/helpers/organizationCache.js deleted file mode 100644 index 23093a9b8..000000000 --- a/src/helpers/organizationCache.js +++ /dev/null @@ -1,41 +0,0 @@ -'use strict' - -/** - * Organization Cache Helper - * - * FIX: Created this wrapper to break circular dependency - * - * ROOT CAUSE: Circular dependency chain caused "getDefaults is not a function" error: - * cacheHelper.js → getDefaultOrgId.js → user.js → cacheHelper.js - * - * When Node.js loads these modules: - * 1. cacheHelper.js starts loading, imports getDefaultOrgId.js - * 2. getDefaultOrgId.js imports user.js (for userRequests.fetchOrgDetails) - * 3. user.js imports cacheHelper.js - * 4. cacheHelper.js is not fully loaded yet, returns partial/empty exports - * 5. getDefaults function is undefined at this point - * - * SOLUTION: user.js now imports this wrapper instead of cacheHelper directly. - * This breaks the cycle because: - * cacheHelper.js → getDefaultOrgId.js → user.js → organizationCache.js → cacheHelper.js - * - * Even though there's still a path to cacheHelper, the actual cacheHelper functions - * are only called at RUNTIME (inside set/get functions), not at MODULE LOAD TIME. - * By the time these functions are called, all modules are fully loaded. - */ - -const cacheHelper = require('@generics/cacheHelper') - -/** - * Set organization details in cache - */ -exports.set = async (tenantCode, orgCode, orgId, data) => { - return await cacheHelper.organizations.set(tenantCode, orgCode, orgId, data) -} - -/** - * Get organization details from cache - */ -exports.get = async (tenantCode, orgCode, orgId) => { - return await cacheHelper.organizations.get(tenantCode, orgCode, orgId) -} diff --git a/src/requests/user.js b/src/requests/user.js index b7e003b80..d307f985a 100644 --- a/src/requests/user.js +++ b/src/requests/user.js @@ -15,10 +15,7 @@ const responses = require('@helpers/responses') const common = require('@constants/common') const { Op } = require('sequelize') const usersHelper = require('@helpers/users') -// FIX: Use organizationCache wrapper instead of cacheHelper directly -// This breaks the circular dependency: cacheHelper → getDefaultOrgId → user.js → cacheHelper -// See helpers/organizationCache.js for detailed explanation -const organizationCache = require('@helpers/organizationCache') +const cacheHelper = require('@generics/cacheHelper') const menteeQueries = require('@database/queries/userExtension') const organisationExtensionQueries = require('@database/queries/organisationExtension') @@ -101,7 +98,7 @@ const getOrgDetails = async function ({ organizationId, tenantCode }) { // If we got the data, populate the cache for future use if (organizationDetails?.organization_code) { try { - await organizationCache.set( + await cacheHelper.organizations.set( tenantCode, organizationDetails.organization_code, organizationId, @@ -887,7 +884,11 @@ const getUserDetailedListUsingCache = async function (userIds, tenantCode, delet const cacheResults = await Promise.all( organizations.map(async (org) => ({ org, - orgCachedData: await organizationCache.get(tenantCode, org.organization_code, org.organization_id), + orgCachedData: await cacheHelper.organizations.get( + tenantCode, + org.organization_code, + org.organization_id + ), })) ) diff --git a/src/services/admin.js b/src/services/admin.js index 7052491ad..722fabcab 100644 --- a/src/services/admin.js +++ b/src/services/admin.js @@ -762,12 +762,6 @@ module.exports = class AdminService { const sentRequestsData = sentRequests.rows || [] // Get requests where user is requestee (received requests) - // FIX: Added missing 'status' parameter to getSessionsMapping call - // ROOT CAUSE: Function signature is getSessionsMapping(userId, status, tenantCode) but was called - // with only (userId, tenantCode). This caused tenantCode to be passed as 'status' parameter, - // and the actual tenantCode parameter received 'undefined'. - // Error: "WHERE parameter 'tenant_code' has invalid 'undefined' value" - // SOLUTION: Pass all three parameters in correct order: userId, status, tenantCode const sessionRequestMapping = await sessionRequestMappingQueries.getSessionsMapping( userId, common.CONNECTIONS_STATUS.REQUESTED, @@ -1417,12 +1411,6 @@ module.exports = class AdminService { } } - // FIX: Changed 'tenantCodes,' to 'tenantCode: tenantCodes,' - // ROOT CAUSE: Using shorthand 'tenantCodes,' creates property {tenantCodes: tenantCodes} - // but sendGenericNotification() expects {tenantCode: ...} (singular, not plural). - // This caused template lookup to fail with undefined tenant, leading to: - // "Cannot read properties of undefined (reading 'replace')" when composing email body. - // SOLUTION: Explicitly map the parameter name: tenantCode: tenantCodes static async notifyMenteesAboutMentorDeletion(mentees, mentorName, orgCodes, tenantCodes) { return await NotificationHelper.sendGenericNotification({ recipients: mentees, @@ -1434,11 +1422,6 @@ module.exports = class AdminService { }) } - // FIX: Removed JOIN with non-existent request_session_mapping table - // ROOT CAUSE: Original query tried to JOIN with 'request_session_mapping' table that doesn't exist: - // "relation 'request_session_mapping' does not exist" - // SOLUTION: Query session_request table directly since requestee_id is already a column in it. - // The RequestSession model (line 15-18) already has requestee_id field, no mapping table needed. static async getPendingSessionRequestsForMentor(mentorUserId, tenantCode) { try { const query = `