From 663d4ff4ea1b1dfeac5c62bbe08da47479da7b95 Mon Sep 17 00:00:00 2001 From: borkarsaish65 Date: Tue, 16 Dec 2025 16:09:36 +0530 Subject: [PATCH 01/10] savepiint --- src/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/src/package.json b/src/package.json index a77226f14..f495802a6 100644 --- a/src/package.json +++ b/src/package.json @@ -12,6 +12,7 @@ "prod": "NODE_ENV=production node app.js", "stage": "NODE_ENV=stage node app.js", "qa": "NODE_ENV=qa node app.js", + "dev": "node --inspect=0.0.0.0:9229 app.js", "prepare": "cd .. && husky install src/.husky", "test:integration": "jest --verbose ./integration-test --config=integrationJest.config.js --runInBand", "db:init": "sequelize-cli db:create || echo 'Database already exists or some issue while creating db, Please check' && sequelize-cli db:migrate ", From 3aeb056de45e0fcb83fcc954ef2efcadf5a4655f Mon Sep 17 00:00:00 2001 From: borkarsaish65 Date: Thu, 18 Dec 2025 01:17:08 +0530 Subject: [PATCH 02/10] filter modification to accomdate AND and OR filtering --- src/generics/utils.js | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/generics/utils.js b/src/generics/utils.js index a6bdd2667..88995f476 100644 --- a/src/generics/utils.js +++ b/src/generics/utils.js @@ -513,6 +513,7 @@ const generateWhereClause = (tableName) => { */ function validateAndBuildFilters(input, validationData) { const entityTypes = {} + let filterType = {} // Ensure validationData is an array if (!Array.isArray(validationData)) { @@ -522,6 +523,8 @@ function validateAndBuildFilters(input, validationData) { // Build the entityTypes dictionary validationData.forEach((entityType) => { entityTypes[entityType.value] = entityType.data_type + const type = entityType?.meta?.filterType?.trim()?.toUpperCase() + filterType[entityType.value] = type === 'OR' ? 'OR' : 'AND' }) const queryParts = [] // Array to store parts of the query @@ -539,14 +542,21 @@ function validateAndBuildFilters(input, validationData) { } // Function to handle array types - function handleArrayType(key, values) { + function handleArrayType(key, values, filterType) { const arrayValues = values .map((value, index) => { replacements[`${key}_${index}`] = value return `:${key}_${index}` }) .join(', ') - queryParts.push(`"${key}" @> ARRAY[${arrayValues}]::character varying[]`) + + const currentFilter = filterType[key]?.trim()?.toUpperCase() === 'OR' ? 'OR' : 'AND' + + if (currentFilter === 'OR') { + queryParts.push(`"${key}" && ARRAY[${arrayValues}]::character varying[]`) + } else { + queryParts.push(`"${key}" @> ARRAY[${arrayValues}]::character varying[]`) + } } // Iterate over each key in the input object @@ -556,9 +566,9 @@ function validateAndBuildFilters(input, validationData) { if (dataType) { if (common.ENTITY_TYPE_DATA_TYPES.STRING_TYPES.includes(dataType)) { - handleStringType(key, input[key]) + handleStringType(key, input[key], filterType) } else if (common.ENTITY_TYPE_DATA_TYPES.ARRAY_TYPES.includes(dataType)) { - handleArrayType(key, input[key]) + handleArrayType(key, input[key], filterType) } } else { // Remove keys that are not in the validationData @@ -724,6 +734,7 @@ function convertEntitiesForFilter(entityTypes) { }) } + const filterType = entityType?.meta?.filterType?.trim()?.toUpperCase() === 'OR' ? 'OR' : 'AND' const newObj = { id: entityType.id, label: entityType.label, @@ -731,6 +742,7 @@ function convertEntitiesForFilter(entityTypes) { parent_id: entityType.parent_id, organization_id: entityType.organization_id, entities: entityType.entities || [], + filterType: filterType, } result[key].push(newObj) From 1d7d788306915749eb5e694e6ae5eb27498c5071 Mon Sep 17 00:00:00 2001 From: borkarsaish65 Date: Thu, 18 Dec 2025 13:50:29 +0530 Subject: [PATCH 03/10] savepoint --- src/package.json | 2 +- src/services/mentees.js | 12 ++++++++++++ src/services/mentors.js | 14 ++++++++++++++ 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/package.json b/src/package.json index f495802a6..776468928 100644 --- a/src/package.json +++ b/src/package.json @@ -12,7 +12,7 @@ "prod": "NODE_ENV=production node app.js", "stage": "NODE_ENV=stage node app.js", "qa": "NODE_ENV=qa node app.js", - "dev": "node --inspect=0.0.0.0:9229 app.js", + "dev": "nodemon --inspect=0.0.0.0:9229 app.js", "prepare": "cd .. && husky install src/.husky", "test:integration": "jest --verbose ./integration-test --config=integrationJest.config.js --runInBand", "db:init": "sequelize-cli db:create || echo 'Database already exists or some issue while creating db, Please check' && sequelize-cli db:migrate ", diff --git a/src/services/mentees.js b/src/services/mentees.js index 2c7d3849a..1240c8bc8 100644 --- a/src/services/mentees.js +++ b/src/services/mentees.js @@ -1536,6 +1536,7 @@ module.exports = class MenteesHelper { responseCode: 'CLIENT_ERROR', }) + /* let validationData = await entityTypeCache.getEntityTypesAndEntitiesWithCache( { status: common.ACTIVE_STATUS, @@ -1545,6 +1546,17 @@ module.exports = class MenteesHelper { organizationCode, userExtensionModelName ) + */ + + let validationData = await entityTypeCache.getEntityTypesAndEntitiesForModel( + userExtensionModelName, + tenantCode, + organizationCode, + { + status: common.ACTIVE_STATUS, + //model_names: { [Op.overlap]: [userExtensionModelName] }, + } + ) let filteredQuery = utils.validateAndBuildFilters( query, diff --git a/src/services/mentors.js b/src/services/mentors.js index 72e468803..18244a45d 100644 --- a/src/services/mentors.js +++ b/src/services/mentors.js @@ -83,6 +83,8 @@ module.exports = class MentorsHelper { statusCode: httpStatusCode.bad_request, responseCode: 'CLIENT_ERROR', }) + + /* let validationData = await entityTypeCache.getEntityTypesAndEntitiesWithCache( { status: common.ACTIVE_STATUS, @@ -93,6 +95,18 @@ module.exports = class MentorsHelper { orgCode, sessionModelName ) + */ + + let validationData = await entityTypeCache.getEntityTypesAndEntitiesForModel( + sessionModelName, + tenantCode, + orgCode, + { + status: common.ACTIVE_STATUS, + allow_filtering: true, + //model_names: { [Op.contains]: [sessionModelName] }, + } + ) if (!orgCode) { return responses.failureResponse({ From 2a74ca37a53ee20e9ae31f6085efe2d6b636a849 Mon Sep 17 00:00:00 2001 From: borkarsaish65 Date: Wed, 24 Dec 2025 00:40:32 +0530 Subject: [PATCH 04/10] modification --- src/generics/utils.js | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/generics/utils.js b/src/generics/utils.js index 00c1f1822..1a4a0041c 100644 --- a/src/generics/utils.js +++ b/src/generics/utils.js @@ -108,6 +108,10 @@ const utcFormat = () => { return momentTimeZone().utc().format('YYYY-MM-DDTHH:mm:ss') } +const getArrayFilterOperator = (filterType, key) => { + return filterType[key]?.trim()?.toUpperCase() === 'OR' ? '&&' : '@>' +} + /** * md5 hash * @function @@ -542,21 +546,14 @@ function validateAndBuildFilters(input, validationData) { } // Function to handle array types - function handleArrayType(key, values, filterType) { + function handleArrayType(key, values, filter) { const arrayValues = values .map((value, index) => { replacements[`${key}_${index}`] = value return `:${key}_${index}` }) .join(', ') - - const currentFilter = filterType[key]?.trim()?.toUpperCase() === 'OR' ? 'OR' : 'AND' - - if (currentFilter === 'OR') { - queryParts.push(`"${key}" && ARRAY[${arrayValues}]::character varying[]`) - } else { - queryParts.push(`"${key}" @> ARRAY[${arrayValues}]::character varying[]`) - } + queryParts.push(`"${key}" ${filter} ARRAY[${arrayValues}]::character varying[]`) } // Iterate over each key in the input object @@ -568,7 +565,8 @@ function validateAndBuildFilters(input, validationData) { if (common.ENTITY_TYPE_DATA_TYPES.STRING_TYPES.includes(dataType)) { handleStringType(key, input[key], filterType) } else if (common.ENTITY_TYPE_DATA_TYPES.ARRAY_TYPES.includes(dataType)) { - handleArrayType(key, input[key], filterType) + let filterToBeApplied = getArrayFilterOperator(filterType, key) + handleArrayType(key, input[key], filterToBeApplied) } } else { // Remove keys that are not in the validationData From 120e89c597b49a7711d06f12a9ce23000165a303 Mon Sep 17 00:00:00 2001 From: borkarsaish65 Date: Wed, 24 Dec 2025 00:55:45 +0530 Subject: [PATCH 05/10] feat:savepoint --- src/generics/utils.js | 2 +- src/services/mentees.js | 12 ------------ src/services/mentors.js | 14 -------------- 3 files changed, 1 insertion(+), 27 deletions(-) diff --git a/src/generics/utils.js b/src/generics/utils.js index 1a4a0041c..3ea243f84 100644 --- a/src/generics/utils.js +++ b/src/generics/utils.js @@ -563,7 +563,7 @@ function validateAndBuildFilters(input, validationData) { if (dataType) { if (common.ENTITY_TYPE_DATA_TYPES.STRING_TYPES.includes(dataType)) { - handleStringType(key, input[key], filterType) + handleStringType(key, input[key]) } else if (common.ENTITY_TYPE_DATA_TYPES.ARRAY_TYPES.includes(dataType)) { let filterToBeApplied = getArrayFilterOperator(filterType, key) handleArrayType(key, input[key], filterToBeApplied) diff --git a/src/services/mentees.js b/src/services/mentees.js index 1240c8bc8..2c7d3849a 100644 --- a/src/services/mentees.js +++ b/src/services/mentees.js @@ -1536,7 +1536,6 @@ module.exports = class MenteesHelper { responseCode: 'CLIENT_ERROR', }) - /* let validationData = await entityTypeCache.getEntityTypesAndEntitiesWithCache( { status: common.ACTIVE_STATUS, @@ -1546,17 +1545,6 @@ module.exports = class MenteesHelper { organizationCode, userExtensionModelName ) - */ - - let validationData = await entityTypeCache.getEntityTypesAndEntitiesForModel( - userExtensionModelName, - tenantCode, - organizationCode, - { - status: common.ACTIVE_STATUS, - //model_names: { [Op.overlap]: [userExtensionModelName] }, - } - ) let filteredQuery = utils.validateAndBuildFilters( query, diff --git a/src/services/mentors.js b/src/services/mentors.js index 18244a45d..72e468803 100644 --- a/src/services/mentors.js +++ b/src/services/mentors.js @@ -83,8 +83,6 @@ module.exports = class MentorsHelper { statusCode: httpStatusCode.bad_request, responseCode: 'CLIENT_ERROR', }) - - /* let validationData = await entityTypeCache.getEntityTypesAndEntitiesWithCache( { status: common.ACTIVE_STATUS, @@ -95,18 +93,6 @@ module.exports = class MentorsHelper { orgCode, sessionModelName ) - */ - - let validationData = await entityTypeCache.getEntityTypesAndEntitiesForModel( - sessionModelName, - tenantCode, - orgCode, - { - status: common.ACTIVE_STATUS, - allow_filtering: true, - //model_names: { [Op.contains]: [sessionModelName] }, - } - ) if (!orgCode) { return responses.failureResponse({ From b103edeb0539b3eb97541cfa22fecb2161eabd2e Mon Sep 17 00:00:00 2001 From: borkarsaish65 Date: Wed, 24 Dec 2025 00:56:25 +0530 Subject: [PATCH 06/10] feat:savepoint-1 --- src/package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/src/package.json b/src/package.json index 776468928..a77226f14 100644 --- a/src/package.json +++ b/src/package.json @@ -12,7 +12,6 @@ "prod": "NODE_ENV=production node app.js", "stage": "NODE_ENV=stage node app.js", "qa": "NODE_ENV=qa node app.js", - "dev": "nodemon --inspect=0.0.0.0:9229 app.js", "prepare": "cd .. && husky install src/.husky", "test:integration": "jest --verbose ./integration-test --config=integrationJest.config.js --runInBand", "db:init": "sequelize-cli db:create || echo 'Database already exists or some issue while creating db, Please check' && sequelize-cli db:migrate ", From fa6a59610eaebc63a46c765384a2ba2c21628abe Mon Sep 17 00:00:00 2001 From: borkarsaish65 Date: Wed, 24 Dec 2025 01:07:19 +0530 Subject: [PATCH 07/10] feat: code rabbit comments --- src/generics/utils.js | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/src/generics/utils.js b/src/generics/utils.js index 3ea243f84..a07b30986 100644 --- a/src/generics/utils.js +++ b/src/generics/utils.js @@ -107,9 +107,30 @@ const getTimeZone = (date, format, tz = null) => { const utcFormat = () => { return momentTimeZone().utc().format('YYYY-MM-DDTHH:mm:ss') } +/** + * Get PostgreSQL array comparison operator based on filter type + * @param {Object} filterType - Map of entity types to their filter types (OR/AND) + * @param {String} key - Entity type key + * @returns {String} PostgreSQL operator: + * - '&&' (overlap) for OR filtering: matches if ANY value matches + * - '@>' (contains) for AND filtering: matches if ALL values match + */ +function getArrayFilterOperator(filterType, key) { + if (!filterType || typeof filterType !== 'object') { + return '@>' + } + const type = filterType[key]?.trim()?.toUpperCase() + return type === 'OR' ? '&&' : '@>' +} -const getArrayFilterOperator = (filterType, key) => { - return filterType[key]?.trim()?.toUpperCase() === 'OR' ? '&&' : '@>' +/** + * Extract and normalize filterType from entity metadata + * @param {Object} entityType - Entity type object with optional meta.filterType + * @returns {String} - Normalized filter type ('OR' or 'AND') + */ +function getFilterType(entityType) { + const type = entityType?.meta?.filterType?.trim()?.toUpperCase() + return type === 'OR' ? 'OR' : 'AND' } /** @@ -527,8 +548,7 @@ function validateAndBuildFilters(input, validationData) { // Build the entityTypes dictionary validationData.forEach((entityType) => { entityTypes[entityType.value] = entityType.data_type - const type = entityType?.meta?.filterType?.trim()?.toUpperCase() - filterType[entityType.value] = type === 'OR' ? 'OR' : 'AND' + filterType[entityType.value] = getFilterType(entityType) }) const queryParts = [] // Array to store parts of the query @@ -732,7 +752,7 @@ function convertEntitiesForFilter(entityTypes) { }) } - const filterType = entityType?.meta?.filterType?.trim()?.toUpperCase() === 'OR' ? 'OR' : 'AND' + const filterTypeValue = getFilterType(entityType) const newObj = { id: entityType.id, label: entityType.label, @@ -740,7 +760,7 @@ function convertEntitiesForFilter(entityTypes) { parent_id: entityType.parent_id, organization_id: entityType.organization_id, entities: entityType.entities || [], - filterType: filterType, + filterType: filterTypeValue, } result[key].push(newObj) From 9c88acb0b06acec33f86997355d9dd23061d26a0 Mon Sep 17 00:00:00 2001 From: borkarsaish65 Date: Wed, 24 Dec 2025 01:28:54 +0530 Subject: [PATCH 08/10] changes --- src/generics/utils.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/generics/utils.js b/src/generics/utils.js index a07b30986..cbb26a028 100644 --- a/src/generics/utils.js +++ b/src/generics/utils.js @@ -555,6 +555,16 @@ function validateAndBuildFilters(input, validationData) { const replacements = {} // Object to store replacements for Sequelize // Function to handle string types + /** + * Handles filtering for string/scalar type fields + * Note: String types always use OR logic because a scalar field can only hold one value at a time. + * The filterType (OR/AND) configuration only applies to array-type field where records can contain + * multiple values. For example, a status field cannot be both 'Active' AND 'Inactive' simultaneously, + * so filtering for multiple statuses must use OR: (status = 'Active' OR status = 'Inactive') + * + * @param {String} key - Field name to filter + * @param {Array} values - Array of possible values to match + */ function handleStringType(key, values) { const orConditions = values .map((value, index) => { @@ -753,6 +763,9 @@ function convertEntitiesForFilter(entityTypes) { } const filterTypeValue = getFilterType(entityType) + /* + filterTypeValue indicates the filtering logic (OR or AND) which is used + */ const newObj = { id: entityType.id, label: entityType.label, From 05f8ea844d9db4a738c32f54fc26e347e9ca820b Mon Sep 17 00:00:00 2001 From: borkarsaish65 Date: Thu, 22 Jan 2026 14:12:51 +0530 Subject: [PATCH 09/10] savepoint --- ...MentorED-Mentoring.postman_collection.json | 4 +-- src/validators/v1/entity-type.js | 26 +++++++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/documentation/latest/postman-collections/mentoring/MentorED-Mentoring.postman_collection.json b/documentation/latest/postman-collections/mentoring/MentorED-Mentoring.postman_collection.json index 207008997..4cf420219 100644 --- a/documentation/latest/postman-collections/mentoring/MentorED-Mentoring.postman_collection.json +++ b/documentation/latest/postman-collections/mentoring/MentorED-Mentoring.postman_collection.json @@ -236,7 +236,7 @@ ], "body": { "mode": "raw", - "raw": "{\n \"value\": \"roless\",\n \"label\": \"RolesV\",\n \"status\": \"ACTIVE\",\n \"type\": \"SYSTEM\",\n \"data_type\": \"ARRAY[STRING]\",\n \"model_names\": [\"UserExtension\",\"Session\"],\n \"required\": true,\n \"allow_filtering\": true\n}", + "raw": "{\n \"value\": \"roless\",\n \"label\": \"RolesV\",\n \"status\": \"ACTIVE\",\n \"type\": \"SYSTEM\",\n \"data_type\": \"ARRAY[STRING]\",\n \"model_names\": [\"UserExtension\",\"Session\"],\n \"required\": true,\n \"allow_filtering\": true,\n \"meta\":{\n \"filterType\":\"OR\"\n }\n}", "options": { "raw": { "language": "json" @@ -331,7 +331,7 @@ ], "body": { "mode": "raw", - "raw": "{\n \"value\": \"roless\",\n \"label\": \"RolesV\",\n \"status\": \"ACTIVE\",\n \"type\": \"SYSTEM\",\n \"data_type\": \"ARRAY[STRING]\",\n \"model_names\": [\"UserExtension\",\"Session\"],\n \"required\": true\n}", + "raw": "{\n \"value\": \"roless\",\n \"label\": \"RolesV\",\n \"status\": \"ACTIVE\",\n \"type\": \"SYSTEM\",\n \"data_type\": \"ARRAY[STRING]\",\n \"model_names\": [\"UserExtension\",\"Session\"],\n \"required\": true,\n \"meta\":{\n \"filterType\":\"AND\"\n }\n}", "options": { "raw": { "language": "json" diff --git a/src/validators/v1/entity-type.js b/src/validators/v1/entity-type.js index ef6b5af5d..9a770edc7 100644 --- a/src/validators/v1/entity-type.js +++ b/src/validators/v1/entity-type.js @@ -76,6 +76,19 @@ module.exports = { .isInt() .withMessage('parent_id is invalid,must be integer') + req.checkBody('meta') + .optional() + .custom((meta) => { + if (meta && meta.filterType) { + const filterType = meta.filterType.toUpperCase() + if (!['OR', 'AND'].includes(filterType)) { + throw new Error('filterType inside meta must be either OR or AND') + } + meta.filterType = filterType + } + return true + }) + if (req.body.has_entities == false) { req.checkBody('allow_filtering').custom((value) => { if (value) { @@ -157,6 +170,19 @@ module.exports = { .isInt() .withMessage('parent_id is invalid,must be integer') + req.checkBody('meta') + .optional() + .custom((meta) => { + if (meta && meta.filterType) { + const filterType = meta.filterType.toUpperCase() + if (!['OR', 'AND'].includes(filterType)) { + throw new Error('filterType inside meta must be either OR or AND') + } + meta.filterType = filterType + } + return true + }) + if (req.body.has_entities == false) { req.checkBody('allow_filtering').custom((value) => { if (value) { From 660fb00e7476e392b6acb43588b7e70f2f2cac81 Mon Sep 17 00:00:00 2001 From: borkarsaish65 Date: Wed, 28 Jan 2026 08:14:20 +0530 Subject: [PATCH 10/10] updated api doc --- src/api-doc/api-doc.yaml | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/api-doc/api-doc.yaml b/src/api-doc/api-doc.yaml index 2116a3314..49852f238 100644 --- a/src/api-doc/api-doc.yaml +++ b/src/api-doc/api-doc.yaml @@ -5896,6 +5896,15 @@ paths: type: string type: type: string + meta: + type: object + properties: + filterType: + type: string + enum: + - OR + - AND + examples: example1: value: @@ -5908,6 +5917,8 @@ paths: required: true status: ACTIVE type: SYSTEM + meta: + filterType: OR example2: value: value: pgender @@ -5962,6 +5973,11 @@ paths: type: 'null' has_entities: type: boolean + meta: + type: object + properties: + filterType: + type: string field_0: type: string meta: @@ -5988,6 +6004,8 @@ paths: updated_at: '2023-09-22T12:40:19.817Z' created_at: '2023-09-22T12:40:19.817Z' has_entities: true + meta: + filterType: OR meta: correlation: 3babe76b-d277-4073-8a59-8dfb94face9b meeting_platform: BBB @@ -6228,6 +6246,14 @@ paths: type: boolean required: type: boolean + meta: + type: object + properties: + filterType: + type: string + enum: + - OR + - AND examples: example1: value: @@ -6240,6 +6266,8 @@ paths: - UserExtension allow_filtering: true required: true + meta: + filterType: OR example2: value: value: pgender @@ -6296,6 +6324,11 @@ paths: type: string has_entities: type: boolean + meta: + type: object + properties: + filterType: + type: string meta: type: object properties: @@ -6322,6 +6355,8 @@ paths: deleted_at: null organization_id: '1' has_entities: true + meta: + filterType: OR meta: correlation: 5f384234-cd5a-467e-a5ac-b43365d2a7a3 meeting_platform: BBB