From 576405ccd7c898a64265179ae0394df9e0d4340f Mon Sep 17 00:00:00 2001 From: prajwal Date: Fri, 5 Dec 2025 16:39:13 +0530 Subject: [PATCH 1/8] multi-user project access feature modification --- .../elevate-project/configs.json | 28 +++ controllers/v1/userProjects.js | 23 +++ generics/constants/api-responses.js | 3 + module/solutions/helper.js | 19 +- module/userProjects/helper.js | 162 ++++++++++-------- 5 files changed, 160 insertions(+), 75 deletions(-) diff --git a/constants/interface-routes/elevate-project/configs.json b/constants/interface-routes/elevate-project/configs.json index 18b611e3..29f9ea08 100644 --- a/constants/interface-routes/elevate-project/configs.json +++ b/constants/interface-routes/elevate-project/configs.json @@ -4115,6 +4115,34 @@ } ], "service": "project" + }, + { + "sourceRoute": "/project/v1/userProjects/updateAcl", + "type": "POST", + "priority": "MUST_HAVE", + "inSequence": false, + "orchestrated": false, + "targetPackages": [ + { + "basePackageName": "project", + "packageName": "elevate-project" + } + ], + "service": "project" + }, + { + "sourceRoute": "/project/v1/userProjects/updateAcl/:id", + "type": "POST", + "priority": "MUST_HAVE", + "inSequence": false, + "orchestrated": false, + "targetPackages": [ + { + "basePackageName": "project", + "packageName": "elevate-project" + } + ], + "service": "project" } ] } diff --git a/controllers/v1/userProjects.js b/controllers/v1/userProjects.js index 011fcef9..b115d2ff 100644 --- a/controllers/v1/userProjects.js +++ b/controllers/v1/userProjects.js @@ -1566,4 +1566,27 @@ module.exports = class UserProjects extends Abstract { } }) } + + /** + * Update ACL for a project if user owns the project and submission level is ENTITY. + * + * @method POST + * @name updateAcl + * @param {Object} req - request Data. + * @returns {Promise} - Response object containing update status, message, and updated ACL data + */ + async updateAcl(req) { + return new Promise(async (resolve, reject) => { + try { + const response = await userProjectsHelper.updateAcl(req.params._id, req.body, req.userDetails) + return resolve(response) + } catch (error) { + return reject({ + status: error.status || HTTP_STATUS_CODE.internal_server_error.status, + message: error.message || HTTP_STATUS_CODE.internal_server_error.message, + errorObject: error, + }) + } + }) + } } diff --git a/generics/constants/api-responses.js b/generics/constants/api-responses.js index 216e3eb3..5672c607 100644 --- a/generics/constants/api-responses.js +++ b/generics/constants/api-responses.js @@ -322,4 +322,7 @@ module.exports = { ACCESS_TOKEN_EXPIRED: 'Access Token Expired!! Please Login Again.', USER_SERVICE_DOWN_CODE: 'USER_SERVICE_DOWN', USER_SERVICE_DOWN: 'User service is down', + PROJECT_DOES_NOT_BELONG_TO_USER: 'Project does not belong to the user!!', + SUBMISSION_LEVEL_NOT_COMPLIED: 'Not allowed to update project. SUBMISSION_LEVEL not complied!!!', + PROJECT_UPDATED_SUCCESSFULLY: 'Project updated successfully.', } diff --git a/module/solutions/helper.js b/module/solutions/helper.js index a215a7be..0af3f212 100644 --- a/module/solutions/helper.js +++ b/module/solutions/helper.js @@ -27,6 +27,7 @@ const solutionsUtils = require(GENERICS_FILES_PATH + '/helpers/solutionAndProjec const surveyService = require(GENERICS_FILES_PATH + '/services/survey') const moment = require('moment-timezone') +const { forEach } = require('lodash') /** * SolutionsHelper * @class @@ -730,6 +731,13 @@ module.exports = class SolutionsHelper { CONSTANTS.common.MANDATORY_SCOPE_FIELD, CONSTANTS.common.OPTIONAL_SCOPE_FIELD ) + if (prefix != '') { + builtQuery['$and'] = builtQuery['$and'].map((obj) => { + const key = Object.keys(obj)[0] + const value = obj[key] + return { [`${prefix}.${key}`]: value } + }) + } filterQuery = { ...filterQuery, ...builtQuery, @@ -2527,7 +2535,7 @@ module.exports = class SolutionsHelper { ) }) - if (process.env.SUBMISSION_LEVEL == 'ENTITY' && requestedData.hasOwnProperty('entityId')) { + if (process.env.SUBMISSION_LEVEL == 'ENTITY') { mergedData = userCreatedProjects.data.data totalCount = mergedData.length if (mergedData.length > 0) { @@ -3157,13 +3165,9 @@ module.exports = class SolutionsHelper { try { let query = { isDeleted: false } - if (process.env.SUBMISSION_LEVEL === 'ENTITY' && requestedData.hasOwnProperty('entityId')) { + if (process.env.SUBMISSION_LEVEL === 'ENTITY') { // Use queryBasedOnRoleAndLocation function to form query for acl.visibility = SCOPE projects - let queryData = await this.queryBasedOnRoleAndLocation( - _.omit(requestedData, ['entityId']), - '', - 'acl' - ) + let queryData = await this.queryBasedOnRoleAndLocation(requestedData, '', 'acl') // status of the project could be anything, hence deleting status property from the querydata delete queryData.data.status // isReusable field doesn't exist for projects model hence removing the key @@ -3173,7 +3177,6 @@ module.exports = class SolutionsHelper { // Construct query for projects accessible by the user query = { - entityId: requestedData.entityId, 'solutionInformation.submissionLevel': process.env.SUBMISSION_LEVEL, $or: [ { 'acl.visibility': CONSTANTS.common.PROJECT_VISIBILITY_ALL }, diff --git a/module/userProjects/helper.js b/module/userProjects/helper.js index d163177e..72e8b70b 100644 --- a/module/userProjects/helper.js +++ b/module/userProjects/helper.js @@ -34,6 +34,7 @@ const path = require('path') const gotenbergService = require(SERVICES_BASE_PATH + '/gotenberg') const projectService = require(SERVICES_BASE_PATH + '/projects') const defaultUserProfileConfig = require('@config/defaultUserProfileDeleteConfig') +const { result } = require('lodash') const configFilePath = process.env.AUTH_CONFIG_FILE_PATH const surveyService = require(SERVICES_BASE_PATH + '/survey') @@ -154,7 +155,7 @@ module.exports = class UserProjectsHelper { } // if entityId & entityInformation are passed through payload, ignore them - const blackListedPayloadItems = ['entityId', 'entityInformation'] + const blackListedPayloadItems = ['entityId', 'entityInformation', 'acl'] blackListedPayloadItems.map((payloadItem) => { if (data.hasOwnProperty(payloadItem)) delete data[payloadItem] }) @@ -274,7 +275,7 @@ module.exports = class UserProjectsHelper { const projectsModel = Object.keys(schemas['projects'].schema) - let keysToRemoveFromUpdation = ['userRoleInformation', 'userProfile', 'certificate'] + let keysToRemoveFromUpdation = ['userRoleInformation', 'userProfile', 'certificate', 'acl'] keysToRemoveFromUpdation.forEach((key) => { if (data[key]) delete data[key] }) @@ -302,70 +303,6 @@ module.exports = class UserProjectsHelper { if (projectData && projectData.success == true) { updateProject = _.merge(updateProject, projectData.data) } - // let createNewProgramAndSolution = false; - // let solutionExists = false; - - // if (data.programId && data.programId !== "") { - - // // Check if program already existed in project and if its not an existing program. - // if (!userProject[0].programInformation) { - // createNewProgramAndSolution = true; - // } else if ( - // userProject[0].programInformation && - // userProject[0].programInformation._id && - // userProject[0].programInformation._id.toString() !== data.programId - // ) { - // // Not an existing program. - - // solutionExists = true; - // } - - // } else if (data.programName) { - - // if (!userProject[0].solutionInformation) { - // createNewProgramAndSolution = true; - // } else { - // solutionExists = true; - // // create new program using current name and add existing solution and remove program from it. - // } - // } - - // if (createNewProgramAndSolution || solutionExists) { - - // let programAndSolutionInformation = - // await this.createProgramAndSolution( - // data.programId, - // data.programName, - // updateProject.entityId ? [updateProject.entityId] : "", - // userToken, - // userProject[0].solutionInformation && userProject[0].solutionInformation._id ? - // userProject[0].solutionInformation._id : "" - // ); - - // if (!programAndSolutionInformation.success) { - // return resolve(programAndSolutionInformation); - // } - - // if (solutionExists) { - - // let updateProgram = - // await programsHelper.removeSolutions( - // userToken, - // userProject[0].programInformation._id, - // [userProject[0].solutionInformation._id] - // ); - - // if (!updateProgram.success) { - // throw { - // status: HTTP_STATUS_CODE.bad_request.status, - // message: CONSTANTS.apiResponses.PROGRAM_NOT_UPDATED - // } - // } - // } - - // updateProject = - // _.merge(updateProject, programAndSolutionInformation.data); - // } let booleanData = this.booleanData(schemas['projects'].schema) let mongooseIdData = this.mongooseIdData(schemas['projects'].schema) @@ -1698,7 +1635,10 @@ module.exports = class UserProjectsHelper { if (bodyData.hasOwnProperty('acl')) { bodyData.acl.visibility = bodyData.acl.visibility.toUpperCase() bodyData.acl.users.push(userId) - if (!bodyData.acl.hasOwnProperty('scope') || !(bodyData.acl.scope.length > 0)) { + if ( + !bodyData.acl.hasOwnProperty('scope') || + !(Object.keys(bodyData.acl.scope.length) > 0) + ) { bodyData.acl['scope'] = solutionDetails.scope } projectCreation.data['acl'] = bodyData.acl @@ -4282,6 +4222,7 @@ module.exports = class UserProjectsHelper { validateAllTasks(allTasksFalttened) } + delete updateData.acl let updateResult = await this.sync( projectId, '', @@ -4550,6 +4491,93 @@ module.exports = class UserProjectsHelper { } }) } + + /** + * Update ACL for a project if user owns the project and submission level is ENTITY. + * + * @param {String} projectId - The ID of the project whose ACL should be updated + * @param {Object} bodyData - The request body containing ACL updates + * @param {Object} userDetails - Logged-in user's details object + * @returns {Promise} - Response object containing update status, message, and updated ACL data + * + * @throws {Object} - Throws an error if submission level is invalid or project update fails + */ + static updateAcl(projectId, bodyData, userDetails) { + return new Promise(async (resolve, reject) => { + try { + // Only allow updates if submission level is ENTITY + if (process.env.SUBMISSION_LEVEL !== 'ENTITY') { + throw { + success: false, + message: CONSTANTS.apiResponses.SUBMISSION_LEVEL_NOT_COMPLIED, + } + } + + // Extract user and tenant IDs + const userId = userDetails.userInformation.userId + const tenantId = userDetails.userInformation.tenantId + + // Check if project exists and belongs to the user + const projectData = await projectQueries.projectDocument({ + _id: projectId, + userId, + tenantId, + }) + + if (!projectData || projectData.length === 0) { + throw { + success: false, + message: CONSTANTS.apiResponses.PROJECT_DOES_NOT_BELONG_TO_USER, + } + } + + // Update ACL field in the project + const updatedProject = await projectQueries.findOneAndUpdate( + { + _id: projectId, + userId, + tenantId, + }, + { + $set: { acl: bodyData.acl }, + }, + { + new: true, + } + ) + + // Check if update was successful + if (!updatedProject) { + throw { + success: false, + message: CONSTANTS.apiResponses.PROJECT_UPDATE_FAILED, + } + } + + // Success response + return resolve({ + success: true, + status: 200, + message: CONSTANTS.apiResponses.PROJECT_UPDATED_SUCCESSFULLY, + result: { + _id: updatedProject._id, + acl: updatedProject.acl, + }, + data: { + _id: updatedProject._id, + acl: updatedProject.acl, + }, + }) + } catch (error) { + // Unified error response + return reject({ + message: error.message, + success: false, + status: error.status ? error.status : HTTP_STATUS_CODE.internal_server_error.status, + }) + } + }) + } } /** From 3756346b985e69fabfbd4f3a2c7e452dc7a3c5a2 Mon Sep 17 00:00:00 2001 From: prajwal Date: Fri, 5 Dec 2025 16:50:23 +0530 Subject: [PATCH 2/8] unwanted code removed --- module/solutions/helper.js | 1 - module/userProjects/helper.js | 1 - 2 files changed, 2 deletions(-) diff --git a/module/solutions/helper.js b/module/solutions/helper.js index 0af3f212..476e19f2 100644 --- a/module/solutions/helper.js +++ b/module/solutions/helper.js @@ -27,7 +27,6 @@ const solutionsUtils = require(GENERICS_FILES_PATH + '/helpers/solutionAndProjec const surveyService = require(GENERICS_FILES_PATH + '/services/survey') const moment = require('moment-timezone') -const { forEach } = require('lodash') /** * SolutionsHelper * @class diff --git a/module/userProjects/helper.js b/module/userProjects/helper.js index 72e8b70b..cd0e302a 100644 --- a/module/userProjects/helper.js +++ b/module/userProjects/helper.js @@ -34,7 +34,6 @@ const path = require('path') const gotenbergService = require(SERVICES_BASE_PATH + '/gotenberg') const projectService = require(SERVICES_BASE_PATH + '/projects') const defaultUserProfileConfig = require('@config/defaultUserProfileDeleteConfig') -const { result } = require('lodash') const configFilePath = process.env.AUTH_CONFIG_FILE_PATH const surveyService = require(SERVICES_BASE_PATH + '/survey') From 7125b960887ce4ef25c2c0f14424a92e5906da59 Mon Sep 17 00:00:00 2001 From: prajwal Date: Mon, 8 Dec 2025 17:23:46 +0530 Subject: [PATCH 3/8] comments addressed --- .../Elevate-Project.postman_collection.json | 92 ++++-- api-doc/api-doc.yaml | 271 ++++++++++++------ .../elevate-project/configs.json | 28 -- controllers/v1/userProjects.js | 28 ++ generics/constants/api-responses.js | 5 +- module/programs/validator/v1.js | 4 +- module/solutions/helper.js | 56 ++-- module/userProjects/helper.js | 18 +- module/userProjects/validator/v1.js | 30 ++ 9 files changed, 358 insertions(+), 174 deletions(-) diff --git a/api-doc/Elevate-Project.postman_collection.json b/api-doc/Elevate-Project.postman_collection.json index f7d38599..b2e02438 100644 --- a/api-doc/Elevate-Project.postman_collection.json +++ b/api-doc/Elevate-Project.postman_collection.json @@ -3,7 +3,7 @@ "_postman_id": "137475d2-44ec-4746-84ec-1f12779168ec", "name": "Elevate-Project", "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", - "_exporter_id": "33513401" + "_exporter_id": "28589818" }, "item": [ { @@ -345,7 +345,7 @@ } }, "url": { - "raw": "{{baseUrl}}/project/v1/project/templates/createChildProjectTemplate?isExternalProgram=true&programExternalId=AWS_Prog_MYS_may192", + "raw": "{{baseUrl}}/v1/project/templates/createChildProjectTemplate?isExternalProgram=true&programExternalId=AWS_Prog_MYS_may192", "host": ["{{baseUrl}}"], "path": ["v1", "project", "templates", "createChildProjectTemplate"], "query": [ @@ -648,7 +648,7 @@ ] }, "url": { - "raw": "{{baseUrl}}/project/v1/project/templateTasks/bulkUpdate/:projectTemplateId", + "raw": "{{baseUrl}}/v1/project/templateTasks/bulkUpdate/:projectTemplateId", "host": ["{{baseUrl}}"], "path": ["v1", "project", "templateTasks", "bulkUpdate", ":projectTemplateId"], "variable": [ @@ -832,7 +832,7 @@ "raw": "{\n \"_id\" : \"6624ff5124fc48e1fcdbec29\",\n \"userId\" : \"2\",\n \"userRole\" : \"BEO\",\n \"status\" : \"started\",\n \"isDeleted\" : false,\n \"categories\" : [\n {\n \"externalId\" : \"\",\n \"_id\" : \"\"\n }\n ],\n \"createdBy\" : \"2\",\n \"tasks\" : [\n {\n \"externalId\" : \"Task1-1710148664591\",\n \"name\" : \"Address the assembly and share ideas about Smart Learn\",\n \"_id\" : \"1234\",\n \"type\" : \"observation\",\n \"status\" : \"notStarted\",\n \"isDeleted\" : false,\n \"isDeletable\" : true,\n \"createdAt\" : \"2024-04-21T11:58:09.204+0000\",\n \"updatedAt\" : \"2024-04-21T11:58:09.204+0000\",\n \"isImportedFromLibrary\" : false,\n \"syncedAt\" : \"2024-04-21T11:58:09.204+0000\",\n \"children\" : [\n\n ]\n }\n ],\n \"updatedBy\" : \"2\",\n \"learningResources\" : [\n\n ],\n \"hasAcceptedTAndC\" : false,\n \"taskSequence\" : [\n\n ],\n \"recommendedFor\" : [\n\n ],\n \"attachments\" : [\n\n ],\n \"deleted\" : false,\n \"userProfile\" : {\n \"id\" : 2,\n \"email\" : \"prajwal@tunerlabs.com\",\n \"email_verified\" : \"false\",\n \"name\" : \"prajwal\",\n \"gender\" : null,\n \"location\" : null,\n \"about\" : null,\n \"share_link\" : null,\n \"status\" : \"ACTIVE\",\n \"image\" : null,\n \"last_logged_in_at\" : \"2024-04-21T07:44:54.239Z\",\n \"has_accepted_terms_and_conditions\" : true,\n \"languages\" : null,\n \"preferred_language\" : \"en\",\n \"organization_id\" : 1,\n \"roles\" : [\n 3\n ],\n \"meta\" : null,\n \"created_at\" : \"2024-03-22T07:11:10.925Z\",\n \"updated_at\" : \"2024-04-21T07:44:54.240Z\",\n \"deleted_at\" : null,\n \"organization\" : {\n \"id\" : 1,\n \"name\" : \"Default Organization\",\n \"code\" : \"default_code\"\n },\n \"user_roles\" : [\n {\n \"id\" : 3,\n \"title\" : \"mentee\",\n \"user_type\" : 0,\n \"status\" : \"ACTIVE\"\n }\n ]\n },\n \"syncedAt\" : \"2024-04-21T11:58:09.183+0000\",\n \"entityInformation\" : {\n \"_id\" : \"5f33c3d85f637784791cd830\",\n \"childHierarchyPath\" : [\n \"district\",\n \"beat\",\n \"cluster\",\n \"school\"\n ],\n \"deleted\" : false,\n \"entityTypeId\" : \"5f32d8228e0dc8312404056e\",\n \"entityType\" : \"state\",\n \"metaInformation\" : {\n \"externalId\" : \"MH\",\n \"name\" : \"Maharashtra\",\n \"region\" : \"West\",\n \"capital\" : \"Mumbai\"\n },\n \"updatedBy\" : \"124fdade-aaa2-4587-9dcd-3c7cf15c7147\",\n \"createdBy\" : \"2b655fd1-201d-4d2a-a1b7-9048a25c0afa\",\n \"updatedAt\" : \"2021-01-18T06:51:31.086Z\",\n \"createdAt\" : \"2020-08-12T10:26:32.038Z\",\n \"__v\" : 0,\n \"groups\" : {\n \"district\" : [\n \"5f33c56fb451f58478b36996\"\n ],\n \"beat\" : [\n \"5f33cb24c1352f84a29f547c\",\n \"5f33cb24c1352f84a29f547d\"\n ],\n \"cluster\" : [\n \"5f33cb07ce438a849b4a17f6\",\n \"5f33cb07ce438a849b4a17f7\"\n ],\n \"school\" : [\n \"5f33caebb451f58478b36998\",\n \"5f33caebb451f58478b36999\"\n ]\n },\n \"registryDetails\" : {\n \"locationId\" : \"db331a8c-b9e2-45f8-b3c0-7ec1e826b6df\",\n \"code\" : \"db331a8c-b9e2-45f8-b3c0-7ec1e826b6df\"\n }\n },\n \"entityId\" : \"5f33c3d85f637784791cd830\",\n \"programInformation\" : {\n \"_id\" : \"5f5f6f66f69c8a0fd28a8aba\",\n \"name\" : \"My Program\",\n \"description\" : \"Unnati-Default-Program\",\n \"isAPrivateProgram\" : true\n },\n \"taskReport\" : {\n \"total\" : 1,\n \"notStarted\" : 1\n },\n \"description\" : \"Come See Our School!- Parent Mela vishwa\",\n \"title\" : \"Come See Our School!- Parent Mela\",\n \"metaInformation\" : {\n \"rationale\" : \"\",\n \"primaryAudience\" : [\n \"Community\"\n ],\n \"goal\" : \"Organizing the Parent Mela in the school in order to make better community reach\",\n \"duration\" : \"At the end of every quarter\",\n \"successIndicators\" : \"\",\n \"risks\" : \"\",\n \"approaches\" : \"\"\n },\n \"programId\" : \"5f5f6f66f69c8a0fd28a8aba\",\n \"lastDownloadedAt\" : \"2024-04-21T11:58:09.204+0000\",\n \"updatedAt\" : \"2024-04-21T11:58:09.213+0000\",\n \"createdAt\" : \"2024-04-21T11:58:09.213+0000\",\n \"__v\" : 0\n}\n" }, "url": { - "raw": "{{baseUrl}}/project/v1/userProjects/sync/6624ff5124fc48e1fcdbec29?lastDownloadedAt=2024-04-21T11:58:09.204Z", + "raw": "{{baseUrl}}/v1/userProjects/sync/6624ff5124fc48e1fcdbec29?lastDownloadedAt=2024-04-21T11:58:09.204Z", "host": ["{{baseUrl}}"], "path": ["v1", "userProjects", "sync", "6624ff5124fc48e1fcdbec29"], "query": [ @@ -875,7 +875,7 @@ "raw": " {\n \"userRole\": \"DEO\",\n \"externalId\" : \"TSCSLHAR02-1710148664591\",\n \"createdFor\": [\n \"0126796199493140480\"\n ],\n \"status\": \"completed\",\n \"isDeleted\": false,\n \"description\": \"Come See Our School!- Parent Mela vishwa\",\n \"title\": \"Come See Our School!- Parent Mela\",\n \"metaInformation\": {\n \"rationale\": \"\",\n \"primaryAudience\": [\n \"Community\"\n ],\n \"goal\": \"Organizing the Parent Mela in the school in order to make better community reach\",\n \"duration\": \"At the end of every quarter\",\n \"successIndicators\": \"\",\n \"risks\": \"\",\n \"approaches\": \"\"\n },\n \"isAPrivateProgram\": false,\n \"hasAcceptedTAndC\": false,\n \"entityId\":\"a4268dc5-61c3-4eab-9a89-ffaaee809147\"\n }" }, "url": { - "raw": "{{baseUrl}}/project/v1/userProjects/details?solutionId=66727b63cf0222d7ec1fa7e0&templateId=IMP-3147f", + "raw": "{{baseUrl}}/v1/userProjects/details?solutionId=66727b63cf0222d7ec1fa7e0&templateId=IMP-3147f", "host": ["{{baseUrl}}"], "path": ["v1", "userProjects", "details"], "query": [ @@ -1105,7 +1105,7 @@ } ], "url": { - "raw": "{{baseUrl}}/project/v1/userProjects/searchEntities?solutionId=6880adeee1b9e76a809cd9db&page=10&limit=5", + "raw": "{{baseUrl}}/v1/userProjects/searchEntities?solutionId=6880adeee1b9e76a809cd9db&page=10&limit=5", "host": ["{{baseUrl}}"], "path": ["v1", "userProjects", "searchEntities"], "query": [ @@ -1147,7 +1147,7 @@ } }, "url": { - "raw": "{{baseUrl}}/project/v1/userProjects/addEntity/68d38c9f69f139b91c9e57b8", + "raw": "{{baseUrl}}/v1/userProjects/addEntity/68d38c9f69f139b91c9e57b8", "host": ["{{baseUrl}}"], "path": ["v1", "userProjects", "addEntity", "68d38c9f69f139b91c9e57b8"] } @@ -1180,12 +1180,46 @@ } }, "url": { - "raw": "{{baseUrl}}/project/v1/userProjects/deleteUserPIIData", + "raw": "{{baseUrl}}/v1/userProjects/deleteUserPIIData", "host": ["{{baseUrl}}"], "path": ["v1", "userProjects", "deleteUserPIIData"] } }, "response": [] + }, + { + "name": "updateAcl", + "request": { + "method": "POST", + "header": [ + { + "key": "x-auth-token", + "value": "{{userToken}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"acl\" : {\n \"visibility\" : \"SPECIFIC\",\n \"users\" : [\"1\",\"2\",\"3\"],\n \"scope\" : {}\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}project/v1/userProjects/updateAcl/:_id", + "host": ["{{baseUrl}}project"], + "path": ["v1", "userProjects", "updateAcl", ":_id"], + "variable": [ + { + "key": "_id", + "value": "68efa7a15a74ed062521a5ca" + } + ] + } + }, + "response": [] } ] }, @@ -1209,7 +1243,7 @@ } ], "url": { - "raw": "{{baseUrl}}/project/v1/reports/entity?requestPdf=true&reportType=0", + "raw": "{{baseUrl}}/v1/reports/entity?requestPdf=true&reportType=0", "host": ["{{baseUrl}}"], "path": ["v1", "reports", "entity"], "query": [ @@ -1243,7 +1277,7 @@ } ], "url": { - "raw": "{{baseUrl}}/project/v1/reports/detailView?requestPdf=true&reportType=2&programId=5f3bc13016fdc4ed008171ae", + "raw": "{{baseUrl}}/v1/reports/detailView?requestPdf=true&reportType=2&programId=5f3bc13016fdc4ed008171ae", "host": ["{{baseUrl}}"], "path": ["v1", "reports", "detailView"], "query": [ @@ -2610,7 +2644,7 @@ ] }, "url": { - "raw": "{{baseUrl}}/project/v1/certificateBaseTemplates/createOrUpdate", + "raw": "{{baseUrl}}/v1/certificateBaseTemplates/createOrUpdate", "host": ["{{baseUrl}}"], "path": ["v1", "certificateBaseTemplates", "createOrUpdate"] } @@ -2667,7 +2701,7 @@ ] }, "url": { - "raw": "{{baseUrl}}/project/v1/certificateBaseTemplates/createOrUpdate/6646e0a7b5226e19a19edf37", + "raw": "{{baseUrl}}/v1/certificateBaseTemplates/createOrUpdate/6646e0a7b5226e19a19edf37", "host": ["{{baseUrl}}"], "path": ["v1", "certificateBaseTemplates", "createOrUpdate", "6646e0a7b5226e19a19edf37"] } @@ -2718,7 +2752,7 @@ } }, "url": { - "raw": "{{baseUrl}}/project/v1/certificateTemplates/createOrUpdate", + "raw": "{{baseUrl}}/v1/certificateTemplates/createOrUpdate", "host": ["{{baseUrl}}"], "path": ["v1", "certificateTemplates", "createOrUpdate"] } @@ -2764,7 +2798,7 @@ } }, "url": { - "raw": "{{baseUrl}}/project/v1/certificateTemplates/createOrUpdate", + "raw": "{{baseUrl}}/v1/certificateTemplates/createOrUpdate", "host": ["{{baseUrl}}"], "path": ["v1", "certificateTemplates", "createOrUpdate"] } @@ -2811,7 +2845,7 @@ ] }, "url": { - "raw": "{{baseUrl}}/project/v1/certificateTemplates/uploadTemplate/:_id?updateTemplate=true", + "raw": "{{baseUrl}}/v1/certificateTemplates/uploadTemplate/:_id?updateTemplate=true", "host": ["{{baseUrl}}"], "path": ["v1", "certificateTemplates", "uploadTemplate", ":_id"], "query": [ @@ -2913,7 +2947,7 @@ ] }, "url": { - "raw": "{{baseUrl}}/project/v1/certificateTemplates/createSvg?baseTemplateId=66bb5c3e196c83581cc26aa8", + "raw": "{{baseUrl}}/v1/certificateTemplates/createSvg?baseTemplateId=66bb5c3e196c83581cc26aa8", "host": ["{{baseUrl}}"], "path": ["v1", "certificateTemplates", "createSvg"], "query": [ @@ -2943,7 +2977,7 @@ } ], "url": { - "raw": "{{baseUrl}}/project/v1/configurations/read", + "raw": "{{baseUrl}}/v1/configurations/read", "host": ["{{baseUrl}}"], "path": ["v1", "configurations", "read"] } @@ -2999,7 +3033,7 @@ } }, "url": { - "raw": "{{baseUrl}}/project/v1/users/programs?isAPrivateProgram=true&page=1&limit=100&search=&language=&getProjectsCount=true", + "raw": "{{baseUrl}}/v1/users/programs?isAPrivateProgram=true&page=1&limit=100&search=&language=&getProjectsCount=true", "host": ["{{baseUrl}}"], "path": ["v1", "users", "programs"], "query": [ @@ -3053,7 +3087,7 @@ } }, "url": { - "raw": "{{baseUrl}}/project/v1/users/solutions/66bb85bebf682a1f367c55b9", + "raw": "{{baseUrl}}/v1/users/solutions/66bb85bebf682a1f367c55b9", "host": ["{{baseUrl}}"], "path": ["v1", "users", "solutions", "66bb85bebf682a1f367c55b9"] } @@ -3077,7 +3111,7 @@ } ], "url": { - "raw": "{{baseUrl}}/project/v1/wishlist/add/66399e6304785762f7e8e4cb", + "raw": "{{baseUrl}}/v1/wishlist/add/66399e6304785762f7e8e4cb", "host": ["{{baseUrl}}"], "path": ["v1", "wishlist", "add", "66399e6304785762f7e8e4cb"] } @@ -3096,7 +3130,7 @@ } ], "url": { - "raw": "{{baseUrl}}/project/v1/wishlist/list?page=1&language=ka&limit=10", + "raw": "{{baseUrl}}/v1/wishlist/list?page=1&language=ka&limit=10", "host": ["{{baseUrl}}"], "path": ["v1", "wishlist", "list"], "query": [ @@ -3129,7 +3163,7 @@ } ], "url": { - "raw": "{{baseUrl}}/project/v1/wishlist/remove/66399e6304785762f7e8e4cb", + "raw": "{{baseUrl}}/v1/wishlist/remove/66399e6304785762f7e8e4cb", "host": ["{{baseUrl}}"], "path": ["v1", "wishlist", "remove", "66399e6304785762f7e8e4cb"] } @@ -3173,7 +3207,7 @@ } ], "url": { - "raw": "{{baseUrl}}/project/v1/projectAttributes/create", + "raw": "{{baseUrl}}/v1/projectAttributes/create", "host": ["{{baseUrl}}"], "path": ["v1", "projectAttributes", "create"] } @@ -3221,7 +3255,7 @@ } }, "url": { - "raw": "{{baseUrl}}/project/v1/projectAttributes/update?code=duration&language=hi", + "raw": "{{baseUrl}}/v1/projectAttributes/update?code=duration&language=hi", "host": ["{{baseUrl}}"], "path": ["v1", "projectAttributes", "update"], "query": [ @@ -3255,7 +3289,7 @@ } ], "url": { - "raw": "{{baseUrl}}/project/v1/projectAttributes/find?language=en", + "raw": "{{baseUrl}}/v1/projectAttributes/find?language=en", "host": ["{{baseUrl}}"], "path": ["v1", "projectAttributes", "find"], "query": [ @@ -3290,7 +3324,7 @@ } ], "url": { - "raw": "{{baseUrl}}/project/v1/template/list?language=ka&hasSpotlight=false&duration=1week&roles=DistrictsEducationOfficer", + "raw": "{{baseUrl}}/v1/template/list?language=ka&hasSpotlight=false&duration=1week&roles=DistrictsEducationOfficer", "host": ["{{baseUrl}}"], "path": ["v1", "template", "list"], "query": [ @@ -3365,7 +3399,7 @@ ] }, "url": { - "raw": "{{baseUrl}}/project/v1/userExtension/bulkUpload", + "raw": "{{baseUrl}}/v1/userExtension/bulkUpload", "host": ["{{baseUrl}}"], "path": ["v1", "userExtension", "bulkUpload"] } @@ -3393,7 +3427,7 @@ } }, "url": { - "raw": "{{baseUrl}}/project/v1/userExtension/update?tenantId&orgId&userId", + "raw": "{{baseUrl}}/v1/userExtension/update?tenantId&orgId&userId", "host": ["{{baseUrl}}"], "path": ["v1", "userExtension", "update"], "query": [ @@ -3425,7 +3459,7 @@ "method": "GET", "header": [], "url": { - "raw": "{{baseUrl}}/project/health", + "raw": "{{baseUrl}}/health", "host": ["{{baseUrl}}"], "path": ["health"] } diff --git a/api-doc/api-doc.yaml b/api-doc/api-doc.yaml index 079a3398..653c5237 100644 --- a/api-doc/api-doc.yaml +++ b/api-doc/api-doc.yaml @@ -420,8 +420,8 @@ paths: message: Missing file of type projectTemplates status: 400 description: >- - Enables bulk creation of project templates by uploading a CSV file - with multiple templates and a JSON file for multilingual data. + Enables bulk creation of project templates by uploading a CSV file with + multiple templates and a JSON file for multilingual data. * The API Endpoint for the bulk creation of project templates is `/v1/project/templates/bulkCreate` @@ -498,8 +498,8 @@ paths: message: Missing file of type projectTemplates status: 400 description: >- - Enables bulk updating of project templates through a CSV - file containing multiple records. + Enables bulk updating of project templates through a CSV file + containing multiple records. * The API Endpoint for the bulk updating of project templates is `/v1/project/templates/bulkUpdate` @@ -601,8 +601,8 @@ paths: status: 400 message: Project template not found description: >- - Updates a specific project template using its template - ID provided in the path parameters. + Updates a specific project template using its template ID provided in + the path parameters. * The API Endpoint for updating a specific project template is `/v1/project/templates/update` @@ -683,7 +683,8 @@ paths: status: 400 message: Project template not found description: >- - Fetches a list of project templates filtered by the provided external ID. + Fetches a list of project templates filtered by the provided external + ID. * The API Endpoint to retrieves a list of project templates is `/v1/project/templates/listByIds` @@ -769,8 +770,8 @@ paths: param: _id msg: required project template id description: >- - Creates a duplicate project template using the parent template’s external - ID and associated solution ID. + Creates a duplicate project template using the parent template’s + external ID and associated solution ID. * The API Endpoint will help you to create a duplicate projectTemplate is `/v1/project/templates/importProjectTemplate` @@ -781,7 +782,7 @@ paths: * This is a mandatory parameter and cannot be empty or null. /v1/project/templates/details/{templateId}: get: - tags: &ref_73 + tags: &ref_71 - Project Templates summary: Fetches details of a specific project template parameters: @@ -1168,7 +1169,7 @@ paths: projectId: '' '400': description: 'Bad response ' - headers: &ref_74 {} + headers: &ref_72 {} content: application/json: schema: @@ -1651,8 +1652,8 @@ paths: param: _id msg: required project template id description: >- - Enables bulk creation of tasks for a specific project template by uploading - a CSV file and providing the template ID. + Enables bulk creation of tasks for a specific project template by + uploading a CSV file and providing the template ID. * The API allows the user to bulk create tasks for a specified project template is `/v1/project/templateTasks/bulkCreate` @@ -1732,8 +1733,8 @@ paths: message: Project template not found status: 400 description: >- - Allows bulk updating of tasks within a project template using a CSV file - and the template ID. + Allows bulk updating of tasks within a project template using a CSV + file and the template ID. * The API allows the user to bulk update tasks for a specified project template is `/v1/project/templateTasks/bulkUpdate` @@ -3142,7 +3143,8 @@ paths: param: title msg: required project title description: >- - Creates a new user project with associated metadata, tasks, and categories. + Creates a new user project with associated metadata, tasks, and + categories. * The API allows for the creation of a new user project is `/v1/userProjects/add`. @@ -3233,7 +3235,7 @@ paths: * This is a mandatory parameter and cannot be empty or null. /v1/userProjects/tasksStatus/{_id}: post: - tags: &ref_39 + tags: &ref_38 - User Projects summary: Retrieve the status of specific tasks for a user project. requestBody: @@ -3251,7 +3253,7 @@ paths: value: taskIds: [] parameters: - - &ref_40 + - &ref_39 name: X-auth-token in: header schema: @@ -3261,7 +3263,7 @@ paths: To use this API, you require an X-auth-token. This token is available in the login API Response. required: true - - &ref_41 + - &ref_40 name: internal-access-token in: header schema: @@ -3292,7 +3294,7 @@ paths: submissionId: 5fbaa71d97ccef111cbb4ee0 '400': description: 'Bad response ' - headers: &ref_42 {} + headers: &ref_41 {} content: application/json: schema: @@ -5733,8 +5735,8 @@ paths: param: entityType msg: entityType required description: >- - Fetches solutions filtered by user role, entity type, and program, - with support for pagination and search. + Fetches solutions filtered by user role, entity type, and program, with + support for pagination and search. * The endpoint allows a user to retrieves solutions based on user role, entity type, and entities within a specified program is @@ -5994,7 +5996,8 @@ paths: param: _id msg: required solution link description: >- - Validates a solution link and provides details of the associated solution. + Validates a solution link and provides details of the associated + solution. * The endpoint allows a user to verifies the validity of a solution link and returns details about the targeted solution is `/v1/solutions/verifyLink`. @@ -6005,7 +6008,7 @@ paths: * This is a mandatory parameter and cannot be empty or null. /v1/solutions/verifySolution/{solutionId}: post: - tags: &ref_36 + tags: &ref_35 - Solutions summary: Verify solution by id. requestBody: @@ -6030,7 +6033,7 @@ paths: - 5fd1b52ab53a6416aaeefc80 - 5fd098e2e049735a86b748ac role: BEO - parameters: &ref_37 + parameters: &ref_36 - name: X-auth-token in: header schema: @@ -6069,7 +6072,7 @@ paths: _id: 5f6853f293734140ccce90cf '400': description: Bad response - headers: &ref_38 {} + headers: &ref_37 {} content: application/json: schema: @@ -6349,7 +6352,7 @@ paths: - BEO '400': description: Bad response - headers: &ref_35 {} + headers: &ref_34 {} content: application/json: schema: @@ -6750,7 +6753,8 @@ paths: description: >- This API retrieves a form based on the provided id. - * The endpoint allows a user to retrieves a form based on the provided id is `/v1/forms/read`. + * The endpoint allows a user to retrieves a form based on the provided + id is `/v1/forms/read`. * It is mandatory to provide values for parameters which are marked as `required` @@ -7176,8 +7180,8 @@ paths: param: name msg: Base template name required description: >- - Creates or updates a base certificate template using multipart - form data. Authentication is required. + Creates or updates a base certificate template using multipart form + data. Authentication is required. * The endpoint allows a user to creates certificate base template`/v1/certificateBaseTemplates/createOrUpdate`. @@ -7232,7 +7236,7 @@ paths: - in: path name: _id description: To update the data we need to match its id - schema: &ref_45 + schema: &ref_44 type: string required: true - in: header @@ -7550,7 +7554,7 @@ paths: - in: path name: categoryExternalId description: To list the data we need to match its id - schema: &ref_44 + schema: &ref_43 type: string required: true responses: @@ -8744,7 +8748,7 @@ paths: updatedAt: '2020-12-10T12:36:36.482Z' createdAt: '2020-12-10T12:36:36.482Z' __v: 0 - '400': &ref_34 + '400': &ref_47 description: 'Bad response ' headers: *ref_26 content: @@ -9498,7 +9502,7 @@ paths: externalId: b54a5c6d-98be-4313-af1c-33040b1703aa '400': description: Bad response - headers: *ref_35 + headers: *ref_34 content: application/json: schema: @@ -9526,7 +9530,7 @@ paths: * This is a mandatory parameter and cannot be empty or null. /v1/solutions/details/:{solutionId}: post: - tags: *ref_36 + tags: *ref_35 summary: To fetch solution details requestBody: content: @@ -9543,7 +9547,7 @@ paths: value: role: district_education_officer state: 66bc5afebb293e9d6930ad7a - parameters: *ref_37 + parameters: *ref_36 responses: '200': description: Successful response @@ -9610,7 +9614,7 @@ paths: description: Project 1 description '400': description: Bad response - headers: *ref_38 + headers: *ref_37 content: application/json: schema: @@ -9659,12 +9663,12 @@ paths: * This is a mandatory parameter and cannot be empty or null. /v1/userProjects/certificateReIssue/:{projectId}: post: - tags: *ref_39 + tags: *ref_38 summary: Reissue the certificate for a specified project. - requestBody: &ref_43 {} + requestBody: &ref_42 {} parameters: + - *ref_39 - *ref_40 - - *ref_41 - in: path name: projectId description: To fetch the data we need to match its id @@ -9685,7 +9689,7 @@ paths: _id: 66ac9949227504a96d8dce1c '400': description: 'Bad response ' - headers: *ref_42 + headers: *ref_41 content: application/json: schema: @@ -9727,12 +9731,12 @@ paths: * This is a mandatory parameter and cannot be empty or null. /v1/userProjects/verifyCertificate/:{projectId}: post: - tags: *ref_39 + tags: *ref_38 summary: Verifies the project certificate. - requestBody: *ref_43 + requestBody: *ref_42 parameters: + - *ref_39 - *ref_40 - - *ref_41 - in: path name: projectId description: '' @@ -9764,7 +9768,7 @@ paths: https://storage.googleapis.com/mentoring-dev-storage-private/project/25291645-6583-46ac-becb-35ce2ac1064c/1/d664feff-1d12-4877-be46-6fac41172852/template.svg?GoogleAccessId=sl-mentoring-dev-storage%40sl-dev-project.iam.gserviceaccount.com&Expires=1722956711&Signature=gUGRNgykUz3Jp%2FiNC82UkSkJhOXR3E8qY58UhkvCSGmJ%2FD34AdkR8HYropHfNeX191CwV%2F7%2FNR0bvgHubCg3a2v9qDelYK5tvAwZ%2FC9pUoaIyYTdmq6yqq9m%2FbdjC%2BmBERpLT8IDVfHl7nttw%2FiIhP%2BVj8NnejVbS%2FgFa7jOe7iFwNVjxXiBspfjnq1Dh9v%2FN9d6NibyTljQx%2FLDUpJ6DfwoAwmnytZUg94%2FwuwGpgCK0FbR78UlJGSanDtJvyZ%2FGh3pkV4ulr%2BLqLfSIMjLI3yjxBVNmkLv0f%2FXP3cH8ULvRT%2BzYtWXU0yRUK%2ByJaCgF%2BIzTCXtzXLZuCLhxxnOeA%3D%3D '400': description: 'Bad response ' - headers: *ref_42 + headers: *ref_41 content: application/json: schema: @@ -9826,19 +9830,19 @@ paths: - in: path name: _id description: To update the data we need to match its id - schema: *ref_44 + schema: *ref_43 required: true - in: header name: X-auth-token description: >- To use this API, you require an X-auth-token. This token is available in the login API Response. - schema: *ref_45 + schema: *ref_44 required: true - in: header name: internal-access-token description: '' - schema: *ref_45 + schema: *ref_44 required: true - in: header name: admin-auth-token @@ -10083,18 +10087,18 @@ paths: description: >- isAPrivateProgram is a boolean variable, which fetches different response on different value. - schema: &ref_47 + schema: &ref_46 type: boolean required: true - in: query name: page description: this query param suggests which page we need to fetch - schema: &ref_46 + schema: &ref_45 type: number - in: query name: limit description: limit suggests the number of entries in each page - schema: *ref_46 + schema: *ref_45 - in: query name: search description: >- @@ -10107,7 +10111,7 @@ paths: description: >- getProjectsCount is a boolean variable, which fetches different response on different value. - schema: *ref_47 + schema: *ref_46 - in: query name: language description: valid language code to be passed. @@ -10218,7 +10222,7 @@ paths: - Users summary: Fetch user solutions details. parameters: - - *ref_34 + - *ref_47 - in: path name: _id description: This is a program id. @@ -10795,7 +10799,8 @@ paths: requires authentication tokens in the headers. - * The API allows the user to create project attributes`v1/projectAttributes/create` + * The API allows the user to create project + attributes`v1/projectAttributes/create` * It is mandatory to provide values for parameters which are marked as `required` @@ -11052,7 +11057,8 @@ paths: params. - * The API allows the user to find project attributes `v1/projectAttributes/find` + * The API allows the user to find project attributes + `v1/projectAttributes/find` * It is mandatory to provide values for parameters which are marked as `required` @@ -11744,7 +11750,7 @@ paths: description: >- Need to pass Resource Type that we need to delete Ex: solution or program - schema: &ref_66 + schema: &ref_64 type: string required: true - in: header @@ -11754,27 +11760,27 @@ paths: authentication. This token is generated when a user logs in and must be included in each subsequent request to verify the user’s session and permissions. - schema: *ref_66 + schema: *ref_64 required: true - in: header name: internal-access-token description: 'internal-access-token ' - schema: *ref_66 + schema: *ref_64 required: true - in: header name: admin-auth-token description: admin-auth-token - schema: *ref_66 + schema: *ref_64 required: true - in: header name: tenantId description: tenantId - schema: *ref_66 + schema: *ref_64 required: true - in: header name: orgId description: orgId - schema: *ref_66 + schema: *ref_64 required: false responses: '200': @@ -11929,28 +11935,28 @@ paths: - in: header name: x-auth-token description: This is user token - schema: &ref_67 + schema: &ref_65 type: string required: true - in: header name: internal-access-token description: This is internal access token - schema: *ref_67 + schema: *ref_65 required: true - in: header name: admin-auth-token description: This is admin auth token - schema: *ref_67 + schema: *ref_65 required: true - in: header name: tenantId description: This is tenantId for which the resource needs to be created - schema: *ref_67 + schema: *ref_65 required: true - in: header name: orgId description: This is organization Id for which the resources needs to be created - schema: *ref_67 + schema: *ref_65 required: true /v1/organizationExtension/updateRelatedOrgs: post: @@ -12088,28 +12094,28 @@ paths: - in: header name: x-auth-token description: This is user token - schema: &ref_68 + schema: &ref_66 type: string required: true - in: header name: internal-access-token description: This is internal access token - schema: *ref_68 + schema: *ref_66 required: true - in: header name: admin-auth-token description: This is admin auth token - schema: *ref_68 + schema: *ref_66 required: true - in: header name: tenantId description: This is tenant Id used to create resources - schema: *ref_68 + schema: *ref_66 required: true - in: header name: orgId description: This is orgId used to create resources - schema: *ref_68 + schema: *ref_66 required: true /v1/userProjects/searchEntities/{solutionId}?search={searchText}: get: @@ -12228,9 +12234,9 @@ paths: * This is a mandatory parameter and cannot be empty or null. operationId: '' - tags: &ref_69 + tags: &ref_67 - User Projects - parameters: &ref_70 + parameters: &ref_68 - in: header name: X-auth-token description: >- @@ -12287,7 +12293,7 @@ paths: entityName: GOVT HIGH SCHOOL Gulbarga '400': description: Bad response - headers: &ref_71 {} + headers: &ref_69 {} content: application/json: schema: @@ -12333,7 +12339,7 @@ paths: sampleBodyData: value: entityId: 687a66c07f4bcd29a40c908d - parameters: &ref_72 + parameters: &ref_70 - name: _id in: path required: true @@ -12354,8 +12360,8 @@ paths: * This is a mandatory parameter and cannot be empty or null. operationId: '' - tags: *ref_69 - parameters: *ref_70 + tags: *ref_67 + parameters: *ref_68 responses: '200': description: Successful response @@ -12375,7 +12381,7 @@ paths: status: 200 '400': description: Bad response - headers: *ref_71 + headers: *ref_69 content: application/json: schema: @@ -12414,10 +12420,10 @@ paths: sampleBodyData: value: id: 1 - parameters: *ref_72 + parameters: *ref_70 /v1/project/templates/createChildProjectTemplate?isExternalProgram={true/false}&programExternalId={programExternalId}: get: - tags: *ref_73 + tags: *ref_71 summary: Create child project template parameters: - *ref_31 @@ -12457,7 +12463,7 @@ paths: "shikshalokam" } '400': description: 'Bad response ' - headers: *ref_74 + headers: *ref_72 content: application/json: schema: @@ -12538,5 +12544,108 @@ paths: schema: type: string description: Valid program external Id to be passed + /v1/userProjects/updateAcl/:_id: + post: + description: '' + operationId: '' + tags: + - User Projects + parameters: [] + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + message: + type: string + result: + type: object + properties: + _id: + type: string + acl: + type: object + properties: + visibility: + type: string + users: + type: array + items: + type: string + examples: + example1: + value: + success: true + message: Project updated successfully. + result: + _id: 68d38c9f69f139b91c9e57b8 + acl: + visibility: SPECIFIC + users: + - '1' + - '2' + - '3' + - '4' + '400': + description: '' + headers: {} + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + message: + type: string + examples: + example1: + value: + success: false + message: Project not found + requestBody: + content: + application/json: + schema: + type: object + properties: + acl: + type: object + properties: + visibility: + type: string + users: + type: array + items: + type: string + scope: + type: object + properties: {} + examples: + example1: + value: + acl: + visibility: SPECIFIC + users: + - '1' + scope: {} + parameters: + - in: path + name: _id + description: Required projectId + schema: + type: string + required: true + - in: header + name: x-auth-token + description: '' + schema: + type: string + required: true components: securitySchemes: {} diff --git a/constants/interface-routes/elevate-project/configs.json b/constants/interface-routes/elevate-project/configs.json index 29f9ea08..18b611e3 100644 --- a/constants/interface-routes/elevate-project/configs.json +++ b/constants/interface-routes/elevate-project/configs.json @@ -4115,34 +4115,6 @@ } ], "service": "project" - }, - { - "sourceRoute": "/project/v1/userProjects/updateAcl", - "type": "POST", - "priority": "MUST_HAVE", - "inSequence": false, - "orchestrated": false, - "targetPackages": [ - { - "basePackageName": "project", - "packageName": "elevate-project" - } - ], - "service": "project" - }, - { - "sourceRoute": "/project/v1/userProjects/updateAcl/:id", - "type": "POST", - "priority": "MUST_HAVE", - "inSequence": false, - "orchestrated": false, - "targetPackages": [ - { - "basePackageName": "project", - "packageName": "elevate-project" - } - ], - "service": "project" } ] } diff --git a/controllers/v1/userProjects.js b/controllers/v1/userProjects.js index b115d2ff..7953e95e 100644 --- a/controllers/v1/userProjects.js +++ b/controllers/v1/userProjects.js @@ -1567,6 +1567,34 @@ module.exports = class UserProjects extends Abstract { }) } + /** + * @api {post} /v1/userProjects/updateAcl/:_id={{projectId}} + * @apiVersion 1.0.0 + * @apiName Update ACL + * @apiHeader {String} X-auth-token Authenticity token + * @apiSampleRequest /v1/userProjects/updateAcl/68d38c9f69f139b91c9e57b8 + * @apiSampleRequest {json} Request + * { + "acl": { + "visibility" : "SPECIFIC", + "users" : ["1", "2", "3", "4"] + } + } + * @apiUse successBody + * @apiParamExample {json} Response: + { + "success" : true, + "message" : "Project updated successfully.", + "result" : { + "_id" : "68d38c9f69f139b91c9e57b8", + "acl" : { + "visibility" : "SPECIFIC", + "users" : ["1", "2", "3", "4"] + } + } + } + */ + /** * Update ACL for a project if user owns the project and submission level is ENTITY. * diff --git a/generics/constants/api-responses.js b/generics/constants/api-responses.js index 5672c607..05fb42d6 100644 --- a/generics/constants/api-responses.js +++ b/generics/constants/api-responses.js @@ -319,10 +319,9 @@ module.exports = { USER_EXTENSION_DELETED: 'User extension deleted successfully', NO_SOLUTION_FOUND_FOR_THE_LINK: 'This link appears to be invalid. Please use a valid link to continue.', ACCESS_TOKEN_EXPIRED_CODE: 'ACC_TOKEN_EXPIRED', - ACCESS_TOKEN_EXPIRED: 'Access Token Expired!! Please Login Again.', + ACCESS_TOKEN_EXPIRED: 'Access Token Expired! Please Login Again.', USER_SERVICE_DOWN_CODE: 'USER_SERVICE_DOWN', USER_SERVICE_DOWN: 'User service is down', - PROJECT_DOES_NOT_BELONG_TO_USER: 'Project does not belong to the user!!', - SUBMISSION_LEVEL_NOT_COMPLIED: 'Not allowed to update project. SUBMISSION_LEVEL not complied!!!', + SUBMISSION_LEVEL_NOT_COMPLIED: 'Not allowed to update project. SUBMISSION_LEVEL not complied!', PROJECT_UPDATED_SUCCESSFULLY: 'Project updated successfully.', } diff --git a/module/programs/validator/v1.js b/module/programs/validator/v1.js index dbcdb163..724d7d81 100644 --- a/module/programs/validator/v1.js +++ b/module/programs/validator/v1.js @@ -6,9 +6,9 @@ module.exports = (req) => { req.checkBody('requestForPIIConsent').exists().withMessage('required requestForPIIConsent value of program') req.checkBody('scope') .exists() - .withMessage('required solution scope') + .withMessage('required program scope') .notEmpty() - .withMessage('solution scope cannot be empty') + .withMessage('program scope cannot be empty') }, update: function () { req.checkParams('_id') diff --git a/module/solutions/helper.js b/module/solutions/helper.js index 476e19f2..f2fc347d 100644 --- a/module/solutions/helper.js +++ b/module/solutions/helper.js @@ -730,10 +730,15 @@ module.exports = class SolutionsHelper { CONSTANTS.common.MANDATORY_SCOPE_FIELD, CONSTANTS.common.OPTIONAL_SCOPE_FIELD ) + + // If a prefix is provided, modify all query conditions to apply on nested fields. + // Example: if prefix = "acl", a condition like { "scope": {...} } + // becomes { "acl.scope": {...} }. + // This ensures the targeting rules apply inside a specific nested object. if (prefix != '') { builtQuery['$and'] = builtQuery['$and'].map((obj) => { - const key = Object.keys(obj)[0] - const value = obj[key] + const key = Object.keys(obj)[0] // Extract the field name in the condition + const value = obj[key] // Extract the condition value return { [`${prefix}.${key}`]: value } }) } @@ -2121,7 +2126,6 @@ module.exports = class SolutionsHelper { } matchQuery['$match']['tenantId'] = userDetails.userInformation.tenantId - matchQuery['$match']['orgId'] = userDetails.userInformation.organizationId if (currentOrgOnly) { let organizationId = userDetails.userInformation.organizationId @@ -2534,27 +2538,27 @@ module.exports = class SolutionsHelper { ) }) - if (process.env.SUBMISSION_LEVEL == 'ENTITY') { - mergedData = userCreatedProjects.data.data - totalCount = mergedData.length - if (mergedData.length > 0) { - let startIndex = pageSize * (pageNo - 1) - let endIndex = startIndex + pageSize - mergedData = mergedData.slice(startIndex, endIndex) - } - return resolve({ - success: true, - message: CONSTANTS.apiResponses.TARGETED_SOLUTIONS_FETCHED, - data: { - data: mergedData, - count: totalCount, - }, - result: { - data: mergedData, - count: totalCount, - }, - }) - } + // if (process.env.SUBMISSION_LEVEL == 'ENTITY') { + // mergedData = userCreatedProjects.data.data + // totalCount = mergedData.length + // if (mergedData.length > 0) { + // let startIndex = pageSize * (pageNo - 1) + // let endIndex = startIndex + pageSize + // mergedData = mergedData.slice(startIndex, endIndex) + // } + // return resolve({ + // success: true, + // message: CONSTANTS.apiResponses.TARGETED_SOLUTIONS_FETCHED, + // data: { + // data: mergedData, + // count: totalCount, + // }, + // result: { + // data: mergedData, + // count: totalCount, + // }, + // }) + // } // Add program data to the fetched projects if (userCreatedProjects.success && userCreatedProjects.data) { totalCount = userCreatedProjects.data.count @@ -3181,10 +3185,10 @@ module.exports = class SolutionsHelper { { 'acl.visibility': CONSTANTS.common.PROJECT_VISIBILITY_ALL }, { 'acl.visibility': CONSTANTS.common.PROJECT_VISIBILITY_SPECIFIC, - 'acl.users': { $in: [userId] }, + 'acl.users': { $in: [userId.toString()] }, }, { 'acl.visibility': CONSTANTS.common.PROJECT_VISIBILITY_SCOPE, ...matchQuery }, - { 'acl.visibility': CONSTANTS.common.PROJECT_VISIBILITY_SELF, userId: userId }, + { createdBy: userId }, ], } } else { diff --git a/module/userProjects/helper.js b/module/userProjects/helper.js index cd0e302a..654c301a 100644 --- a/module/userProjects/helper.js +++ b/module/userProjects/helper.js @@ -152,7 +152,6 @@ module.exports = class UserProjectsHelper { message: CONSTANTS.apiResponses.USER_PROJECT_NOT_FOUND, } } - // if entityId & entityInformation are passed through payload, ignore them const blackListedPayloadItems = ['entityId', 'entityInformation', 'acl'] blackListedPayloadItems.map((payloadItem) => { @@ -182,6 +181,7 @@ module.exports = class UserProjectsHelper { // validate user authenticity if the acl.visibility of project is SCOPE else if (userProject[0].acl.visibility == CONSTANTS.common.PROJECT_VISIBILITY_SCOPE) { let scopeData = data.userProfileInformation.scope + scopeData['tenantId'] = tenantId let queryData = await solutionsHelper.queryBasedOnRoleAndLocation(scopeData, '', 'acl') if (!queryData.success) { return resolve(queryData) @@ -4519,22 +4519,30 @@ module.exports = class UserProjectsHelper { // Check if project exists and belongs to the user const projectData = await projectQueries.projectDocument({ _id: projectId, - userId, + createdBy: userId, tenantId, }) if (!projectData || projectData.length === 0) { throw { success: false, - message: CONSTANTS.apiResponses.PROJECT_DOES_NOT_BELONG_TO_USER, + message: CONSTANTS.apiResponses.PROJECT_NOT_FOUND, + status: HTTP_STATUS_CODE['bad_request'].status, + } + } + if ( + bodyData.acl.visibility === CONSTANTS.common.PROJECT_VISIBILITY_SPECIFIC && + bodyData.acl.users.length > 0 + ) { + if (!bodyData.acl.users.includes(userId.toString())) { + bodyData.acl.users.push(userId.toString()) } } - // Update ACL field in the project const updatedProject = await projectQueries.findOneAndUpdate( { _id: projectId, - userId, + createdBy: userId, tenantId, }, { diff --git a/module/userProjects/validator/v1.js b/module/userProjects/validator/v1.js index 780ffb75..65a54010 100644 --- a/module/userProjects/validator/v1.js +++ b/module/userProjects/validator/v1.js @@ -132,6 +132,36 @@ module.exports = (req) => { } req.checkQuery(existId).exists().withMessage('required solution or projectId Id') }, + updateAcl: function () { + // Validate path param: id + req.checkParams('_id') + .exists() + .withMessage('projectId is required in path params') + .isMongoId() + .withMessage('projectId must be a valid MongoDB ObjectId') + + // acl must be present + req.checkBody('acl').exists().withMessage('acl is required in body') + + // acl.visibility must be present and string + req.checkBody('acl.visibility') + .exists() + .withMessage('acl.visibility is required') + .isString() + .withMessage('acl.visibility must be a string') + + // if acl.users is present → must be an array + req.checkBody('acl.users') + .optional() + .custom((value) => Array.isArray(value)) + .withMessage('acl.users must be an array if provided') + + // if acl.scope is present → must be an object + req.checkBody('acl.scope') + .optional() + .custom((value) => typeof value === 'object' && !Array.isArray(value)) + .withMessage('acl.scope must be an object if provided') + }, } if (projectsValidator[req.params.method]) { From cbbfcde5c1f8e15a1b2faebc0f72c1bfeb714325 Mon Sep 17 00:00:00 2001 From: prajwal Date: Fri, 2 Jan 2026 13:04:16 +0530 Subject: [PATCH 4/8] unwanted code removed --- module/solutions/helper.js | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/module/solutions/helper.js b/module/solutions/helper.js index f2fc347d..c6e3e5bd 100644 --- a/module/solutions/helper.js +++ b/module/solutions/helper.js @@ -2538,27 +2538,6 @@ module.exports = class SolutionsHelper { ) }) - // if (process.env.SUBMISSION_LEVEL == 'ENTITY') { - // mergedData = userCreatedProjects.data.data - // totalCount = mergedData.length - // if (mergedData.length > 0) { - // let startIndex = pageSize * (pageNo - 1) - // let endIndex = startIndex + pageSize - // mergedData = mergedData.slice(startIndex, endIndex) - // } - // return resolve({ - // success: true, - // message: CONSTANTS.apiResponses.TARGETED_SOLUTIONS_FETCHED, - // data: { - // data: mergedData, - // count: totalCount, - // }, - // result: { - // data: mergedData, - // count: totalCount, - // }, - // }) - // } // Add program data to the fetched projects if (userCreatedProjects.success && userCreatedProjects.data) { totalCount = userCreatedProjects.data.count From b7cf532332ec1ac422b33473e1183090cec9157c Mon Sep 17 00:00:00 2001 From: prajwal Date: Fri, 5 Dec 2025 16:39:13 +0530 Subject: [PATCH 5/8] multi-user project access feature modification --- .../elevate-project/configs.json | 28 +++ controllers/v1/userProjects.js | 23 +++ generics/constants/api-responses.js | 3 + module/solutions/helper.js | 19 +- module/userProjects/helper.js | 162 ++++++++++-------- 5 files changed, 160 insertions(+), 75 deletions(-) diff --git a/constants/interface-routes/elevate-project/configs.json b/constants/interface-routes/elevate-project/configs.json index 18b611e3..29f9ea08 100644 --- a/constants/interface-routes/elevate-project/configs.json +++ b/constants/interface-routes/elevate-project/configs.json @@ -4115,6 +4115,34 @@ } ], "service": "project" + }, + { + "sourceRoute": "/project/v1/userProjects/updateAcl", + "type": "POST", + "priority": "MUST_HAVE", + "inSequence": false, + "orchestrated": false, + "targetPackages": [ + { + "basePackageName": "project", + "packageName": "elevate-project" + } + ], + "service": "project" + }, + { + "sourceRoute": "/project/v1/userProjects/updateAcl/:id", + "type": "POST", + "priority": "MUST_HAVE", + "inSequence": false, + "orchestrated": false, + "targetPackages": [ + { + "basePackageName": "project", + "packageName": "elevate-project" + } + ], + "service": "project" } ] } diff --git a/controllers/v1/userProjects.js b/controllers/v1/userProjects.js index 011fcef9..b115d2ff 100644 --- a/controllers/v1/userProjects.js +++ b/controllers/v1/userProjects.js @@ -1566,4 +1566,27 @@ module.exports = class UserProjects extends Abstract { } }) } + + /** + * Update ACL for a project if user owns the project and submission level is ENTITY. + * + * @method POST + * @name updateAcl + * @param {Object} req - request Data. + * @returns {Promise} - Response object containing update status, message, and updated ACL data + */ + async updateAcl(req) { + return new Promise(async (resolve, reject) => { + try { + const response = await userProjectsHelper.updateAcl(req.params._id, req.body, req.userDetails) + return resolve(response) + } catch (error) { + return reject({ + status: error.status || HTTP_STATUS_CODE.internal_server_error.status, + message: error.message || HTTP_STATUS_CODE.internal_server_error.message, + errorObject: error, + }) + } + }) + } } diff --git a/generics/constants/api-responses.js b/generics/constants/api-responses.js index 78fef811..9ccae3d7 100644 --- a/generics/constants/api-responses.js +++ b/generics/constants/api-responses.js @@ -324,4 +324,7 @@ module.exports = { USER_SERVICE_DOWN: 'User service is down', INVALID_USER_PERMISSION: 'User do not have required permission ', INVALID_USER_PERMISSION_CODE: 'INVALID_USER_PERMISSION', + PROJECT_DOES_NOT_BELONG_TO_USER: 'Project does not belong to the user!!', + SUBMISSION_LEVEL_NOT_COMPLIED: 'Not allowed to update project. SUBMISSION_LEVEL not complied!!!', + PROJECT_UPDATED_SUCCESSFULLY: 'Project updated successfully.', } diff --git a/module/solutions/helper.js b/module/solutions/helper.js index 936079a3..7f20bd83 100644 --- a/module/solutions/helper.js +++ b/module/solutions/helper.js @@ -27,6 +27,7 @@ const solutionsUtils = require(GENERICS_FILES_PATH + '/helpers/solutionAndProjec const surveyService = require(GENERICS_FILES_PATH + '/services/survey') const moment = require('moment-timezone') +const { forEach } = require('lodash') /** * SolutionsHelper * @class @@ -731,6 +732,13 @@ module.exports = class SolutionsHelper { CONSTANTS.common.MANDATORY_SCOPE_FIELD, CONSTANTS.common.OPTIONAL_SCOPE_FIELD ) + if (prefix != '') { + builtQuery['$and'] = builtQuery['$and'].map((obj) => { + const key = Object.keys(obj)[0] + const value = obj[key] + return { [`${prefix}.${key}`]: value } + }) + } filterQuery = { ...filterQuery, ...builtQuery, @@ -2537,7 +2545,7 @@ module.exports = class SolutionsHelper { ) }) - if (process.env.SUBMISSION_LEVEL == 'ENTITY' && requestedData.hasOwnProperty('entityId')) { + if (process.env.SUBMISSION_LEVEL == 'ENTITY') { mergedData = userCreatedProjects.data.data totalCount = mergedData.length if (mergedData.length > 0) { @@ -3167,13 +3175,9 @@ module.exports = class SolutionsHelper { try { let query = { isDeleted: false } - if (process.env.SUBMISSION_LEVEL === 'ENTITY' && requestedData.hasOwnProperty('entityId')) { + if (process.env.SUBMISSION_LEVEL === 'ENTITY') { // Use queryBasedOnRoleAndLocation function to form query for acl.visibility = SCOPE projects - let queryData = await this.queryBasedOnRoleAndLocation( - _.omit(requestedData, ['entityId']), - '', - 'acl' - ) + let queryData = await this.queryBasedOnRoleAndLocation(requestedData, '', 'acl') // status of the project could be anything, hence deleting status property from the querydata delete queryData.data.status // isReusable field doesn't exist for projects model hence removing the key @@ -3183,7 +3187,6 @@ module.exports = class SolutionsHelper { // Construct query for projects accessible by the user query = { - entityId: requestedData.entityId, 'solutionInformation.submissionLevel': process.env.SUBMISSION_LEVEL, $or: [ { 'acl.visibility': CONSTANTS.common.PROJECT_VISIBILITY_ALL }, diff --git a/module/userProjects/helper.js b/module/userProjects/helper.js index 75e9f6a9..0d70b045 100644 --- a/module/userProjects/helper.js +++ b/module/userProjects/helper.js @@ -34,6 +34,7 @@ const path = require('path') const gotenbergService = require(SERVICES_BASE_PATH + '/gotenberg') const projectService = require(SERVICES_BASE_PATH + '/projects') const defaultUserProfileConfig = require('@config/defaultUserProfileDeleteConfig') +const { result } = require('lodash') const configFilePath = process.env.AUTH_CONFIG_FILE_PATH const surveyService = require(SERVICES_BASE_PATH + '/survey') @@ -154,7 +155,7 @@ module.exports = class UserProjectsHelper { } // if entityId & entityInformation are passed through payload, ignore them - const blackListedPayloadItems = ['entityId', 'entityInformation'] + const blackListedPayloadItems = ['entityId', 'entityInformation', 'acl'] blackListedPayloadItems.map((payloadItem) => { if (data.hasOwnProperty(payloadItem)) delete data[payloadItem] }) @@ -274,7 +275,7 @@ module.exports = class UserProjectsHelper { const projectsModel = Object.keys(schemas['projects'].schema) - let keysToRemoveFromUpdation = ['userRoleInformation', 'userProfile', 'certificate'] + let keysToRemoveFromUpdation = ['userRoleInformation', 'userProfile', 'certificate', 'acl'] keysToRemoveFromUpdation.forEach((key) => { if (data[key]) delete data[key] }) @@ -302,70 +303,6 @@ module.exports = class UserProjectsHelper { if (projectData && projectData.success == true) { updateProject = _.merge(updateProject, projectData.data) } - // let createNewProgramAndSolution = false; - // let solutionExists = false; - - // if (data.programId && data.programId !== "") { - - // // Check if program already existed in project and if its not an existing program. - // if (!userProject[0].programInformation) { - // createNewProgramAndSolution = true; - // } else if ( - // userProject[0].programInformation && - // userProject[0].programInformation._id && - // userProject[0].programInformation._id.toString() !== data.programId - // ) { - // // Not an existing program. - - // solutionExists = true; - // } - - // } else if (data.programName) { - - // if (!userProject[0].solutionInformation) { - // createNewProgramAndSolution = true; - // } else { - // solutionExists = true; - // // create new program using current name and add existing solution and remove program from it. - // } - // } - - // if (createNewProgramAndSolution || solutionExists) { - - // let programAndSolutionInformation = - // await this.createProgramAndSolution( - // data.programId, - // data.programName, - // updateProject.entityId ? [updateProject.entityId] : "", - // userToken, - // userProject[0].solutionInformation && userProject[0].solutionInformation._id ? - // userProject[0].solutionInformation._id : "" - // ); - - // if (!programAndSolutionInformation.success) { - // return resolve(programAndSolutionInformation); - // } - - // if (solutionExists) { - - // let updateProgram = - // await programsHelper.removeSolutions( - // userToken, - // userProject[0].programInformation._id, - // [userProject[0].solutionInformation._id] - // ); - - // if (!updateProgram.success) { - // throw { - // status: HTTP_STATUS_CODE.bad_request.status, - // message: CONSTANTS.apiResponses.PROGRAM_NOT_UPDATED - // } - // } - // } - - // updateProject = - // _.merge(updateProject, programAndSolutionInformation.data); - // } let booleanData = this.booleanData(schemas['projects'].schema) let mongooseIdData = this.mongooseIdData(schemas['projects'].schema) @@ -1698,7 +1635,10 @@ module.exports = class UserProjectsHelper { if (bodyData.hasOwnProperty('acl')) { bodyData.acl.visibility = bodyData.acl.visibility.toUpperCase() bodyData.acl.users.push(userId) - if (!bodyData.acl.hasOwnProperty('scope') || !(bodyData.acl.scope.length > 0)) { + if ( + !bodyData.acl.hasOwnProperty('scope') || + !(Object.keys(bodyData.acl.scope.length) > 0) + ) { bodyData.acl['scope'] = solutionDetails.scope } projectCreation.data['acl'] = bodyData.acl @@ -4282,6 +4222,7 @@ module.exports = class UserProjectsHelper { validateAllTasks(allTasksFalttened) } + delete updateData.acl let updateResult = await this.sync( projectId, '', @@ -4550,6 +4491,93 @@ module.exports = class UserProjectsHelper { } }) } + + /** + * Update ACL for a project if user owns the project and submission level is ENTITY. + * + * @param {String} projectId - The ID of the project whose ACL should be updated + * @param {Object} bodyData - The request body containing ACL updates + * @param {Object} userDetails - Logged-in user's details object + * @returns {Promise} - Response object containing update status, message, and updated ACL data + * + * @throws {Object} - Throws an error if submission level is invalid or project update fails + */ + static updateAcl(projectId, bodyData, userDetails) { + return new Promise(async (resolve, reject) => { + try { + // Only allow updates if submission level is ENTITY + if (process.env.SUBMISSION_LEVEL !== 'ENTITY') { + throw { + success: false, + message: CONSTANTS.apiResponses.SUBMISSION_LEVEL_NOT_COMPLIED, + } + } + + // Extract user and tenant IDs + const userId = userDetails.userInformation.userId + const tenantId = userDetails.userInformation.tenantId + + // Check if project exists and belongs to the user + const projectData = await projectQueries.projectDocument({ + _id: projectId, + userId, + tenantId, + }) + + if (!projectData || projectData.length === 0) { + throw { + success: false, + message: CONSTANTS.apiResponses.PROJECT_DOES_NOT_BELONG_TO_USER, + } + } + + // Update ACL field in the project + const updatedProject = await projectQueries.findOneAndUpdate( + { + _id: projectId, + userId, + tenantId, + }, + { + $set: { acl: bodyData.acl }, + }, + { + new: true, + } + ) + + // Check if update was successful + if (!updatedProject) { + throw { + success: false, + message: CONSTANTS.apiResponses.PROJECT_UPDATE_FAILED, + } + } + + // Success response + return resolve({ + success: true, + status: 200, + message: CONSTANTS.apiResponses.PROJECT_UPDATED_SUCCESSFULLY, + result: { + _id: updatedProject._id, + acl: updatedProject.acl, + }, + data: { + _id: updatedProject._id, + acl: updatedProject.acl, + }, + }) + } catch (error) { + // Unified error response + return reject({ + message: error.message, + success: false, + status: error.status ? error.status : HTTP_STATUS_CODE.internal_server_error.status, + }) + } + }) + } } /** From 4991be358acce01ee578113c04b4de5b9a1c7897 Mon Sep 17 00:00:00 2001 From: prajwal Date: Fri, 5 Dec 2025 16:50:23 +0530 Subject: [PATCH 6/8] unwanted code removed --- module/solutions/helper.js | 1 - module/userProjects/helper.js | 1 - 2 files changed, 2 deletions(-) diff --git a/module/solutions/helper.js b/module/solutions/helper.js index 7f20bd83..bd8357fb 100644 --- a/module/solutions/helper.js +++ b/module/solutions/helper.js @@ -27,7 +27,6 @@ const solutionsUtils = require(GENERICS_FILES_PATH + '/helpers/solutionAndProjec const surveyService = require(GENERICS_FILES_PATH + '/services/survey') const moment = require('moment-timezone') -const { forEach } = require('lodash') /** * SolutionsHelper * @class diff --git a/module/userProjects/helper.js b/module/userProjects/helper.js index 0d70b045..461956fd 100644 --- a/module/userProjects/helper.js +++ b/module/userProjects/helper.js @@ -34,7 +34,6 @@ const path = require('path') const gotenbergService = require(SERVICES_BASE_PATH + '/gotenberg') const projectService = require(SERVICES_BASE_PATH + '/projects') const defaultUserProfileConfig = require('@config/defaultUserProfileDeleteConfig') -const { result } = require('lodash') const configFilePath = process.env.AUTH_CONFIG_FILE_PATH const surveyService = require(SERVICES_BASE_PATH + '/survey') From fc24e760054d8eb549c3ab1fc1d5cdd4030c462a Mon Sep 17 00:00:00 2001 From: prajwal Date: Mon, 8 Dec 2025 17:23:46 +0530 Subject: [PATCH 7/8] comments addressed --- .../Elevate-Project.postman_collection.json | 92 ++++-- api-doc/api-doc.yaml | 271 ++++++++++++------ .../elevate-project/configs.json | 28 -- controllers/v1/userProjects.js | 28 ++ generics/constants/api-responses.js | 3 +- module/programs/validator/v1.js | 4 +- module/solutions/helper.js | 56 ++-- module/userProjects/helper.js | 18 +- module/userProjects/validator/v1.js | 30 ++ 9 files changed, 358 insertions(+), 172 deletions(-) diff --git a/api-doc/Elevate-Project.postman_collection.json b/api-doc/Elevate-Project.postman_collection.json index f7d38599..b2e02438 100644 --- a/api-doc/Elevate-Project.postman_collection.json +++ b/api-doc/Elevate-Project.postman_collection.json @@ -3,7 +3,7 @@ "_postman_id": "137475d2-44ec-4746-84ec-1f12779168ec", "name": "Elevate-Project", "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", - "_exporter_id": "33513401" + "_exporter_id": "28589818" }, "item": [ { @@ -345,7 +345,7 @@ } }, "url": { - "raw": "{{baseUrl}}/project/v1/project/templates/createChildProjectTemplate?isExternalProgram=true&programExternalId=AWS_Prog_MYS_may192", + "raw": "{{baseUrl}}/v1/project/templates/createChildProjectTemplate?isExternalProgram=true&programExternalId=AWS_Prog_MYS_may192", "host": ["{{baseUrl}}"], "path": ["v1", "project", "templates", "createChildProjectTemplate"], "query": [ @@ -648,7 +648,7 @@ ] }, "url": { - "raw": "{{baseUrl}}/project/v1/project/templateTasks/bulkUpdate/:projectTemplateId", + "raw": "{{baseUrl}}/v1/project/templateTasks/bulkUpdate/:projectTemplateId", "host": ["{{baseUrl}}"], "path": ["v1", "project", "templateTasks", "bulkUpdate", ":projectTemplateId"], "variable": [ @@ -832,7 +832,7 @@ "raw": "{\n \"_id\" : \"6624ff5124fc48e1fcdbec29\",\n \"userId\" : \"2\",\n \"userRole\" : \"BEO\",\n \"status\" : \"started\",\n \"isDeleted\" : false,\n \"categories\" : [\n {\n \"externalId\" : \"\",\n \"_id\" : \"\"\n }\n ],\n \"createdBy\" : \"2\",\n \"tasks\" : [\n {\n \"externalId\" : \"Task1-1710148664591\",\n \"name\" : \"Address the assembly and share ideas about Smart Learn\",\n \"_id\" : \"1234\",\n \"type\" : \"observation\",\n \"status\" : \"notStarted\",\n \"isDeleted\" : false,\n \"isDeletable\" : true,\n \"createdAt\" : \"2024-04-21T11:58:09.204+0000\",\n \"updatedAt\" : \"2024-04-21T11:58:09.204+0000\",\n \"isImportedFromLibrary\" : false,\n \"syncedAt\" : \"2024-04-21T11:58:09.204+0000\",\n \"children\" : [\n\n ]\n }\n ],\n \"updatedBy\" : \"2\",\n \"learningResources\" : [\n\n ],\n \"hasAcceptedTAndC\" : false,\n \"taskSequence\" : [\n\n ],\n \"recommendedFor\" : [\n\n ],\n \"attachments\" : [\n\n ],\n \"deleted\" : false,\n \"userProfile\" : {\n \"id\" : 2,\n \"email\" : \"prajwal@tunerlabs.com\",\n \"email_verified\" : \"false\",\n \"name\" : \"prajwal\",\n \"gender\" : null,\n \"location\" : null,\n \"about\" : null,\n \"share_link\" : null,\n \"status\" : \"ACTIVE\",\n \"image\" : null,\n \"last_logged_in_at\" : \"2024-04-21T07:44:54.239Z\",\n \"has_accepted_terms_and_conditions\" : true,\n \"languages\" : null,\n \"preferred_language\" : \"en\",\n \"organization_id\" : 1,\n \"roles\" : [\n 3\n ],\n \"meta\" : null,\n \"created_at\" : \"2024-03-22T07:11:10.925Z\",\n \"updated_at\" : \"2024-04-21T07:44:54.240Z\",\n \"deleted_at\" : null,\n \"organization\" : {\n \"id\" : 1,\n \"name\" : \"Default Organization\",\n \"code\" : \"default_code\"\n },\n \"user_roles\" : [\n {\n \"id\" : 3,\n \"title\" : \"mentee\",\n \"user_type\" : 0,\n \"status\" : \"ACTIVE\"\n }\n ]\n },\n \"syncedAt\" : \"2024-04-21T11:58:09.183+0000\",\n \"entityInformation\" : {\n \"_id\" : \"5f33c3d85f637784791cd830\",\n \"childHierarchyPath\" : [\n \"district\",\n \"beat\",\n \"cluster\",\n \"school\"\n ],\n \"deleted\" : false,\n \"entityTypeId\" : \"5f32d8228e0dc8312404056e\",\n \"entityType\" : \"state\",\n \"metaInformation\" : {\n \"externalId\" : \"MH\",\n \"name\" : \"Maharashtra\",\n \"region\" : \"West\",\n \"capital\" : \"Mumbai\"\n },\n \"updatedBy\" : \"124fdade-aaa2-4587-9dcd-3c7cf15c7147\",\n \"createdBy\" : \"2b655fd1-201d-4d2a-a1b7-9048a25c0afa\",\n \"updatedAt\" : \"2021-01-18T06:51:31.086Z\",\n \"createdAt\" : \"2020-08-12T10:26:32.038Z\",\n \"__v\" : 0,\n \"groups\" : {\n \"district\" : [\n \"5f33c56fb451f58478b36996\"\n ],\n \"beat\" : [\n \"5f33cb24c1352f84a29f547c\",\n \"5f33cb24c1352f84a29f547d\"\n ],\n \"cluster\" : [\n \"5f33cb07ce438a849b4a17f6\",\n \"5f33cb07ce438a849b4a17f7\"\n ],\n \"school\" : [\n \"5f33caebb451f58478b36998\",\n \"5f33caebb451f58478b36999\"\n ]\n },\n \"registryDetails\" : {\n \"locationId\" : \"db331a8c-b9e2-45f8-b3c0-7ec1e826b6df\",\n \"code\" : \"db331a8c-b9e2-45f8-b3c0-7ec1e826b6df\"\n }\n },\n \"entityId\" : \"5f33c3d85f637784791cd830\",\n \"programInformation\" : {\n \"_id\" : \"5f5f6f66f69c8a0fd28a8aba\",\n \"name\" : \"My Program\",\n \"description\" : \"Unnati-Default-Program\",\n \"isAPrivateProgram\" : true\n },\n \"taskReport\" : {\n \"total\" : 1,\n \"notStarted\" : 1\n },\n \"description\" : \"Come See Our School!- Parent Mela vishwa\",\n \"title\" : \"Come See Our School!- Parent Mela\",\n \"metaInformation\" : {\n \"rationale\" : \"\",\n \"primaryAudience\" : [\n \"Community\"\n ],\n \"goal\" : \"Organizing the Parent Mela in the school in order to make better community reach\",\n \"duration\" : \"At the end of every quarter\",\n \"successIndicators\" : \"\",\n \"risks\" : \"\",\n \"approaches\" : \"\"\n },\n \"programId\" : \"5f5f6f66f69c8a0fd28a8aba\",\n \"lastDownloadedAt\" : \"2024-04-21T11:58:09.204+0000\",\n \"updatedAt\" : \"2024-04-21T11:58:09.213+0000\",\n \"createdAt\" : \"2024-04-21T11:58:09.213+0000\",\n \"__v\" : 0\n}\n" }, "url": { - "raw": "{{baseUrl}}/project/v1/userProjects/sync/6624ff5124fc48e1fcdbec29?lastDownloadedAt=2024-04-21T11:58:09.204Z", + "raw": "{{baseUrl}}/v1/userProjects/sync/6624ff5124fc48e1fcdbec29?lastDownloadedAt=2024-04-21T11:58:09.204Z", "host": ["{{baseUrl}}"], "path": ["v1", "userProjects", "sync", "6624ff5124fc48e1fcdbec29"], "query": [ @@ -875,7 +875,7 @@ "raw": " {\n \"userRole\": \"DEO\",\n \"externalId\" : \"TSCSLHAR02-1710148664591\",\n \"createdFor\": [\n \"0126796199493140480\"\n ],\n \"status\": \"completed\",\n \"isDeleted\": false,\n \"description\": \"Come See Our School!- Parent Mela vishwa\",\n \"title\": \"Come See Our School!- Parent Mela\",\n \"metaInformation\": {\n \"rationale\": \"\",\n \"primaryAudience\": [\n \"Community\"\n ],\n \"goal\": \"Organizing the Parent Mela in the school in order to make better community reach\",\n \"duration\": \"At the end of every quarter\",\n \"successIndicators\": \"\",\n \"risks\": \"\",\n \"approaches\": \"\"\n },\n \"isAPrivateProgram\": false,\n \"hasAcceptedTAndC\": false,\n \"entityId\":\"a4268dc5-61c3-4eab-9a89-ffaaee809147\"\n }" }, "url": { - "raw": "{{baseUrl}}/project/v1/userProjects/details?solutionId=66727b63cf0222d7ec1fa7e0&templateId=IMP-3147f", + "raw": "{{baseUrl}}/v1/userProjects/details?solutionId=66727b63cf0222d7ec1fa7e0&templateId=IMP-3147f", "host": ["{{baseUrl}}"], "path": ["v1", "userProjects", "details"], "query": [ @@ -1105,7 +1105,7 @@ } ], "url": { - "raw": "{{baseUrl}}/project/v1/userProjects/searchEntities?solutionId=6880adeee1b9e76a809cd9db&page=10&limit=5", + "raw": "{{baseUrl}}/v1/userProjects/searchEntities?solutionId=6880adeee1b9e76a809cd9db&page=10&limit=5", "host": ["{{baseUrl}}"], "path": ["v1", "userProjects", "searchEntities"], "query": [ @@ -1147,7 +1147,7 @@ } }, "url": { - "raw": "{{baseUrl}}/project/v1/userProjects/addEntity/68d38c9f69f139b91c9e57b8", + "raw": "{{baseUrl}}/v1/userProjects/addEntity/68d38c9f69f139b91c9e57b8", "host": ["{{baseUrl}}"], "path": ["v1", "userProjects", "addEntity", "68d38c9f69f139b91c9e57b8"] } @@ -1180,12 +1180,46 @@ } }, "url": { - "raw": "{{baseUrl}}/project/v1/userProjects/deleteUserPIIData", + "raw": "{{baseUrl}}/v1/userProjects/deleteUserPIIData", "host": ["{{baseUrl}}"], "path": ["v1", "userProjects", "deleteUserPIIData"] } }, "response": [] + }, + { + "name": "updateAcl", + "request": { + "method": "POST", + "header": [ + { + "key": "x-auth-token", + "value": "{{userToken}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"acl\" : {\n \"visibility\" : \"SPECIFIC\",\n \"users\" : [\"1\",\"2\",\"3\"],\n \"scope\" : {}\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}project/v1/userProjects/updateAcl/:_id", + "host": ["{{baseUrl}}project"], + "path": ["v1", "userProjects", "updateAcl", ":_id"], + "variable": [ + { + "key": "_id", + "value": "68efa7a15a74ed062521a5ca" + } + ] + } + }, + "response": [] } ] }, @@ -1209,7 +1243,7 @@ } ], "url": { - "raw": "{{baseUrl}}/project/v1/reports/entity?requestPdf=true&reportType=0", + "raw": "{{baseUrl}}/v1/reports/entity?requestPdf=true&reportType=0", "host": ["{{baseUrl}}"], "path": ["v1", "reports", "entity"], "query": [ @@ -1243,7 +1277,7 @@ } ], "url": { - "raw": "{{baseUrl}}/project/v1/reports/detailView?requestPdf=true&reportType=2&programId=5f3bc13016fdc4ed008171ae", + "raw": "{{baseUrl}}/v1/reports/detailView?requestPdf=true&reportType=2&programId=5f3bc13016fdc4ed008171ae", "host": ["{{baseUrl}}"], "path": ["v1", "reports", "detailView"], "query": [ @@ -2610,7 +2644,7 @@ ] }, "url": { - "raw": "{{baseUrl}}/project/v1/certificateBaseTemplates/createOrUpdate", + "raw": "{{baseUrl}}/v1/certificateBaseTemplates/createOrUpdate", "host": ["{{baseUrl}}"], "path": ["v1", "certificateBaseTemplates", "createOrUpdate"] } @@ -2667,7 +2701,7 @@ ] }, "url": { - "raw": "{{baseUrl}}/project/v1/certificateBaseTemplates/createOrUpdate/6646e0a7b5226e19a19edf37", + "raw": "{{baseUrl}}/v1/certificateBaseTemplates/createOrUpdate/6646e0a7b5226e19a19edf37", "host": ["{{baseUrl}}"], "path": ["v1", "certificateBaseTemplates", "createOrUpdate", "6646e0a7b5226e19a19edf37"] } @@ -2718,7 +2752,7 @@ } }, "url": { - "raw": "{{baseUrl}}/project/v1/certificateTemplates/createOrUpdate", + "raw": "{{baseUrl}}/v1/certificateTemplates/createOrUpdate", "host": ["{{baseUrl}}"], "path": ["v1", "certificateTemplates", "createOrUpdate"] } @@ -2764,7 +2798,7 @@ } }, "url": { - "raw": "{{baseUrl}}/project/v1/certificateTemplates/createOrUpdate", + "raw": "{{baseUrl}}/v1/certificateTemplates/createOrUpdate", "host": ["{{baseUrl}}"], "path": ["v1", "certificateTemplates", "createOrUpdate"] } @@ -2811,7 +2845,7 @@ ] }, "url": { - "raw": "{{baseUrl}}/project/v1/certificateTemplates/uploadTemplate/:_id?updateTemplate=true", + "raw": "{{baseUrl}}/v1/certificateTemplates/uploadTemplate/:_id?updateTemplate=true", "host": ["{{baseUrl}}"], "path": ["v1", "certificateTemplates", "uploadTemplate", ":_id"], "query": [ @@ -2913,7 +2947,7 @@ ] }, "url": { - "raw": "{{baseUrl}}/project/v1/certificateTemplates/createSvg?baseTemplateId=66bb5c3e196c83581cc26aa8", + "raw": "{{baseUrl}}/v1/certificateTemplates/createSvg?baseTemplateId=66bb5c3e196c83581cc26aa8", "host": ["{{baseUrl}}"], "path": ["v1", "certificateTemplates", "createSvg"], "query": [ @@ -2943,7 +2977,7 @@ } ], "url": { - "raw": "{{baseUrl}}/project/v1/configurations/read", + "raw": "{{baseUrl}}/v1/configurations/read", "host": ["{{baseUrl}}"], "path": ["v1", "configurations", "read"] } @@ -2999,7 +3033,7 @@ } }, "url": { - "raw": "{{baseUrl}}/project/v1/users/programs?isAPrivateProgram=true&page=1&limit=100&search=&language=&getProjectsCount=true", + "raw": "{{baseUrl}}/v1/users/programs?isAPrivateProgram=true&page=1&limit=100&search=&language=&getProjectsCount=true", "host": ["{{baseUrl}}"], "path": ["v1", "users", "programs"], "query": [ @@ -3053,7 +3087,7 @@ } }, "url": { - "raw": "{{baseUrl}}/project/v1/users/solutions/66bb85bebf682a1f367c55b9", + "raw": "{{baseUrl}}/v1/users/solutions/66bb85bebf682a1f367c55b9", "host": ["{{baseUrl}}"], "path": ["v1", "users", "solutions", "66bb85bebf682a1f367c55b9"] } @@ -3077,7 +3111,7 @@ } ], "url": { - "raw": "{{baseUrl}}/project/v1/wishlist/add/66399e6304785762f7e8e4cb", + "raw": "{{baseUrl}}/v1/wishlist/add/66399e6304785762f7e8e4cb", "host": ["{{baseUrl}}"], "path": ["v1", "wishlist", "add", "66399e6304785762f7e8e4cb"] } @@ -3096,7 +3130,7 @@ } ], "url": { - "raw": "{{baseUrl}}/project/v1/wishlist/list?page=1&language=ka&limit=10", + "raw": "{{baseUrl}}/v1/wishlist/list?page=1&language=ka&limit=10", "host": ["{{baseUrl}}"], "path": ["v1", "wishlist", "list"], "query": [ @@ -3129,7 +3163,7 @@ } ], "url": { - "raw": "{{baseUrl}}/project/v1/wishlist/remove/66399e6304785762f7e8e4cb", + "raw": "{{baseUrl}}/v1/wishlist/remove/66399e6304785762f7e8e4cb", "host": ["{{baseUrl}}"], "path": ["v1", "wishlist", "remove", "66399e6304785762f7e8e4cb"] } @@ -3173,7 +3207,7 @@ } ], "url": { - "raw": "{{baseUrl}}/project/v1/projectAttributes/create", + "raw": "{{baseUrl}}/v1/projectAttributes/create", "host": ["{{baseUrl}}"], "path": ["v1", "projectAttributes", "create"] } @@ -3221,7 +3255,7 @@ } }, "url": { - "raw": "{{baseUrl}}/project/v1/projectAttributes/update?code=duration&language=hi", + "raw": "{{baseUrl}}/v1/projectAttributes/update?code=duration&language=hi", "host": ["{{baseUrl}}"], "path": ["v1", "projectAttributes", "update"], "query": [ @@ -3255,7 +3289,7 @@ } ], "url": { - "raw": "{{baseUrl}}/project/v1/projectAttributes/find?language=en", + "raw": "{{baseUrl}}/v1/projectAttributes/find?language=en", "host": ["{{baseUrl}}"], "path": ["v1", "projectAttributes", "find"], "query": [ @@ -3290,7 +3324,7 @@ } ], "url": { - "raw": "{{baseUrl}}/project/v1/template/list?language=ka&hasSpotlight=false&duration=1week&roles=DistrictsEducationOfficer", + "raw": "{{baseUrl}}/v1/template/list?language=ka&hasSpotlight=false&duration=1week&roles=DistrictsEducationOfficer", "host": ["{{baseUrl}}"], "path": ["v1", "template", "list"], "query": [ @@ -3365,7 +3399,7 @@ ] }, "url": { - "raw": "{{baseUrl}}/project/v1/userExtension/bulkUpload", + "raw": "{{baseUrl}}/v1/userExtension/bulkUpload", "host": ["{{baseUrl}}"], "path": ["v1", "userExtension", "bulkUpload"] } @@ -3393,7 +3427,7 @@ } }, "url": { - "raw": "{{baseUrl}}/project/v1/userExtension/update?tenantId&orgId&userId", + "raw": "{{baseUrl}}/v1/userExtension/update?tenantId&orgId&userId", "host": ["{{baseUrl}}"], "path": ["v1", "userExtension", "update"], "query": [ @@ -3425,7 +3459,7 @@ "method": "GET", "header": [], "url": { - "raw": "{{baseUrl}}/project/health", + "raw": "{{baseUrl}}/health", "host": ["{{baseUrl}}"], "path": ["health"] } diff --git a/api-doc/api-doc.yaml b/api-doc/api-doc.yaml index 079a3398..653c5237 100644 --- a/api-doc/api-doc.yaml +++ b/api-doc/api-doc.yaml @@ -420,8 +420,8 @@ paths: message: Missing file of type projectTemplates status: 400 description: >- - Enables bulk creation of project templates by uploading a CSV file - with multiple templates and a JSON file for multilingual data. + Enables bulk creation of project templates by uploading a CSV file with + multiple templates and a JSON file for multilingual data. * The API Endpoint for the bulk creation of project templates is `/v1/project/templates/bulkCreate` @@ -498,8 +498,8 @@ paths: message: Missing file of type projectTemplates status: 400 description: >- - Enables bulk updating of project templates through a CSV - file containing multiple records. + Enables bulk updating of project templates through a CSV file + containing multiple records. * The API Endpoint for the bulk updating of project templates is `/v1/project/templates/bulkUpdate` @@ -601,8 +601,8 @@ paths: status: 400 message: Project template not found description: >- - Updates a specific project template using its template - ID provided in the path parameters. + Updates a specific project template using its template ID provided in + the path parameters. * The API Endpoint for updating a specific project template is `/v1/project/templates/update` @@ -683,7 +683,8 @@ paths: status: 400 message: Project template not found description: >- - Fetches a list of project templates filtered by the provided external ID. + Fetches a list of project templates filtered by the provided external + ID. * The API Endpoint to retrieves a list of project templates is `/v1/project/templates/listByIds` @@ -769,8 +770,8 @@ paths: param: _id msg: required project template id description: >- - Creates a duplicate project template using the parent template’s external - ID and associated solution ID. + Creates a duplicate project template using the parent template’s + external ID and associated solution ID. * The API Endpoint will help you to create a duplicate projectTemplate is `/v1/project/templates/importProjectTemplate` @@ -781,7 +782,7 @@ paths: * This is a mandatory parameter and cannot be empty or null. /v1/project/templates/details/{templateId}: get: - tags: &ref_73 + tags: &ref_71 - Project Templates summary: Fetches details of a specific project template parameters: @@ -1168,7 +1169,7 @@ paths: projectId: '' '400': description: 'Bad response ' - headers: &ref_74 {} + headers: &ref_72 {} content: application/json: schema: @@ -1651,8 +1652,8 @@ paths: param: _id msg: required project template id description: >- - Enables bulk creation of tasks for a specific project template by uploading - a CSV file and providing the template ID. + Enables bulk creation of tasks for a specific project template by + uploading a CSV file and providing the template ID. * The API allows the user to bulk create tasks for a specified project template is `/v1/project/templateTasks/bulkCreate` @@ -1732,8 +1733,8 @@ paths: message: Project template not found status: 400 description: >- - Allows bulk updating of tasks within a project template using a CSV file - and the template ID. + Allows bulk updating of tasks within a project template using a CSV + file and the template ID. * The API allows the user to bulk update tasks for a specified project template is `/v1/project/templateTasks/bulkUpdate` @@ -3142,7 +3143,8 @@ paths: param: title msg: required project title description: >- - Creates a new user project with associated metadata, tasks, and categories. + Creates a new user project with associated metadata, tasks, and + categories. * The API allows for the creation of a new user project is `/v1/userProjects/add`. @@ -3233,7 +3235,7 @@ paths: * This is a mandatory parameter and cannot be empty or null. /v1/userProjects/tasksStatus/{_id}: post: - tags: &ref_39 + tags: &ref_38 - User Projects summary: Retrieve the status of specific tasks for a user project. requestBody: @@ -3251,7 +3253,7 @@ paths: value: taskIds: [] parameters: - - &ref_40 + - &ref_39 name: X-auth-token in: header schema: @@ -3261,7 +3263,7 @@ paths: To use this API, you require an X-auth-token. This token is available in the login API Response. required: true - - &ref_41 + - &ref_40 name: internal-access-token in: header schema: @@ -3292,7 +3294,7 @@ paths: submissionId: 5fbaa71d97ccef111cbb4ee0 '400': description: 'Bad response ' - headers: &ref_42 {} + headers: &ref_41 {} content: application/json: schema: @@ -5733,8 +5735,8 @@ paths: param: entityType msg: entityType required description: >- - Fetches solutions filtered by user role, entity type, and program, - with support for pagination and search. + Fetches solutions filtered by user role, entity type, and program, with + support for pagination and search. * The endpoint allows a user to retrieves solutions based on user role, entity type, and entities within a specified program is @@ -5994,7 +5996,8 @@ paths: param: _id msg: required solution link description: >- - Validates a solution link and provides details of the associated solution. + Validates a solution link and provides details of the associated + solution. * The endpoint allows a user to verifies the validity of a solution link and returns details about the targeted solution is `/v1/solutions/verifyLink`. @@ -6005,7 +6008,7 @@ paths: * This is a mandatory parameter and cannot be empty or null. /v1/solutions/verifySolution/{solutionId}: post: - tags: &ref_36 + tags: &ref_35 - Solutions summary: Verify solution by id. requestBody: @@ -6030,7 +6033,7 @@ paths: - 5fd1b52ab53a6416aaeefc80 - 5fd098e2e049735a86b748ac role: BEO - parameters: &ref_37 + parameters: &ref_36 - name: X-auth-token in: header schema: @@ -6069,7 +6072,7 @@ paths: _id: 5f6853f293734140ccce90cf '400': description: Bad response - headers: &ref_38 {} + headers: &ref_37 {} content: application/json: schema: @@ -6349,7 +6352,7 @@ paths: - BEO '400': description: Bad response - headers: &ref_35 {} + headers: &ref_34 {} content: application/json: schema: @@ -6750,7 +6753,8 @@ paths: description: >- This API retrieves a form based on the provided id. - * The endpoint allows a user to retrieves a form based on the provided id is `/v1/forms/read`. + * The endpoint allows a user to retrieves a form based on the provided + id is `/v1/forms/read`. * It is mandatory to provide values for parameters which are marked as `required` @@ -7176,8 +7180,8 @@ paths: param: name msg: Base template name required description: >- - Creates or updates a base certificate template using multipart - form data. Authentication is required. + Creates or updates a base certificate template using multipart form + data. Authentication is required. * The endpoint allows a user to creates certificate base template`/v1/certificateBaseTemplates/createOrUpdate`. @@ -7232,7 +7236,7 @@ paths: - in: path name: _id description: To update the data we need to match its id - schema: &ref_45 + schema: &ref_44 type: string required: true - in: header @@ -7550,7 +7554,7 @@ paths: - in: path name: categoryExternalId description: To list the data we need to match its id - schema: &ref_44 + schema: &ref_43 type: string required: true responses: @@ -8744,7 +8748,7 @@ paths: updatedAt: '2020-12-10T12:36:36.482Z' createdAt: '2020-12-10T12:36:36.482Z' __v: 0 - '400': &ref_34 + '400': &ref_47 description: 'Bad response ' headers: *ref_26 content: @@ -9498,7 +9502,7 @@ paths: externalId: b54a5c6d-98be-4313-af1c-33040b1703aa '400': description: Bad response - headers: *ref_35 + headers: *ref_34 content: application/json: schema: @@ -9526,7 +9530,7 @@ paths: * This is a mandatory parameter and cannot be empty or null. /v1/solutions/details/:{solutionId}: post: - tags: *ref_36 + tags: *ref_35 summary: To fetch solution details requestBody: content: @@ -9543,7 +9547,7 @@ paths: value: role: district_education_officer state: 66bc5afebb293e9d6930ad7a - parameters: *ref_37 + parameters: *ref_36 responses: '200': description: Successful response @@ -9610,7 +9614,7 @@ paths: description: Project 1 description '400': description: Bad response - headers: *ref_38 + headers: *ref_37 content: application/json: schema: @@ -9659,12 +9663,12 @@ paths: * This is a mandatory parameter and cannot be empty or null. /v1/userProjects/certificateReIssue/:{projectId}: post: - tags: *ref_39 + tags: *ref_38 summary: Reissue the certificate for a specified project. - requestBody: &ref_43 {} + requestBody: &ref_42 {} parameters: + - *ref_39 - *ref_40 - - *ref_41 - in: path name: projectId description: To fetch the data we need to match its id @@ -9685,7 +9689,7 @@ paths: _id: 66ac9949227504a96d8dce1c '400': description: 'Bad response ' - headers: *ref_42 + headers: *ref_41 content: application/json: schema: @@ -9727,12 +9731,12 @@ paths: * This is a mandatory parameter and cannot be empty or null. /v1/userProjects/verifyCertificate/:{projectId}: post: - tags: *ref_39 + tags: *ref_38 summary: Verifies the project certificate. - requestBody: *ref_43 + requestBody: *ref_42 parameters: + - *ref_39 - *ref_40 - - *ref_41 - in: path name: projectId description: '' @@ -9764,7 +9768,7 @@ paths: https://storage.googleapis.com/mentoring-dev-storage-private/project/25291645-6583-46ac-becb-35ce2ac1064c/1/d664feff-1d12-4877-be46-6fac41172852/template.svg?GoogleAccessId=sl-mentoring-dev-storage%40sl-dev-project.iam.gserviceaccount.com&Expires=1722956711&Signature=gUGRNgykUz3Jp%2FiNC82UkSkJhOXR3E8qY58UhkvCSGmJ%2FD34AdkR8HYropHfNeX191CwV%2F7%2FNR0bvgHubCg3a2v9qDelYK5tvAwZ%2FC9pUoaIyYTdmq6yqq9m%2FbdjC%2BmBERpLT8IDVfHl7nttw%2FiIhP%2BVj8NnejVbS%2FgFa7jOe7iFwNVjxXiBspfjnq1Dh9v%2FN9d6NibyTljQx%2FLDUpJ6DfwoAwmnytZUg94%2FwuwGpgCK0FbR78UlJGSanDtJvyZ%2FGh3pkV4ulr%2BLqLfSIMjLI3yjxBVNmkLv0f%2FXP3cH8ULvRT%2BzYtWXU0yRUK%2ByJaCgF%2BIzTCXtzXLZuCLhxxnOeA%3D%3D '400': description: 'Bad response ' - headers: *ref_42 + headers: *ref_41 content: application/json: schema: @@ -9826,19 +9830,19 @@ paths: - in: path name: _id description: To update the data we need to match its id - schema: *ref_44 + schema: *ref_43 required: true - in: header name: X-auth-token description: >- To use this API, you require an X-auth-token. This token is available in the login API Response. - schema: *ref_45 + schema: *ref_44 required: true - in: header name: internal-access-token description: '' - schema: *ref_45 + schema: *ref_44 required: true - in: header name: admin-auth-token @@ -10083,18 +10087,18 @@ paths: description: >- isAPrivateProgram is a boolean variable, which fetches different response on different value. - schema: &ref_47 + schema: &ref_46 type: boolean required: true - in: query name: page description: this query param suggests which page we need to fetch - schema: &ref_46 + schema: &ref_45 type: number - in: query name: limit description: limit suggests the number of entries in each page - schema: *ref_46 + schema: *ref_45 - in: query name: search description: >- @@ -10107,7 +10111,7 @@ paths: description: >- getProjectsCount is a boolean variable, which fetches different response on different value. - schema: *ref_47 + schema: *ref_46 - in: query name: language description: valid language code to be passed. @@ -10218,7 +10222,7 @@ paths: - Users summary: Fetch user solutions details. parameters: - - *ref_34 + - *ref_47 - in: path name: _id description: This is a program id. @@ -10795,7 +10799,8 @@ paths: requires authentication tokens in the headers. - * The API allows the user to create project attributes`v1/projectAttributes/create` + * The API allows the user to create project + attributes`v1/projectAttributes/create` * It is mandatory to provide values for parameters which are marked as `required` @@ -11052,7 +11057,8 @@ paths: params. - * The API allows the user to find project attributes `v1/projectAttributes/find` + * The API allows the user to find project attributes + `v1/projectAttributes/find` * It is mandatory to provide values for parameters which are marked as `required` @@ -11744,7 +11750,7 @@ paths: description: >- Need to pass Resource Type that we need to delete Ex: solution or program - schema: &ref_66 + schema: &ref_64 type: string required: true - in: header @@ -11754,27 +11760,27 @@ paths: authentication. This token is generated when a user logs in and must be included in each subsequent request to verify the user’s session and permissions. - schema: *ref_66 + schema: *ref_64 required: true - in: header name: internal-access-token description: 'internal-access-token ' - schema: *ref_66 + schema: *ref_64 required: true - in: header name: admin-auth-token description: admin-auth-token - schema: *ref_66 + schema: *ref_64 required: true - in: header name: tenantId description: tenantId - schema: *ref_66 + schema: *ref_64 required: true - in: header name: orgId description: orgId - schema: *ref_66 + schema: *ref_64 required: false responses: '200': @@ -11929,28 +11935,28 @@ paths: - in: header name: x-auth-token description: This is user token - schema: &ref_67 + schema: &ref_65 type: string required: true - in: header name: internal-access-token description: This is internal access token - schema: *ref_67 + schema: *ref_65 required: true - in: header name: admin-auth-token description: This is admin auth token - schema: *ref_67 + schema: *ref_65 required: true - in: header name: tenantId description: This is tenantId for which the resource needs to be created - schema: *ref_67 + schema: *ref_65 required: true - in: header name: orgId description: This is organization Id for which the resources needs to be created - schema: *ref_67 + schema: *ref_65 required: true /v1/organizationExtension/updateRelatedOrgs: post: @@ -12088,28 +12094,28 @@ paths: - in: header name: x-auth-token description: This is user token - schema: &ref_68 + schema: &ref_66 type: string required: true - in: header name: internal-access-token description: This is internal access token - schema: *ref_68 + schema: *ref_66 required: true - in: header name: admin-auth-token description: This is admin auth token - schema: *ref_68 + schema: *ref_66 required: true - in: header name: tenantId description: This is tenant Id used to create resources - schema: *ref_68 + schema: *ref_66 required: true - in: header name: orgId description: This is orgId used to create resources - schema: *ref_68 + schema: *ref_66 required: true /v1/userProjects/searchEntities/{solutionId}?search={searchText}: get: @@ -12228,9 +12234,9 @@ paths: * This is a mandatory parameter and cannot be empty or null. operationId: '' - tags: &ref_69 + tags: &ref_67 - User Projects - parameters: &ref_70 + parameters: &ref_68 - in: header name: X-auth-token description: >- @@ -12287,7 +12293,7 @@ paths: entityName: GOVT HIGH SCHOOL Gulbarga '400': description: Bad response - headers: &ref_71 {} + headers: &ref_69 {} content: application/json: schema: @@ -12333,7 +12339,7 @@ paths: sampleBodyData: value: entityId: 687a66c07f4bcd29a40c908d - parameters: &ref_72 + parameters: &ref_70 - name: _id in: path required: true @@ -12354,8 +12360,8 @@ paths: * This is a mandatory parameter and cannot be empty or null. operationId: '' - tags: *ref_69 - parameters: *ref_70 + tags: *ref_67 + parameters: *ref_68 responses: '200': description: Successful response @@ -12375,7 +12381,7 @@ paths: status: 200 '400': description: Bad response - headers: *ref_71 + headers: *ref_69 content: application/json: schema: @@ -12414,10 +12420,10 @@ paths: sampleBodyData: value: id: 1 - parameters: *ref_72 + parameters: *ref_70 /v1/project/templates/createChildProjectTemplate?isExternalProgram={true/false}&programExternalId={programExternalId}: get: - tags: *ref_73 + tags: *ref_71 summary: Create child project template parameters: - *ref_31 @@ -12457,7 +12463,7 @@ paths: "shikshalokam" } '400': description: 'Bad response ' - headers: *ref_74 + headers: *ref_72 content: application/json: schema: @@ -12538,5 +12544,108 @@ paths: schema: type: string description: Valid program external Id to be passed + /v1/userProjects/updateAcl/:_id: + post: + description: '' + operationId: '' + tags: + - User Projects + parameters: [] + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + message: + type: string + result: + type: object + properties: + _id: + type: string + acl: + type: object + properties: + visibility: + type: string + users: + type: array + items: + type: string + examples: + example1: + value: + success: true + message: Project updated successfully. + result: + _id: 68d38c9f69f139b91c9e57b8 + acl: + visibility: SPECIFIC + users: + - '1' + - '2' + - '3' + - '4' + '400': + description: '' + headers: {} + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + message: + type: string + examples: + example1: + value: + success: false + message: Project not found + requestBody: + content: + application/json: + schema: + type: object + properties: + acl: + type: object + properties: + visibility: + type: string + users: + type: array + items: + type: string + scope: + type: object + properties: {} + examples: + example1: + value: + acl: + visibility: SPECIFIC + users: + - '1' + scope: {} + parameters: + - in: path + name: _id + description: Required projectId + schema: + type: string + required: true + - in: header + name: x-auth-token + description: '' + schema: + type: string + required: true components: securitySchemes: {} diff --git a/constants/interface-routes/elevate-project/configs.json b/constants/interface-routes/elevate-project/configs.json index 29f9ea08..18b611e3 100644 --- a/constants/interface-routes/elevate-project/configs.json +++ b/constants/interface-routes/elevate-project/configs.json @@ -4115,34 +4115,6 @@ } ], "service": "project" - }, - { - "sourceRoute": "/project/v1/userProjects/updateAcl", - "type": "POST", - "priority": "MUST_HAVE", - "inSequence": false, - "orchestrated": false, - "targetPackages": [ - { - "basePackageName": "project", - "packageName": "elevate-project" - } - ], - "service": "project" - }, - { - "sourceRoute": "/project/v1/userProjects/updateAcl/:id", - "type": "POST", - "priority": "MUST_HAVE", - "inSequence": false, - "orchestrated": false, - "targetPackages": [ - { - "basePackageName": "project", - "packageName": "elevate-project" - } - ], - "service": "project" } ] } diff --git a/controllers/v1/userProjects.js b/controllers/v1/userProjects.js index b115d2ff..7953e95e 100644 --- a/controllers/v1/userProjects.js +++ b/controllers/v1/userProjects.js @@ -1567,6 +1567,34 @@ module.exports = class UserProjects extends Abstract { }) } + /** + * @api {post} /v1/userProjects/updateAcl/:_id={{projectId}} + * @apiVersion 1.0.0 + * @apiName Update ACL + * @apiHeader {String} X-auth-token Authenticity token + * @apiSampleRequest /v1/userProjects/updateAcl/68d38c9f69f139b91c9e57b8 + * @apiSampleRequest {json} Request + * { + "acl": { + "visibility" : "SPECIFIC", + "users" : ["1", "2", "3", "4"] + } + } + * @apiUse successBody + * @apiParamExample {json} Response: + { + "success" : true, + "message" : "Project updated successfully.", + "result" : { + "_id" : "68d38c9f69f139b91c9e57b8", + "acl" : { + "visibility" : "SPECIFIC", + "users" : ["1", "2", "3", "4"] + } + } + } + */ + /** * Update ACL for a project if user owns the project and submission level is ENTITY. * diff --git a/generics/constants/api-responses.js b/generics/constants/api-responses.js index 9ccae3d7..7ccca1f1 100644 --- a/generics/constants/api-responses.js +++ b/generics/constants/api-responses.js @@ -319,12 +319,13 @@ module.exports = { USER_EXTENSION_DELETED: 'User extension deleted successfully', NO_SOLUTION_FOUND_FOR_THE_LINK: 'This link appears to be invalid. Please use a valid link to continue.', ACCESS_TOKEN_EXPIRED_CODE: 'ACC_TOKEN_EXPIRED', - ACCESS_TOKEN_EXPIRED: 'Access Token Expired!! Please Login Again.', + ACCESS_TOKEN_EXPIRED: 'Access Token Expired! Please Login Again.', USER_SERVICE_DOWN_CODE: 'USER_SERVICE_DOWN', USER_SERVICE_DOWN: 'User service is down', INVALID_USER_PERMISSION: 'User do not have required permission ', INVALID_USER_PERMISSION_CODE: 'INVALID_USER_PERMISSION', PROJECT_DOES_NOT_BELONG_TO_USER: 'Project does not belong to the user!!', SUBMISSION_LEVEL_NOT_COMPLIED: 'Not allowed to update project. SUBMISSION_LEVEL not complied!!!', + SUBMISSION_LEVEL_NOT_COMPLIED: 'Not allowed to update project. SUBMISSION_LEVEL not complied!', PROJECT_UPDATED_SUCCESSFULLY: 'Project updated successfully.', } diff --git a/module/programs/validator/v1.js b/module/programs/validator/v1.js index dbcdb163..724d7d81 100644 --- a/module/programs/validator/v1.js +++ b/module/programs/validator/v1.js @@ -6,9 +6,9 @@ module.exports = (req) => { req.checkBody('requestForPIIConsent').exists().withMessage('required requestForPIIConsent value of program') req.checkBody('scope') .exists() - .withMessage('required solution scope') + .withMessage('required program scope') .notEmpty() - .withMessage('solution scope cannot be empty') + .withMessage('program scope cannot be empty') }, update: function () { req.checkParams('_id') diff --git a/module/solutions/helper.js b/module/solutions/helper.js index bd8357fb..7511df2a 100644 --- a/module/solutions/helper.js +++ b/module/solutions/helper.js @@ -731,10 +731,15 @@ module.exports = class SolutionsHelper { CONSTANTS.common.MANDATORY_SCOPE_FIELD, CONSTANTS.common.OPTIONAL_SCOPE_FIELD ) + + // If a prefix is provided, modify all query conditions to apply on nested fields. + // Example: if prefix = "acl", a condition like { "scope": {...} } + // becomes { "acl.scope": {...} }. + // This ensures the targeting rules apply inside a specific nested object. if (prefix != '') { builtQuery['$and'] = builtQuery['$and'].map((obj) => { - const key = Object.keys(obj)[0] - const value = obj[key] + const key = Object.keys(obj)[0] // Extract the field name in the condition + const value = obj[key] // Extract the condition value return { [`${prefix}.${key}`]: value } }) } @@ -2131,7 +2136,6 @@ module.exports = class SolutionsHelper { } matchQuery['$match']['tenantId'] = userDetails.userInformation.tenantId - matchQuery['$match']['orgId'] = userDetails.userInformation.organizationId if (currentOrgOnly) { let organizationId = userDetails.userInformation.organizationId @@ -2544,27 +2548,27 @@ module.exports = class SolutionsHelper { ) }) - if (process.env.SUBMISSION_LEVEL == 'ENTITY') { - mergedData = userCreatedProjects.data.data - totalCount = mergedData.length - if (mergedData.length > 0) { - let startIndex = pageSize * (pageNo - 1) - let endIndex = startIndex + pageSize - mergedData = mergedData.slice(startIndex, endIndex) - } - return resolve({ - success: true, - message: CONSTANTS.apiResponses.TARGETED_SOLUTIONS_FETCHED, - data: { - data: mergedData, - count: totalCount, - }, - result: { - data: mergedData, - count: totalCount, - }, - }) - } + // if (process.env.SUBMISSION_LEVEL == 'ENTITY') { + // mergedData = userCreatedProjects.data.data + // totalCount = mergedData.length + // if (mergedData.length > 0) { + // let startIndex = pageSize * (pageNo - 1) + // let endIndex = startIndex + pageSize + // mergedData = mergedData.slice(startIndex, endIndex) + // } + // return resolve({ + // success: true, + // message: CONSTANTS.apiResponses.TARGETED_SOLUTIONS_FETCHED, + // data: { + // data: mergedData, + // count: totalCount, + // }, + // result: { + // data: mergedData, + // count: totalCount, + // }, + // }) + // } // Add program data to the fetched projects if (userCreatedProjects.success && userCreatedProjects.data) { totalCount = userCreatedProjects.data.count @@ -3191,10 +3195,10 @@ module.exports = class SolutionsHelper { { 'acl.visibility': CONSTANTS.common.PROJECT_VISIBILITY_ALL }, { 'acl.visibility': CONSTANTS.common.PROJECT_VISIBILITY_SPECIFIC, - 'acl.users': { $in: [userId] }, + 'acl.users': { $in: [userId.toString()] }, }, { 'acl.visibility': CONSTANTS.common.PROJECT_VISIBILITY_SCOPE, ...matchQuery }, - { 'acl.visibility': CONSTANTS.common.PROJECT_VISIBILITY_SELF, userId: userId }, + { createdBy: userId }, ], } } else { diff --git a/module/userProjects/helper.js b/module/userProjects/helper.js index 461956fd..760588d5 100644 --- a/module/userProjects/helper.js +++ b/module/userProjects/helper.js @@ -152,7 +152,6 @@ module.exports = class UserProjectsHelper { message: CONSTANTS.apiResponses.USER_PROJECT_NOT_FOUND, } } - // if entityId & entityInformation are passed through payload, ignore them const blackListedPayloadItems = ['entityId', 'entityInformation', 'acl'] blackListedPayloadItems.map((payloadItem) => { @@ -182,6 +181,7 @@ module.exports = class UserProjectsHelper { // validate user authenticity if the acl.visibility of project is SCOPE else if (userProject[0].acl.visibility == CONSTANTS.common.PROJECT_VISIBILITY_SCOPE) { let scopeData = data.userProfileInformation.scope + scopeData['tenantId'] = tenantId let queryData = await solutionsHelper.queryBasedOnRoleAndLocation(scopeData, '', 'acl') if (!queryData.success) { return resolve(queryData) @@ -4519,22 +4519,30 @@ module.exports = class UserProjectsHelper { // Check if project exists and belongs to the user const projectData = await projectQueries.projectDocument({ _id: projectId, - userId, + createdBy: userId, tenantId, }) if (!projectData || projectData.length === 0) { throw { success: false, - message: CONSTANTS.apiResponses.PROJECT_DOES_NOT_BELONG_TO_USER, + message: CONSTANTS.apiResponses.PROJECT_NOT_FOUND, + status: HTTP_STATUS_CODE['bad_request'].status, + } + } + if ( + bodyData.acl.visibility === CONSTANTS.common.PROJECT_VISIBILITY_SPECIFIC && + bodyData.acl.users.length > 0 + ) { + if (!bodyData.acl.users.includes(userId.toString())) { + bodyData.acl.users.push(userId.toString()) } } - // Update ACL field in the project const updatedProject = await projectQueries.findOneAndUpdate( { _id: projectId, - userId, + createdBy: userId, tenantId, }, { diff --git a/module/userProjects/validator/v1.js b/module/userProjects/validator/v1.js index 780ffb75..65a54010 100644 --- a/module/userProjects/validator/v1.js +++ b/module/userProjects/validator/v1.js @@ -132,6 +132,36 @@ module.exports = (req) => { } req.checkQuery(existId).exists().withMessage('required solution or projectId Id') }, + updateAcl: function () { + // Validate path param: id + req.checkParams('_id') + .exists() + .withMessage('projectId is required in path params') + .isMongoId() + .withMessage('projectId must be a valid MongoDB ObjectId') + + // acl must be present + req.checkBody('acl').exists().withMessage('acl is required in body') + + // acl.visibility must be present and string + req.checkBody('acl.visibility') + .exists() + .withMessage('acl.visibility is required') + .isString() + .withMessage('acl.visibility must be a string') + + // if acl.users is present → must be an array + req.checkBody('acl.users') + .optional() + .custom((value) => Array.isArray(value)) + .withMessage('acl.users must be an array if provided') + + // if acl.scope is present → must be an object + req.checkBody('acl.scope') + .optional() + .custom((value) => typeof value === 'object' && !Array.isArray(value)) + .withMessage('acl.scope must be an object if provided') + }, } if (projectsValidator[req.params.method]) { From 6ac5dd4e4132021fb88b0410263e8fdd9320bb3c Mon Sep 17 00:00:00 2001 From: prajwal Date: Fri, 2 Jan 2026 13:04:16 +0530 Subject: [PATCH 8/8] unwanted code removed --- module/solutions/helper.js | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/module/solutions/helper.js b/module/solutions/helper.js index 7511df2a..f6806dd3 100644 --- a/module/solutions/helper.js +++ b/module/solutions/helper.js @@ -2548,27 +2548,6 @@ module.exports = class SolutionsHelper { ) }) - // if (process.env.SUBMISSION_LEVEL == 'ENTITY') { - // mergedData = userCreatedProjects.data.data - // totalCount = mergedData.length - // if (mergedData.length > 0) { - // let startIndex = pageSize * (pageNo - 1) - // let endIndex = startIndex + pageSize - // mergedData = mergedData.slice(startIndex, endIndex) - // } - // return resolve({ - // success: true, - // message: CONSTANTS.apiResponses.TARGETED_SOLUTIONS_FETCHED, - // data: { - // data: mergedData, - // count: totalCount, - // }, - // result: { - // data: mergedData, - // count: totalCount, - // }, - // }) - // } // Add program data to the fetched projects if (userCreatedProjects.success && userCreatedProjects.data) { totalCount = userCreatedProjects.data.count