From 67b9cd1eb477701bbe9572eac62b4bb2e8e2f39b Mon Sep 17 00:00:00 2001 From: VISHNUDAS-tunerlabse Date: Mon, 3 Oct 2022 21:47:47 +0530 Subject: [PATCH 01/92] certificate template mapped to solution and project --- config/globals.js | 1 + controllers/v1/certificateTemplates.js | 16 ++++++ databaseQueries/certificateTemplates.js | 60 ++++++++++++++++++++++ models/certificateTemplates.js | 14 ++++++ models/projects.js | 13 ++++- module/library/categories/helper.js | 4 +- module/project/templates/helper.js | 2 +- module/userProjects/helper.js | 67 +++++++++++++++++++------ 8 files changed, 159 insertions(+), 18 deletions(-) create mode 100644 controllers/v1/certificateTemplates.js create mode 100644 databaseQueries/certificateTemplates.js create mode 100644 models/certificateTemplates.js diff --git a/config/globals.js b/config/globals.js index ad63ab7a..c14c6428 100644 --- a/config/globals.js +++ b/config/globals.js @@ -76,6 +76,7 @@ module.exports = function () { global.schemas[name] = require(PROJECT_ROOT_DIRECTORY + '/models/' + file); } }); + // All controllers global.controllers = requireAll({ diff --git a/controllers/v1/certificateTemplates.js b/controllers/v1/certificateTemplates.js new file mode 100644 index 00000000..c57fa739 --- /dev/null +++ b/controllers/v1/certificateTemplates.js @@ -0,0 +1,16 @@ +/** + * name : certificateTemplates.js + * author : Vishnu + * created-date : 29-Sep-2022 + * Description : Certificate template related information. +*/ + +module.exports = class CertificateTemplates extends Abstract { + constructor() { + super("certificateTemplates"); + } + + static get name() { + return "certificateTemplates"; + } +} \ No newline at end of file diff --git a/databaseQueries/certificateTemplates.js b/databaseQueries/certificateTemplates.js new file mode 100644 index 00000000..fa2a2ddc --- /dev/null +++ b/databaseQueries/certificateTemplates.js @@ -0,0 +1,60 @@ +/** + * name : certificateTemplates.js + * author : Vishnu + * created-date : 03-Oct-2022 + * Description : Certificate template helper for DB interactions. + */ + +// Dependencies + +/** + * CertificateTemplates + * @class +*/ + +module.exports= class CertificateTemplates{ + /** + * certificate template details. + * @method + * @name certificateTemplateDocument + * @param {Array} [filterData = "all"] - certificate template filter query. + * @param {Array} [fieldsArray = "all"] - projected fields. + * @param {Array} [skipFields = "none"] - field not to include + * @returns {Array} certificateTemplates details. + */ + + static certificateTemplateDocument( + filterData = "all", + fieldsArray = "all", + skipFields = "none" + ) { + return new Promise(async (resolve, reject) => { + try { + let queryObject = (filterData != "all") ? filterData : {}; + let projection = {} + + if (fieldsArray != "all") { + fieldsArray.forEach(field => { + projection[field] = 1; + }); + } + + if( skipFields !== "none" ) { + skipFields.forEach(field=>{ + projection[field] = 0; + }); + } + let certificateTemplateDoc = + await database.models.certificateTemplates.find( + queryObject, + projection + ).lean(); + + return resolve(certificateTemplateDoc); + + } catch (error) { + return reject(error); + } + }); + } +} \ No newline at end of file diff --git a/models/certificateTemplates.js b/models/certificateTemplates.js new file mode 100644 index 00000000..d2f9f855 --- /dev/null +++ b/models/certificateTemplates.js @@ -0,0 +1,14 @@ +module.exports = { + name: "certificateTemplates", + schema: { + templateUrl: { + type : String, + index : true + }, + issuer: Object, + status: String, + solutionId: "ObjectId", + programId: "ObjectId", + criteria: Object + } +}; \ No newline at end of file diff --git a/models/projects.js b/models/projects.js index c9c2a1a6..64d95dc4 100644 --- a/models/projects.js +++ b/models/projects.js @@ -135,7 +135,18 @@ module.exports = { default : [] }, remarks : String, - userProfile : Object + userProfile : Object, + certificate : { + templateId : "ObjectId", + osid : String, + transactionId : String, + templateUrl : String, + status : String, + eligible : Boolean, + message : String, + issuedOn : Date, + criteria : Object + } }, compoundIndex: [ { diff --git a/module/library/categories/helper.js b/module/library/categories/helper.js index dd732aa4..f69ed22c 100644 --- a/module/library/categories/helper.js +++ b/module/library/categories/helper.js @@ -198,7 +198,7 @@ module.exports = class LibraryCategoriesHelper { message : CONSTANTS.apiResponses.PROJECT_NOT_FOUND, }; } - + projectsData[0].showProgramAndEntity = false; if( projectsData[0].tasks && projectsData[0].tasks.length > 0 ) { @@ -259,7 +259,7 @@ module.exports = class LibraryCategoriesHelper { data : projectsData[0] }); - } catch (error) { + } catch (error) { return resolve({ status : error.status ? error.status : HTTP_STATUS_CODE['internal_server_error'].status, success: false, diff --git a/module/project/templates/helper.js b/module/project/templates/helper.js index 8b07d62d..72d7d7b5 100644 --- a/module/project/templates/helper.js +++ b/module/project/templates/helper.js @@ -557,7 +557,7 @@ module.exports = class ProjectTemplatesHelper { externalId : templateId, isReusable : true }); - + if ( !projectTemplateData.length > 0 ) { throw new Error(CONSTANTS.apiResponses.PROJECT_TEMPLATE_NOT_FOUND) } diff --git a/module/userProjects/helper.js b/module/userProjects/helper.js index 383daaee..74b7a588 100644 --- a/module/userProjects/helper.js +++ b/module/userProjects/helper.js @@ -23,6 +23,7 @@ const removeFieldsFromRequest = ["submissionDetails"]; const programsQueries = require(DB_QUERY_BASE_PATH + "/programs"); const userProfileService = require(GENERICS_FILES_PATH + "/services/users"); const solutionsHelper = require(MODULES_BASE_PATH + "/solutions/helper"); +const certificateTemplateQueries = require(DB_QUERY_BASE_PATH + "/certificateTemplates"); /** * UserProjectsHelper @@ -454,9 +455,9 @@ module.exports = class UserProjectsHelper { result.solutionInformation = _.pick( solutionAndProgramCreation.data.solution, - ["name", "externalId", "description", "_id", "entityType"] + ["name", "externalId", "description", "_id", "entityType", "certificateTemplateId"] ); - + result.solutionInformation._id = ObjectId(result.solutionInformation._id); @@ -1157,7 +1158,24 @@ module.exports = class UserProjectsHelper { if( appVersion !== "" ) { projectCreation.data["appInformation"]["appVersion"] = appVersion; } - + + if ( solutionDetails.certificateTemplateId && solutionDetails.certificateTemplateId !== "" ) { + // <- Add certificate template details to projectCreation data if present -> + const certificateTemplateDetails = await certificateTemplateQueries.certificateTemplateDocument({ + _id : solutionDetails.certificateTemplateId + }); + + // create certificate object and add data if certificate template is present. + if ( certificateTemplateDetails.length > 0 ) { + projectCreation.data["certificate"] = { + templateId : certificateTemplateDetails[0]._id, + templateUrl : certificateTemplateDetails[0].templateUrl, + status : certificateTemplateDetails[0].status ? certificateTemplateDetails[0].status : "", + criteria : certificateTemplateDetails[0].criteria ? certificateTemplateDetails[0].criteria : "", + } + } + } + let getUserProfileFromObservation = false; if( bodyData && Object.keys(bodyData).length > 0 ) { @@ -2061,7 +2079,6 @@ module.exports = class UserProjectsHelper { isATargetedSolution ); - if ( libraryProjects.data && !Object.keys(libraryProjects.data).length > 0 @@ -2173,7 +2190,29 @@ module.exports = class UserProjectsHelper { programAndSolutionInformation.data ) } - + // <- Add certificate template data + if ( + libraryProjects.data.solutionInformation && + libraryProjects.data.solutionInformation.certificateTemplateId && + libraryProjects.data.solutionInformation.certificateTemplateId !== "" + ){ + // <- Add certificate template details to projectCreation data if present -> + const certificateTemplateDetails = await certificateTemplateQueries.certificateTemplateDocument({ + _id : libraryProjects.data.solutionInformation.certificateTemplateId + }); + + // create certificate object and add data if certificate template is present. + if ( certificateTemplateDetails.length > 0 ) { + libraryProjects.data["certificate"] = { + templateId : certificateTemplateDetails[0]._id, + templateUrl : certificateTemplateDetails[0].templateUrl, + status : certificateTemplateDetails[0].status ? certificateTemplateDetails[0].status : "", + criteria : certificateTemplateDetails[0].criteria ? certificateTemplateDetails[0].criteria : "", + } + } + delete libraryProjects.data.solutionInformation.certificateTemplateId; + } + //Fetch user profile information by calling sunbird's user read api. let addReportInfoToSolution = false; let userProfile = await userProfileService.profile(userToken, userId); @@ -2199,11 +2238,11 @@ module.exports = class UserProjectsHelper { libraryProjects.data.projectTemplateId = libraryProjects.data._id; libraryProjects.data.projectTemplateExternalId = libraryProjects.data.externalId; - + let projectCreation = await database.models.projects.create( _.omit(libraryProjects.data, ["_id"]) ); - + if ( addReportInfoToSolution && projectCreation._doc.solutionId ) { let updateSolution = await solutionsHelper.addReportInformationInSolution( @@ -2221,9 +2260,9 @@ module.exports = class UserProjectsHelper { userToken ); } - + projectCreation = await _projectInformation(projectCreation._doc); - + return resolve({ success: true, message: CONSTANTS.apiResponses.PROJECTS_FETCHED, @@ -2294,7 +2333,7 @@ function _projectInformation(project) { return new Promise(async (resolve, reject) => { try { - + if (project.entityInformation) { project.entityId = project.entityInformation._id; project.entityName = project.entityInformation.name; @@ -2304,7 +2343,7 @@ function _projectInformation(project) { project.programId = project.programInformation._id; project.programName = project.programInformation.name; } - + //project attachments if ( project.attachments && project.attachments.length > 0 ) { @@ -2331,7 +2370,7 @@ function _projectInformation(project) { } } - + //task attachments if (project.tasks && project.tasks.length > 0) { //order task based on task sequence @@ -2378,7 +2417,7 @@ function _projectInformation(project) { project.tasks = taskAttachmentsUrl.data; } } - + project.status = project.status ? project.status : CONSTANTS.common.NOT_STARTED_STATUS; @@ -2393,7 +2432,7 @@ function _projectInformation(project) { delete project.entityInformation; delete project.solutionInformation; delete project.programInformation; - + return resolve({ success: true, data: project From e6fea80bccdad4c98fbd6f857483fc5c6c73389f Mon Sep 17 00:00:00 2001 From: VISHNUDAS-tunerlabse Date: Thu, 6 Oct 2022 11:54:14 +0530 Subject: [PATCH 02/92] certificate template schema updated --- models/certificateTemplates.js | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/models/certificateTemplates.js b/models/certificateTemplates.js index d2f9f855..b9a1541e 100644 --- a/models/certificateTemplates.js +++ b/models/certificateTemplates.js @@ -1,14 +1,14 @@ module.exports = { - name: "certificateTemplates", - schema: { - templateUrl: { - type : String, - index : true - }, - issuer: Object, - status: String, - solutionId: "ObjectId", - programId: "ObjectId", - criteria: Object - } + name: "certificateTemplates", + schema: { + templateUrl: String, + issuer: Object, + status: String, + solutionId: { + type : "ObjectId", + index : true + }, + programId: "ObjectId", + criteria: Object + } }; \ No newline at end of file From 127cd21fe8219997b2fb100ac56c99b959d6058d Mon Sep 17 00:00:00 2001 From: VISHNUDAS-tunerlabse Date: Thu, 6 Oct 2022 13:46:06 +0530 Subject: [PATCH 03/92] certificate template model changes --- models/certificateTemplates.js | 10 ++++++++-- module/userProjects/helper.js | 8 ++++---- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/models/certificateTemplates.js b/models/certificateTemplates.js index b9a1541e..40b55e56 100644 --- a/models/certificateTemplates.js +++ b/models/certificateTemplates.js @@ -3,12 +3,18 @@ module.exports = { schema: { templateUrl: String, issuer: Object, - status: String, + status: { + type : String, + required : true + }, solutionId: { type : "ObjectId", index : true }, programId: "ObjectId", - criteria: Object + criteria: { + type : Object, + required : true + } } }; \ No newline at end of file diff --git a/module/userProjects/helper.js b/module/userProjects/helper.js index 74b7a588..54c86770 100644 --- a/module/userProjects/helper.js +++ b/module/userProjects/helper.js @@ -1170,8 +1170,8 @@ module.exports = class UserProjectsHelper { projectCreation.data["certificate"] = { templateId : certificateTemplateDetails[0]._id, templateUrl : certificateTemplateDetails[0].templateUrl, - status : certificateTemplateDetails[0].status ? certificateTemplateDetails[0].status : "", - criteria : certificateTemplateDetails[0].criteria ? certificateTemplateDetails[0].criteria : "", + status : certificateTemplateDetails[0].status, + criteria : certificateTemplateDetails[0].criteria, } } } @@ -2206,8 +2206,8 @@ module.exports = class UserProjectsHelper { libraryProjects.data["certificate"] = { templateId : certificateTemplateDetails[0]._id, templateUrl : certificateTemplateDetails[0].templateUrl, - status : certificateTemplateDetails[0].status ? certificateTemplateDetails[0].status : "", - criteria : certificateTemplateDetails[0].criteria ? certificateTemplateDetails[0].criteria : "", + status : certificateTemplateDetails[0].status, + criteria : certificateTemplateDetails[0].criteria, } } delete libraryProjects.data.solutionInformation.certificateTemplateId; From c53a23da9e7f0974a22f7f3f007391cd192c6311 Mon Sep 17 00:00:00 2001 From: VISHNUDAS-tunerlabse Date: Fri, 7 Oct 2022 09:27:34 +0530 Subject: [PATCH 04/92] templateUrl property change --- models/certificateTemplates.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/models/certificateTemplates.js b/models/certificateTemplates.js index 40b55e56..8f8dfaf3 100644 --- a/models/certificateTemplates.js +++ b/models/certificateTemplates.js @@ -1,7 +1,10 @@ module.exports = { name: "certificateTemplates", schema: { - templateUrl: String, + templateUrl: { + type : String, + required : true + }, issuer: Object, status: { type : String, From 67e9afcc537454556c09c92e937f65a2658066f3 Mon Sep 17 00:00:00 2001 From: VISHNUDAS-tunerlabse Date: Fri, 7 Oct 2022 13:47:14 +0530 Subject: [PATCH 05/92] made templateUrl required false --- models/certificateTemplates.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/models/certificateTemplates.js b/models/certificateTemplates.js index 8f8dfaf3..40b55e56 100644 --- a/models/certificateTemplates.js +++ b/models/certificateTemplates.js @@ -1,10 +1,7 @@ module.exports = { name: "certificateTemplates", schema: { - templateUrl: { - type : String, - required : true - }, + templateUrl: String, issuer: Object, status: { type : String, From ac02cad3bbe8f69ef83a44230337565397789d13 Mon Sep 17 00:00:00 2001 From: VISHNUDAS-tunerlabse Date: Thu, 13 Oct 2022 09:14:35 +0530 Subject: [PATCH 06/92] kafka consumer added to existing topic --- config/kafka.js | 14 +++++ .../kafka/consumers/certificateSubmissions.js | 61 +++++++++++++++++++ 2 files changed, 75 insertions(+) create mode 100644 generics/kafka/consumers/certificateSubmissions.js diff --git a/config/kafka.js b/config/kafka.js index 3d248170..23cdfbc8 100644 --- a/config/kafka.js +++ b/config/kafka.js @@ -9,6 +9,7 @@ //dependencies const kafka = require('kafka-node'); const SUBMISSION_TOPIC = process.env.SUBMISSION_TOPIC; +const CERTIFICATE_TOPIC = process.env.PROJECT_SUBMISSION_TOPIC; /** * Kafka configurations. @@ -44,6 +45,12 @@ const connect = function() { process.env.KAFKA_URL ); + // project certificate details consumer + _sendToKafkaConsumers( + CERTIFICATE_TOPIC, + process.env.KAFKA_URL + ); + return { kafkaProducer: producer, kafkaClient: client @@ -82,6 +89,10 @@ var _sendToKafkaConsumers = function (topic,host) { if (message && message.topic === SUBMISSION_TOPIC) { submissionsConsumer.messageReceived(message); } + // call certificateSubmissionsConsumer + if (message && message.topic === CERTIFICATE_TOPIC) { + certificateSubmissionsConsumer.messageReceived(message); + } }); @@ -90,6 +101,9 @@ var _sendToKafkaConsumers = function (topic,host) { if(error.topics && error.topics[0] === SUBMISSION_TOPIC) { submissionsConsumer.errorTriggered(error); } + if(error.topics && error.topics[0] === CERTIFICATE_TOPIC) { + certificateSubmissionsConsumer.errorTriggered(error); + } }); diff --git a/generics/kafka/consumers/certificateSubmissions.js b/generics/kafka/consumers/certificateSubmissions.js new file mode 100644 index 00000000..bca98ab2 --- /dev/null +++ b/generics/kafka/consumers/certificateSubmissions.js @@ -0,0 +1,61 @@ +/** + * name : certificateSubmissions.js + * author : Vishnu + * created-date : 10-Oct-2022 + * Description : Project certificates submission consumer. +*/ + +//dependencies +const userProjectsHelper = require(MODULES_BASE_PATH + "/userProjects/helper"); + +/** + * submission consumer message received. + * @function + * @name messageReceived + * @param {String} message - consumer data + * @returns {Promise} return a Promise. +*/ + +var messageReceived = function (message) { + + return new Promise(async function (resolve, reject) { + + try { + // This consumer is consuming from an old topic : PROJECT_CERTIFICATE_TOPIC, which is no more used by data team. ie) using existig topic instead of creating new one. + let parsedMessage = JSON.parse(message.value); + if ( parsedMessage.status == CONSTANTS.common.SUBMITTED_STATUS && parsedMessage.certificate ) { + await userProjectsHelper.generateCertificate(parsedMessage); + } + return resolve("Message Received"); + } catch (error) { + return reject(error); + } + + }); +}; + +/** + * If message is not received. + * @function + * @name errorTriggered + * @param {Object} error - error object + * @returns {Promise} return a Promise. +*/ + +var errorTriggered = function (error) { + + return new Promise(function (resolve, reject) { + + try { + return resolve(error); + } catch (error) { + return reject(error); + } + + }); +}; + +module.exports = { + messageReceived: messageReceived, + errorTriggered: errorTriggered +}; From f981d216d3d86b8d7bf181c19146c8025722870d Mon Sep 17 00:00:00 2001 From: VISHNUDAS-tunerlabse Date: Thu, 13 Oct 2022 13:47:33 +0530 Subject: [PATCH 07/92] generics, model and validation --- generics/constants/api-responses.js | 4 +++- generics/constants/common.js | 4 +++- generics/constants/endpoints.js | 4 +++- generics/helpers/utils.js | 15 ++++++++++++++- models/projects.js | 5 ++++- module/userProjects/validator/v1.js | 3 +++ 6 files changed, 30 insertions(+), 5 deletions(-) diff --git a/generics/constants/api-responses.js b/generics/constants/api-responses.js index 9b85a71f..bd0c1d6a 100644 --- a/generics/constants/api-responses.js +++ b/generics/constants/api-responses.js @@ -124,5 +124,7 @@ module.exports = { "TEMPLATE_ID_OR_LINK_REQUIRED" : "TemplateId or Link either one is required", "TEMPLATE_ID_NOT_FOUND_IN_SOLUTION" : "Could not found templateId in solution", "FAILED_TO_SYNC_PROJECT_ALREADY_SUBMITTED" : "Failed to sync, Project is already Submitted", - "SOLUTION_ID_AND_USERPROFILE_REQUIRED": "Required solution Id and userProfile" + "SOLUTION_ID_AND_USERPROFILE_REQUIRED": "Required solution Id and userProfile", + "PROJECT_WITH_CERTIFICATE_NOT_FOUND": "No certification project found for user", + "PROJECT_CERTIFICATE_GENERATED" : "Successfully generated project certificate" }; diff --git a/generics/constants/common.js b/generics/constants/common.js index dd75260a..ecc4ba61 100644 --- a/generics/constants/common.js +++ b/generics/constants/common.js @@ -50,5 +50,7 @@ module.exports = { "DISTRICT": "district", "SERVER_TIME_OUT" : 5000, "OK" : "OK", - "PROJECT" : "project" + "PROJECT" : "project", + "PROJECT_CERTIFICATE_RECIPIENT_TYPE" : "user", + "PROJECT_CERTIFICATE_GENERATED" : "Certificate generated successfully" }; diff --git a/generics/constants/endpoints.js b/generics/constants/endpoints.js index 11c43bb4..5cde94a3 100644 --- a/generics/constants/endpoints.js +++ b/generics/constants/endpoints.js @@ -47,5 +47,7 @@ module.exports = { FILES_DOWNLOADABLE_URL: "/v1/cloud-services/files/getDownloadableUrl", OBSERVATION_DETAILS : "/v1/observations/details", USER_READ_V5 : "/v5/user/read", - GET_LOCATION_DATA : "/v1/location/search" + GET_LOCATION_DATA : "/v1/location/search", + CERTIFICATE_CREATE : "/api/v1/ProjectCertificate", + CERTIFICATE_API_CALLBACK : "/api/userProject/mlproject/v1/certificateCallback" }; diff --git a/generics/helpers/utils.js b/generics/helpers/utils.js index 58d92e77..b4b1f62c 100644 --- a/generics/helpers/utils.js +++ b/generics/helpers/utils.js @@ -264,6 +264,18 @@ function checkValidUUID(uuids) { } return validateUUID; } + +/** + * convert string to upperCase. + * @function + * @name lowerCase + * @param {String} str + * @returns {String} returns a lowercase string. ex:hello , o/p: HELLO +*/ + +function upperCase(str) { + return str.toUpperCase() +} module.exports = { camelCaseToTitleCase : camelCaseToTitleCase, lowerCase : lowerCase, @@ -277,5 +289,6 @@ module.exports = { convertProjectStatus : convertProjectStatus, revertProjectStatus:revertProjectStatus, revertStatusorNot:revertStatusorNot, - checkValidUUID : checkValidUUID + checkValidUUID : checkValidUUID, + upperCase : upperCase }; diff --git a/models/projects.js b/models/projects.js index 64d95dc4..738ff6bd 100644 --- a/models/projects.js +++ b/models/projects.js @@ -139,7 +139,10 @@ module.exports = { certificate : { templateId : "ObjectId", osid : String, - transactionId : String, + transactionId : { + type : String, + index : true + }, templateUrl : String, status : String, eligible : Boolean, diff --git a/module/userProjects/validator/v1.js b/module/userProjects/validator/v1.js index 81c87ce9..a0282a23 100644 --- a/module/userProjects/validator/v1.js +++ b/module/userProjects/validator/v1.js @@ -25,6 +25,9 @@ module.exports = (req) => { }, share : function () { req.checkParams('_id').exists().withMessage("required project id"); + }, + certificateReIssue : function () { + req.checkParams('_id').exists().withMessage("required project id"); } } From 19c3ac42c24f15da58174a082a0c0505734cb6d8 Mon Sep 17 00:00:00 2001 From: VISHNUDAS-tunerlabse Date: Thu, 13 Oct 2022 15:58:45 +0530 Subject: [PATCH 08/92] new apis added (pls ignore comments and consoles- not final PR) --- controllers/v1/userProjects.js | 176 +++++++++++++++++ module/userProjects/helper.js | 335 +++++++++++++++++++++++++++++++-- 2 files changed, 499 insertions(+), 12 deletions(-) diff --git a/controllers/v1/userProjects.js b/controllers/v1/userProjects.js index e8019d26..f9c2ece1 100644 --- a/controllers/v1/userProjects.js +++ b/controllers/v1/userProjects.js @@ -957,4 +957,180 @@ module.exports = class UserProjects extends Abstract { }) } + /** + * @api {post} /improvement-project/api/v1/userProjects/certificateCallback + * Project certificate callback + * @apiVersion 1.0.0 + * @apiGroup User Projects + * @apiSampleRequest /improvement-project/api/v1/userProjects/certificateCallback + * @apiParamExample {json} Request + * { + "event": "sunbird-rc-create", + "timestamp": 1660145509358, + "data": { + "userId": "anonymous", + "entityType": "ProjectCertificate", + "osid": "ce2244a4-1a17-49a0-a3f9-c151161e70bl", + "transactionId": "1-3a4892d8-2221-4e96-9434-f4b37886126b", + "status": "SUCCESSFUL", + "message": "" + }, + "webhookUrl": "http://ml-project-service:3000/v1/userProjects/certificateCallback" + } + * @apiParamExample {json} Response: + /**{ + "message": "Successfully generated project certificate", + "status": 200, + "result": { + "_id": "63446059eeffea2b819f036e" + } + } + /** + + /** + * Project certificate callback. + * @method + * @name certificateCallback + * @param {Object} req - request data. + * @returns {JSON} certificate details. + */ + + async certificateCallback(req) { + return new Promise(async (resolve, reject) => { + try { + let callback = req.body + if ( callback.data && + callback.data.transactionId && + callback.data.transactionId !== "" && + callback.data.osid && + callback.data.osid !== "" + ) { + let certificateDetails = await userProjectsHelper.certificateCallback( callback.data.transactionId, callback.data.osid ); + return resolve({ + message: certificateDetails.message, + result: certificateDetails.data + }); + } + } 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 + }); + } + }) + } + + /** + * @api {get} /improvement-project/api/v1/userProjects/certificates + * List of user project with certificate + * @apiVersion 1.0.0 + * @apiGroup User Projects + * @apiSampleRequest /improvement-project/api/v1/userProjects/certificates + * @apiParamExample {json} Response: + * { + "message": "User project fetched successfully", + "status": 200, + "result": { + "data": [{ + "_id": "60793b80bd49095a19ddeae1", + "title": "Project with learning resources", + "certificate": { + "osid": "1-21c8ecab-7b8d-40f1-9961-cae7fcb6a5f9", + "status": "active", + "templateId": "600acc42c7de076e6f995147", + "templateUrl": "certificateTemplates/6343bd978f9d8980b7841e85/ba9aa220-ff1b-4717-b6ea-ace55f04fc16_2022-9-10-1665383945769.svg", + "issuedOn": "2020-12-03 13:22:31.988Z" + }, + "status": "submitted" + }, + { + "_id": "6011136a2d25b926974d9ec9", + "title": "Keep Our Schools Alive! (Petition)", + "status": "submitted", + "certificate": { + "eligible": false, + "templateId": "600acc42c7de076e6f995147", + "message": "Not submitted the project the project within program end date" + } + } + ], + "count": 2, + "certificateCount": 1 + } + } + /** + + /** + * List user project details with certificate + * @method + * @name certificates + * @returns {JSON} User project detaills with certificate + */ + + async certificates(req) { + return new Promise(async (resolve, reject) => { + try { + // fetch projects data of user, whish has certificate on completion + let projectDetails = await userProjectsHelper.certificates( req.userDetails.userInformation.userId ); + return resolve({ + message: projectDetails.message, + result: projectDetails.data + }); + + } 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 + }); + } + }) + } + + /** + * @api {post} /improvement-project/api/v1/userProjects/certificateReIssue + * ReIssue project certificate (admin api) + * @apiVersion 1.0.0 + * @apiGroup User Projects + * @apiSampleRequest /improvement-project/api/v1/userProjects/certificateReIssue + * @apiParamExample {json} Response: + /**{ + "message": "Successfully generated project certificate", + "status": 200, + "result": { + "_id": "63446059eeffea2b819f036e" + } + } + /** + * ReIssue project certificate + * @method + * @name certificateReIssue + * @returns {JSON} Reissued details + */ + + async certificateReIssue(req) { + return new Promise(async (resolve, reject) => { + try { + // ReIssue certificate of given project : projectId is passed as param + let projectDetails = await userProjectsHelper.certificateReIssue( + req.params._id, + req.userDetails.userToken, + req.query.recipientName ? req.query.recipientName : "" + ); + return resolve({ + message: projectDetails.message, + result: projectDetails.data + }); + + } 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 + }); + } + }) + } + }; \ No newline at end of file diff --git a/module/userProjects/helper.js b/module/userProjects/helper.js index 54c86770..6b400025 100644 --- a/module/userProjects/helper.js +++ b/module/userProjects/helper.js @@ -24,6 +24,7 @@ const programsQueries = require(DB_QUERY_BASE_PATH + "/programs"); const userProfileService = require(GENERICS_FILES_PATH + "/services/users"); const solutionsHelper = require(MODULES_BASE_PATH + "/solutions/helper"); const certificateTemplateQueries = require(DB_QUERY_BASE_PATH + "/certificateTemplates"); +const certificateService = require(GENERICS_FILES_PATH + "/services/certificate"); /** * UserProjectsHelper @@ -118,7 +119,6 @@ module.exports = class UserProjectsHelper { "appInformation", "status" ]); - if (!userProject.length > 0) { throw { @@ -353,7 +353,7 @@ module.exports = class UserProjectsHelper { if ( data.status == CONSTANTS.common.COMPLETED_STATUS || data.status == CONSTANTS.common.SUBMITTED_STATUS ) { updateProject.completedDate = new Date(); } - + let projectUpdated = await projectQueries.findOneAndUpdate( { @@ -372,9 +372,8 @@ module.exports = class UserProjectsHelper { status: HTTP_STATUS_CODE['bad_request'].status } } - await kafkaProducersHelper.pushProjectToKafka(projectUpdated); - + return resolve({ success: true, message: CONSTANTS.apiResponses.USER_PROJECT_UPDATED, @@ -1311,6 +1310,21 @@ module.exports = class UserProjectsHelper { } else { projectDetails.data.status = UTILS.convertProjectStatus(projectDetails.data.status); } + // make templateUrl downloadable befor passing to front-end + if ( projectDetails.data.certificate && + projectDetails.data.certificate.templateUrl && + projectDetails.data.certificate.templateUrl !== "" + ) { + let certificateTemplateDownloadableUrl = + await coreService.getDownloadableUrl( + { + filePaths: [projectDetails.data.certificate.templateUrl] + } + ); + if ( certificateTemplateDownloadableUrl.success ) { + projectDetails.data.certificate.templateUrl = certificateTemplateDownloadableUrl.data.url; + } + } return resolve({ success: true, @@ -2279,15 +2293,15 @@ module.exports = class UserProjectsHelper { }) } - /** - * get project details. - * @method - * @name userProject - * @param {String} projectId - project id. - * @returns {Object} Project details. - */ + /** + * get project details. + * @method + * @name userProject + * @param {String} projectId - project id. + * @returns {Object} Project details. + */ - static userProject(projectId) { + static userProject(projectId) { return new Promise(async (resolve, reject) => { try { @@ -2319,6 +2333,303 @@ module.exports = class UserProjectsHelper { }) } + /** + * generate project certificate. + * @method + * @name generateCertificate + * @param {Object} data - certificate creation data. + * @returns {JSON} certificate details. + */ + + static generateCertificate(data) { + return new Promise(async (resolve, reject) => { + try { + // logic to check criteria eligibility to be added + // :Check criteria is sattisfied + // if criteria check passes then call sunbird-RC certificate api + let certificateTemplateDetails = []; + // get downloadable url for certificate template + if ( data.certificate && data.certificate.templateUrl && data.certificate.templateUrl !== "" ) { + let certificateTemplateDownloadableUrl = + await coreService.getDownloadableUrl( + { + filePaths: [data.certificate.templateUrl] + } + ); + if ( certificateTemplateDownloadableUrl.success ) { + data.certificate.templateUrl = certificateTemplateDownloadableUrl.data.url; + } else { + return resolve({ + success:false + }); + } + } + if ( data.certificate && data.certificate.templateId && data.certificate.templateId !== "" ) { + certificateTemplateDetails = await certificateTemplateQueries.certificateTemplateDocument({ + _id : data.certificate.templateId + },["issuer","solutionId","programId"]); + + //certificate template data do not exists. + if ( !certificateTemplateDetails.length > 0 ) { + return resolve({ + success:false + }); + } + } + + //create certificate request body + let certificateData = { + recipient : { + id : data.userId, + name : data.userProfile.userName, + type : CONSTANTS.common.PROJECT_CERTIFICATE_RECIPIENT_TYPE + }, + templateUrl : data.certificate.templateUrl, + issuer : certificateTemplateDetails[0].issuer, + status : UTILS.upperCase(data.certificate.status), + projectId : data._id, + projectName : data.title, + programId : certificateTemplateDetails[0].programId, + programName : ( data.programInformation && data.programInformation.name ) ? data.programInformation.name : "", + solutionId : certificateTemplateDetails[0].solutionId, + solutionName : ( data.solutionInformation && data.solutionInformation.name ) ? data.solutionInformation.name : "", + completedDate : data.completedDate + }; + + const certificateDetails = await certificateService.createCertificate( certificateData ); + + let updateObject = { + "$set" : {} + }; + + // if transaction id is present. + if ( certificateDetails.success && + certificateDetails.data && + certificateDetails.data.ProjectCertificate && + certificateDetails.data.ProjectCertificate.transactionId && + certificateDetails.data.ProjectCertificate.transactionId !== "" + ) { + updateObject["$set"]["certificate.transactionId"] = certificateDetails.data.ProjectCertificate.transactionId; + } + + if ( certificateDetails.success && + certificateDetails.data && + certificateDetails.data.ProjectCertificate && + certificateDetails.data.ProjectCertificate.osid && + certificateDetails.data.ProjectCertificate.osid !== "" + ) { + updateObject["$set"]["certificate.osid"] = certificateDetails.data.ProjectCertificate.osid; + } + let projectDetails = await projectQueries.findOneAndUpdate( + { + _id: data._id + }, + updateObject + ); + + return resolve( { + success: true + }); + + } catch (error) { + return resolve({ + success: false, + message: error.message, + data: {} + }); + } + }) + } + + /** + * certificate callback + * @method + * @name certificateCallback + * @param {String} transactionId - transactionId for create certificate. + * @param {String} osid - osid for created certificate. + * @returns {JSON} certificate data updation details. + */ + + static certificateCallback(transactionId, osid) { + return new Promise(async (resolve, reject) => { + try { + let updateObject = { + "$set" : {} + }; + + // update osid and eligibility based on transactionId + updateObject["$set"]["certificate.osid"] = osid; + updateObject["$set"]["certificate.eligible"] = true; + updateObject["$set"]["certificate.message"] = CONSTANTS.common.PROJECT_CERTIFICATE_GENERATED; + updateObject["$set"]["certificate.issuedOn"] = new Date(); + + let projectDetails = await projectQueries.findOneAndUpdate( + { + "certificate.transactionId" : transactionId + }, + updateObject, + { + new: true + } + ); + + if ( projectDetails == null || !Object.keys(projectDetails).length > 0 ) { + throw { + status: HTTP_STATUS_CODE["bad_request"].status, + message: CONSTANTS.apiResponses.PROJECT_NOT_FOUND + } + } + + return resolve({ + success: true, + message: CONSTANTS.apiResponses.PROJECT_CERTIFICATE_GENERATED, + data : { + _id : ObjectId(projectDetails._id) + } + + }); + + } catch (error) { + return resolve({ + success: false, + message: error.message, + data: {} + }); + } + }) + } + + /** + * List user project details with certificate + * @method + * @name certificates + * @param {String} userId - userId. + * @returns {JSON} certificate data updation details. + */ + + static certificates(userId) { + return new Promise(async (resolve, reject) => { + try { + let certificateCount = 0; + // get project details of user which have certificate. + const userProject = await projectQueries.projectDocument({ + userId: userId, + status: CONSTANTS.common.SUBMITTED_STATUS, + certificate: {$exists:true} + }, [ + "_id", + "title", + "status", + "certificate.osid", + "certificate.transactioId", + "certificate.templateUrl", + "certificate.status", + "certificate.eligible", + "certificate.message", + "certificate.issuedOn" + ]); + + if ( !userProject.length > 0 ) { + throw { + status: HTTP_STATUS_CODE["bad_request"].status, + message: CONSTANTS.apiResponses.PROJECT_WITH_CERTIFICATE_NOT_FOUND + } + } + // find certificate generated project count + for ( let projectIndex = 0; projectIndex < userProject.length; projectIndex++ ) { + if ( userProject[projectIndex].certificate && + userProject[projectIndex].certificate.osid && + userProject[projectIndex].certificate.osid !== "" + ) { + certificateCount++; + } + } + return resolve({ + success: true, + message: CONSTANTS.apiResponses.PROJECT_CERTIFICATE_GENERATED, + data : { + data : userProject, + count : userProject.length, + certificateCount : certificateCount + } + + }); + } catch (error) { + return resolve({ + success: false, + message: error.message, + data: {} + }); + } + }) + } + + /** + * Re-Issue project certificate + * @method + * @name certificateReIssue + * @param {String} projectId - projectId. + * @param {String} token - usertoken. + * @param {String} recipientName - recipient name. + * @returns {JSON} certificate re-issued details. + */ + + static certificateReIssue(projectId, token, recipientName = "") { + return new Promise(async (resolve, reject) => { + try { + // get project details project for which certificate re-issue required . + const userProject = await projectQueries.projectDocument({ + _id: projectId + }); + + // if project details not found. + if (!userProject.length > 0) { + throw { + status: HTTP_STATUS_CODE['bad_request'].status, + message: CONSTANTS.apiResponses.USER_PROJECT_NOT_FOUND + }; + } + + // This logic can be used if we are not going with user read api + if ( recipientName != "" ) { + userProject[0].userProfile.userName = recipientName + } + + // fetch user data using userId of project and calling the profile API + // let userProfileData = await userProfileService.profile(token, userProject[0].userId); + // if ( userProfileData.success && + // userProfileData.data && + // userProfileData.data.response && + // userProfileData.data.response.userName && + // userProfileData.data.response.userName !== "" + // ) { + // userProject[0].userName = userProfileData.data.response.userName; + // } else { + // throw { + // status: HTTP_STATUS_CODE['bad_request'].status, + // message: CONSTANTS.apiResponses.USER_PROFILE_NOT_FOUND + // }; + // } + // await kafkaProducersHelper.pushProjectToKafka(userProject[0]); + return resolve({ + success: true, + message: CONSTANTS.apiResponses.PROJECT_CERTIFICATE_GENERATED, + data : { + _id : userProject[0]._id + } + + }); + } catch (error) { + return resolve({ + success: false, + message: error.message, + data: {} + }); + } + }) + } + + }; /** From f68aafe95d7a014cd25891c14a6b7d7eb125b90c Mon Sep 17 00:00:00 2001 From: VISHNUDAS-tunerlabse Date: Mon, 17 Oct 2022 18:43:30 +0530 Subject: [PATCH 09/92] certificate-story --- .env.sample | 5 +- config/kafka.js | 6 +- controllers/v1/userProjects.js | 8 +- envVariables.js | 4 + generics/constants/common.js | 3 +- generics/constants/endpoints.js | 3 +- generics/helpers/utils.js | 102 ++++- ...teSubmissions.js => projectCertificate.js} | 8 +- generics/kafka/consumers/submissions.js | 2 +- generics/services/certificate.js | 65 +++ generics/services/users.js | 51 ++- models/projects.js | 5 +- module/userProjects/helper.js | 429 +++++++++++++----- module/userProjects/validator/v1.js | 5 + 14 files changed, 565 insertions(+), 131 deletions(-) rename generics/kafka/consumers/{certificateSubmissions.js => projectCertificate.js} (81%) create mode 100644 generics/services/certificate.js diff --git a/.env.sample b/.env.sample index f6307ace..f41c8cdd 100644 --- a/.env.sample +++ b/.env.sample @@ -30,4 +30,7 @@ SUBMISSION_TOPIC = "dev.sl.projects.submissions" PROJECT_SUBMISSION_TOPIC = "dev.sl.projects.submissions" // project submission topic # SUNBIRD LOCATION AND USER READ -USER_SERVICE_URL = "http://user-service:3000" // service used for user profile read location search are using this base url \ No newline at end of file +USER_SERVICE_URL = "http://user-service:3000" // service used for user profile read location search are using this base url + +#service name +SERVICE_NAME = ml-project-service // ml-project service name \ No newline at end of file diff --git a/config/kafka.js b/config/kafka.js index 23cdfbc8..3e959a19 100644 --- a/config/kafka.js +++ b/config/kafka.js @@ -89,9 +89,9 @@ var _sendToKafkaConsumers = function (topic,host) { if (message && message.topic === SUBMISSION_TOPIC) { submissionsConsumer.messageReceived(message); } - // call certificateSubmissionsConsumer + // call projectCertificateConsumer if (message && message.topic === CERTIFICATE_TOPIC) { - certificateSubmissionsConsumer.messageReceived(message); + projectCertificateConsumer.messageReceived(message); } }); @@ -102,7 +102,7 @@ var _sendToKafkaConsumers = function (topic,host) { submissionsConsumer.errorTriggered(error); } if(error.topics && error.topics[0] === CERTIFICATE_TOPIC) { - certificateSubmissionsConsumer.errorTriggered(error); + projectCertificateConsumer.errorTriggered(error); } }); diff --git a/controllers/v1/userProjects.js b/controllers/v1/userProjects.js index f9c2ece1..663044d0 100644 --- a/controllers/v1/userProjects.js +++ b/controllers/v1/userProjects.js @@ -126,7 +126,6 @@ module.exports = class UserProjects extends Abstract { async sync(req) { return new Promise(async (resolve, reject) => { try { - let createdProject = await userProjectsHelper.sync( req.params._id, req.query.lastDownloadedAt, @@ -999,10 +998,7 @@ module.exports = class UserProjects extends Abstract { return new Promise(async (resolve, reject) => { try { let callback = req.body - if ( callback.data && - callback.data.transactionId && - callback.data.transactionId !== "" && - callback.data.osid && + if ( callback.data.transactionId !== "" && callback.data.osid !== "" ) { let certificateDetails = await userProjectsHelper.certificateCallback( callback.data.transactionId, callback.data.osid ); @@ -1115,8 +1111,6 @@ module.exports = class UserProjects extends Abstract { // ReIssue certificate of given project : projectId is passed as param let projectDetails = await userProjectsHelper.certificateReIssue( req.params._id, - req.userDetails.userToken, - req.query.recipientName ? req.query.recipientName : "" ); return resolve({ message: projectDetails.message, diff --git a/envVariables.js b/envVariables.js index 8ce15ac4..31447958 100644 --- a/envVariables.js +++ b/envVariables.js @@ -39,6 +39,10 @@ let enviromentVariables = { "USER_SERVICE_URL" : { "message" : "Required user service base url", "optional" : false + }, + "SERVICE_NAME" : { + "message" : "Required ml-project-service name", + "optional" : false } } diff --git a/generics/constants/common.js b/generics/constants/common.js index ecc4ba61..30e2703c 100644 --- a/generics/constants/common.js +++ b/generics/constants/common.js @@ -51,6 +51,5 @@ module.exports = { "SERVER_TIME_OUT" : 5000, "OK" : "OK", "PROJECT" : "project", - "PROJECT_CERTIFICATE_RECIPIENT_TYPE" : "user", - "PROJECT_CERTIFICATE_GENERATED" : "Certificate generated successfully" + "PROJECT_CERTIFICATE_GENERATED_SUCCESSFULLY" : "Certificate generated successfully" }; diff --git a/generics/constants/endpoints.js b/generics/constants/endpoints.js index 5cde94a3..109dfaf2 100644 --- a/generics/constants/endpoints.js +++ b/generics/constants/endpoints.js @@ -49,5 +49,6 @@ module.exports = { USER_READ_V5 : "/v5/user/read", GET_LOCATION_DATA : "/v1/location/search", CERTIFICATE_CREATE : "/api/v1/ProjectCertificate", - CERTIFICATE_API_CALLBACK : "/api/userProject/mlproject/v1/certificateCallback" + PROJECT_CERTIFICATE_API_CALLBACK : "/v1/userProject/certificateCallback", + USER_READ_PRIVATE : "/private/user/v1/read" // !Caution: End point for reading user details without token. Do not use for public work flow }; diff --git a/generics/helpers/utils.js b/generics/helpers/utils.js index b4b1f62c..be149348 100644 --- a/generics/helpers/utils.js +++ b/generics/helpers/utils.js @@ -268,14 +268,107 @@ function checkValidUUID(uuids) { /** * convert string to upperCase. * @function - * @name lowerCase + * @name upperCase * @param {String} str - * @returns {String} returns a lowercase string. ex:hello , o/p: HELLO + * @returns {String} returns a upperCase string. ex:hello , o/p: HELLO */ function upperCase(str) { return str.toUpperCase() } + +/** + * make dates comparable + * @function + * @name createComparableDates + * @param {String} dateArg1 + * @param {String} dateArg2 + * @returns {Object} - date object +*/ + +function createComparableDates(dateArg1, dateArg2) { + let date1 + if(typeof dateArg1 === "string") { + date1 = new Date(dateArg1.replace( /(\d{2})-(\d{2})-(\d{4})/, "$2/$1/$3")) + } else { + date1 = new Date(dateArg1) + } + + let date2 + if(typeof dateArg2 === "string") { + date2 = new Date(dateArg2.replace( /(\d{2})-(\d{2})-(\d{4})/, "$2/$1/$3")) + } else { + date2 = new Date(dateArg2) + } + + date1.setHours(0) + date1.setMinutes(0) + date1.setSeconds(0) + date2.setHours(0) + date2.setMinutes(0) + date2.setSeconds(0) + return({ + dateOne: date1, + dateTwo: date2 + }) +} + +/** + * count attachments + * @function + * @name getAttachmentCount + * @param {Object} data - data to count + * @param {Object} filter - filter data + * @returns {Number} - attachment count +*/ + +function getAttachmentCount(data, filter) { + if ( !filter || !Object.keys(filter).length > 0 ) { + return 0 + } + if ( !data.length > 0 ) { + return 0; + } else { + if ( filter.value == "all" ){ + return data.length; + } else { + let count = 0; + for ( let attachment = 0; attachment < data.length; attachment++ ) { + if ( data[attachment][filter.key] == filter.value ) { + count++ + } + } + return count; + } + } +} + +/** + * validate lhs and rhs using operator passed as String + * @function + * @name operatorValidation + * @param {Number or String} valueLhs + * @param {Number or String} valueRhs + * @returns {Boolean} - validation result +*/ + +function operatorValidation(valueLhs, valueRhs, operator) { + return new Promise(async (resolve, reject) => { + let result = false; + if (operator == "==" ) { + result = (valueLhs == valueRhs) ? true : false + } else if (operator == "!=" ) { + result = (valueLhs != valueRhs) ? true : false + } else if (operator == ">" ) { + result = (valueLhs > valueRhs) ? true : false + } else if (operator == "<" ) { + result = (valueLhs < valueRhs) ? true : false + } + return resolve(result) + }) +} + + module.exports = { camelCaseToTitleCase : camelCaseToTitleCase, lowerCase : lowerCase, @@ -290,5 +383,8 @@ module.exports = { revertProjectStatus:revertProjectStatus, revertStatusorNot:revertStatusorNot, checkValidUUID : checkValidUUID, - upperCase : upperCase + upperCase : upperCase, + createComparableDates : createComparableDates, + getAttachmentCount : getAttachmentCount, + operatorValidation : operatorValidation }; diff --git a/generics/kafka/consumers/certificateSubmissions.js b/generics/kafka/consumers/projectCertificate.js similarity index 81% rename from generics/kafka/consumers/certificateSubmissions.js rename to generics/kafka/consumers/projectCertificate.js index bca98ab2..d01f0d10 100644 --- a/generics/kafka/consumers/certificateSubmissions.js +++ b/generics/kafka/consumers/projectCertificate.js @@ -1,5 +1,5 @@ /** - * name : certificateSubmissions.js + * name : projectCertificate.js * author : Vishnu * created-date : 10-Oct-2022 * Description : Project certificates submission consumer. @@ -22,10 +22,8 @@ var messageReceived = function (message) { try { // This consumer is consuming from an old topic : PROJECT_CERTIFICATE_TOPIC, which is no more used by data team. ie) using existig topic instead of creating new one. - let parsedMessage = JSON.parse(message.value); - if ( parsedMessage.status == CONSTANTS.common.SUBMITTED_STATUS && parsedMessage.certificate ) { - await userProjectsHelper.generateCertificate(parsedMessage); - } + let parsedMessage = JSON.parse( message.value ); + await userProjectsHelper.generateCertificate( parsedMessage ); return resolve("Message Received"); } catch (error) { return reject(error); diff --git a/generics/kafka/consumers/submissions.js b/generics/kafka/consumers/submissions.js index 9c56c4d3..e2411f53 100644 --- a/generics/kafka/consumers/submissions.js +++ b/generics/kafka/consumers/submissions.js @@ -25,7 +25,7 @@ var messageReceived = function (message) { try { let parsedMessage = JSON.parse(message.value); - + let submissionDocument = { "_id" : parsedMessage._id.toString(), "status" : parsedMessage.status, diff --git a/generics/services/certificate.js b/generics/services/certificate.js new file mode 100644 index 00000000..60238069 --- /dev/null +++ b/generics/services/certificate.js @@ -0,0 +1,65 @@ +/** + * name : certificate.js + * author : Vishnu + * Date : 07-Oct-2022 + * Description : Sunbird-RC certificate api. + */ + +//dependencies +const request = require('request'); +const CERTIFICATE_SERVICE_URL = process.env.CERTIFICATE_SERVICE_URL; +const ML_PROJECT_URL = `https://${process.env.SERVICE_NAME}:${process.env.APPLICATION_PORT}`; + +/** + * Project certificate creation + * @function + * @name createCertificate + * @param {Object} bodyData - Body data + * @returns {JSON} - Certificate creation details. +*/ + +const createCertificate = function (bodyData) { + return new Promise(async (resolve, reject) => { + try { + const callbackUrl = ML_PROJECT_URL + CONSTANTS.endpoints.PROJECT_CERTIFICATE_API_CALLBACK; + let certificateCreateUrl = + CERTIFICATE_SERVICE_URL + + CONSTANTS.endpoints.CERTIFICATE_CREATE + "?mode=async&callback=" + callbackUrl; + + const options = { + headers : { + "content-type": "application/json" + }, + json : bodyData + }; + + request.post(certificateCreateUrl,options,certificateCallback); + + function certificateCallback(err, data) { + + let result = { + success : true + }; + + if (err) { + result.success = false; + } else { + let response = data.body; + if( response.params.status === "SUCCESSFUL" ) { + result["data"] = response.result; + } else { + result.success = false; + } + } + return resolve(result); + } + + } catch (error) { + return reject(error); + } + }) +} + +module.exports = { + createCertificate : createCertificate +} \ No newline at end of file diff --git a/generics/services/users.js b/generics/services/users.js index 53f8edae..57c00fdf 100644 --- a/generics/services/users.js +++ b/generics/services/users.js @@ -176,8 +176,57 @@ async function getParentEntities( entityId, iteration = 0, parentEntities ) { return parentEntities; } + +/** + * get user profileData without token. + * @method + * @name profileReadPrivate + * @param {String} userId - user Id + * @returns {JSON} - User profile details +*/ +const profileReadPrivate = function (userId) { + return new Promise(async (resolve, reject) => { + try { + // <--- Important : This url endpoint is private do not use it for regular workflows ---> + let url = userServiceUrl + CONSTANTS.endpoints.USER_READ_PRIVATE + "/" + userId; + const options = { + headers : { + "content-type": "application/json" + } + }; + request.get(url,options,userReadCallback); + let result = { + success : true + }; + function userReadCallback(err, data) { + if (err) { + result.success = false; + } else { + + let response = JSON.parse(data.body); + if( response.responseCode === HTTP_STATUS_CODE['ok'].code ) { + result["data"] = response.result; + } else { + result.success = false; + } + + } + return resolve(result); + } + setTimeout(function () { + return resolve (result = { + success : false + }); + }, CONSTANTS.common.SERVER_TIME_OUT); + + } catch (error) { + return reject(error); + } + }) +} module.exports = { profile : profile, locationSearch : locationSearch, - getParentEntities : getParentEntities + getParentEntities : getParentEntities, + profileReadPrivate : profileReadPrivate }; diff --git a/models/projects.js b/models/projects.js index 738ff6bd..0d234bc6 100644 --- a/models/projects.js +++ b/models/projects.js @@ -145,7 +145,10 @@ module.exports = { }, templateUrl : String, status : String, - eligible : Boolean, + eligible : { + type : Boolean, + default : false + }, message : String, issuedOn : Date, criteria : Object diff --git a/module/userProjects/helper.js b/module/userProjects/helper.js index 6b400025..b6780b98 100644 --- a/module/userProjects/helper.js +++ b/module/userProjects/helper.js @@ -104,7 +104,6 @@ module.exports = class UserProjectsHelper { static sync(projectId, lastDownloadedAt, data, userId, userToken, appName = "", appVersion = "") { return new Promise(async (resolve, reject) => { try { - const userProject = await projectQueries.projectDocument({ _id: projectId, userId: userId @@ -353,7 +352,7 @@ module.exports = class UserProjectsHelper { if ( data.status == CONSTANTS.common.COMPLETED_STATUS || data.status == CONSTANTS.common.SUBMITTED_STATUS ) { updateProject.completedDate = new Date(); } - + let projectUpdated = await projectQueries.findOneAndUpdate( { @@ -372,8 +371,15 @@ module.exports = class UserProjectsHelper { status: HTTP_STATUS_CODE['bad_request'].status } } - await kafkaProducersHelper.pushProjectToKafka(projectUpdated); - + + // push to kafka only if project is submitted and certificate key is present + if ( projectUpdated.status == CONSTANTS.common.SUBMITTED_STATUS && + projectUpdated.certificate && + Object.keys(projectUpdated.certificate).length > 0 + ) { + await kafkaProducersHelper.pushProjectToKafka(projectUpdated); + } + return resolve({ success: true, message: CONSTANTS.apiResponses.USER_PROJECT_UPDATED, @@ -1170,7 +1176,7 @@ module.exports = class UserProjectsHelper { templateId : certificateTemplateDetails[0]._id, templateUrl : certificateTemplateDetails[0].templateUrl, status : certificateTemplateDetails[0].status, - criteria : certificateTemplateDetails[0].criteria, + criteria : certificateTemplateDetails[0].criteria } } } @@ -2333,104 +2339,163 @@ module.exports = class UserProjectsHelper { }) } + /** + * validate certificate criteria. + * @method + * @name criteriaValidation + * @param {Object} data - project data for certificate creation + * @returns + */ + + static criteriaValidation(data) { + return new Promise(async (resolve, reject) => { + try { + let criteria = data.certificate.criteria; + let validationResult = []; + let validationMessage = ""; + if ( criteria.conditions ) { + let conditions = criteria.conditions; + let conditionKeys = Object.keys(conditions) + + for ( let index = 0; index < conditionKeys.length; index++ ) { + // correntCondition contain the prefinal level data + let currentCondition = conditions[conditionKeys[index]]; + + //now pass expression and validation scope to another function which will start the validation procedure + let validation = await _subCriteriaValidation( currentCondition.conditions, currentCondition.expression, data ); + + validationResult.push(validation.success); + ( validation.success == false ) ? validationMessage = validationMessage + " " + currentCondition.validationText : ""; + } + + return resolve({ + success: criteriaValidation, + message: ( criteriaValidation == false ) ? validationMessage : CONSTANTS.common.PROJECT_CERTIFICATE_GENERATED_SUCCESSFULLY + }); + } + } catch (error) { + return resolve({ + success: false, + message: error.message, + data: {} + }); + } + }) + } + /** * generate project certificate. * @method * @name generateCertificate - * @param {Object} data - certificate creation data. + * @param {Object} data - project data for certificate creation data. * @returns {JSON} certificate details. */ static generateCertificate(data) { return new Promise(async (resolve, reject) => { try { - // logic to check criteria eligibility to be added - // :Check criteria is sattisfied - // if criteria check passes then call sunbird-RC certificate api - let certificateTemplateDetails = []; - // get downloadable url for certificate template - if ( data.certificate && data.certificate.templateUrl && data.certificate.templateUrl !== "" ) { - let certificateTemplateDownloadableUrl = - await coreService.getDownloadableUrl( - { - filePaths: [data.certificate.templateUrl] - } - ); - if ( certificateTemplateDownloadableUrl.success ) { - data.certificate.templateUrl = certificateTemplateDownloadableUrl.data.url; + // Check criteria is sattisfied if eligible is false + if ( data.certificate.eligible == false ) { + let validateCriteria = await this.criteriaValidation(projectUpdated) + if ( validateCriteria ) { + data.certificate.eligible = true; + data.certificate.message = validateCriteria.message } else { - return resolve({ - success:false - }); + data.certificate.message = validateCriteria.message } } - if ( data.certificate && data.certificate.templateId && data.certificate.templateId !== "" ) { - certificateTemplateDetails = await certificateTemplateQueries.certificateTemplateDocument({ - _id : data.certificate.templateId - },["issuer","solutionId","programId"]); + + // after criteria validation eligibility can change + if ( data.certificate.eligible == false ) { + return resolve( { + success: false + }); + } else { + let certificateTemplateDetails = []; + // get downloadable url for certificate template + if ( data.certificate.templateUrl && data.certificate.templateUrl !== "" ) { + let certificateTemplateDownloadableUrl = + await coreService.getDownloadableUrl( + { + filePaths: [data.certificate.templateUrl] + } + ); + if ( certificateTemplateDownloadableUrl.success ) { + data.certificate.templateUrl = certificateTemplateDownloadableUrl.data.url; + } else { + return resolve({ + success:false + }); + } + } + if ( data.certificate.templateId && data.certificate.templateId !== "" ) { + certificateTemplateDetails = await certificateTemplateQueries.certificateTemplateDocument({ + _id : data.certificate.templateId + },["issuer","solutionId","programId"]); - //certificate template data do not exists. - if ( !certificateTemplateDetails.length > 0 ) { + //certificate template data do not exists. + if ( !certificateTemplateDetails.length > 0 ) { + return resolve({ + success:false + }); + } + } + + //create certificate request body + let certificateData = { + recipient : { + id : data.userId, + name : data.userProfile.userName, + type : data.userProfile.userType + }, + templateUrl : data.certificate.templateUrl, + issuer : certificateTemplateDetails[0].issuer, + status : UTILS.upperCase(data.certificate.status), + projectId : data._id, + projectName : data.title, + programId : certificateTemplateDetails[0].programId, + programName : ( data.programInformation && data.programInformation.name ) ? data.programInformation.name : "", + solutionId : certificateTemplateDetails[0].solutionId, + solutionName : ( data.solutionInformation && data.solutionInformation.name ) ? data.solutionInformation.name : "", + completedDate : data.completedDate + }; + + const certificateDetails = await certificateService.createCertificate( certificateData ); + + if ( certificateDetails.success || certificateDetails.data || certificateDetails.data.ProjectCertificate ) { return resolve({ success:false }); } - } - //create certificate request body - let certificateData = { - recipient : { - id : data.userId, - name : data.userProfile.userName, - type : CONSTANTS.common.PROJECT_CERTIFICATE_RECIPIENT_TYPE - }, - templateUrl : data.certificate.templateUrl, - issuer : certificateTemplateDetails[0].issuer, - status : UTILS.upperCase(data.certificate.status), - projectId : data._id, - projectName : data.title, - programId : certificateTemplateDetails[0].programId, - programName : ( data.programInformation && data.programInformation.name ) ? data.programInformation.name : "", - solutionId : certificateTemplateDetails[0].solutionId, - solutionName : ( data.solutionInformation && data.solutionInformation.name ) ? data.solutionInformation.name : "", - completedDate : data.completedDate - }; - - const certificateDetails = await certificateService.createCertificate( certificateData ); - - let updateObject = { - "$set" : {} - }; + let updateObject = { + "$set" : {} + }; - // if transaction id is present. - if ( certificateDetails.success && - certificateDetails.data && - certificateDetails.data.ProjectCertificate && - certificateDetails.data.ProjectCertificate.transactionId && - certificateDetails.data.ProjectCertificate.transactionId !== "" - ) { - updateObject["$set"]["certificate.transactionId"] = certificateDetails.data.ProjectCertificate.transactionId; - } + // if transaction id is present. + if (certificateDetails.data.ProjectCertificate.transactionId && + certificateDetails.data.ProjectCertificate.transactionId !== "" + ) { + updateObject["$set"]["certificate.transactionId"] = certificateDetails.data.ProjectCertificate.transactionId; + } - if ( certificateDetails.success && - certificateDetails.data && - certificateDetails.data.ProjectCertificate && - certificateDetails.data.ProjectCertificate.osid && - certificateDetails.data.ProjectCertificate.osid !== "" - ) { - updateObject["$set"]["certificate.osid"] = certificateDetails.data.ProjectCertificate.osid; + if ( certificateDetails.data.ProjectCertificate.osid && + certificateDetails.data.ProjectCertificate.osid !== "" + ) { + updateObject["$set"]["certificate.osid"] = certificateDetails.data.ProjectCertificate.osid; + } + let projectDetails = await projectQueries.findOneAndUpdate( + { + _id: data._id + }, + updateObject + ); + + return resolve( { + success: true + }); } - let projectDetails = await projectQueries.findOneAndUpdate( - { - _id: data._id - }, - updateObject - ); - - return resolve( { - success: true - }); - + } catch (error) { return resolve({ success: false, @@ -2460,7 +2525,7 @@ module.exports = class UserProjectsHelper { // update osid and eligibility based on transactionId updateObject["$set"]["certificate.osid"] = osid; updateObject["$set"]["certificate.eligible"] = true; - updateObject["$set"]["certificate.message"] = CONSTANTS.common.PROJECT_CERTIFICATE_GENERATED; + updateObject["$set"]["certificate.message"] = CONSTANTS.common.PROJECT_CERTIFICATE_GENERATED_SUCCESSFULLY; updateObject["$set"]["certificate.issuedOn"] = new Date(); let projectDetails = await projectQueries.findOneAndUpdate( @@ -2569,17 +2634,17 @@ module.exports = class UserProjectsHelper { * @method * @name certificateReIssue * @param {String} projectId - projectId. - * @param {String} token - usertoken. - * @param {String} recipientName - recipient name. * @returns {JSON} certificate re-issued details. */ - static certificateReIssue(projectId, token, recipientName = "") { + static certificateReIssue(projectId) { return new Promise(async (resolve, reject) => { try { // get project details project for which certificate re-issue required . const userProject = await projectQueries.projectDocument({ - _id: projectId + _id: projectId, + status: CONSTANTS.common.SUBMITTED_STATUS, + certificate: {$exists:true} }); // if project details not found. @@ -2590,27 +2655,22 @@ module.exports = class UserProjectsHelper { }; } - // This logic can be used if we are not going with user read api - if ( recipientName != "" ) { - userProject[0].userProfile.userName = recipientName - } - // fetch user data using userId of project and calling the profile API - // let userProfileData = await userProfileService.profile(token, userProject[0].userId); - // if ( userProfileData.success && - // userProfileData.data && - // userProfileData.data.response && - // userProfileData.data.response.userName && - // userProfileData.data.response.userName !== "" - // ) { - // userProject[0].userName = userProfileData.data.response.userName; - // } else { - // throw { - // status: HTTP_STATUS_CODE['bad_request'].status, - // message: CONSTANTS.apiResponses.USER_PROFILE_NOT_FOUND - // }; - // } - // await kafkaProducersHelper.pushProjectToKafka(userProject[0]); + let userProfileData = await userProfileService.profileReadPrivate(userProject[0].userId); + if ( userProfileData.success && + userProfileData.data && + userProfileData.data.response && + userProfileData.data.response.userName && + userProfileData.data.response.userName !== "" + ) { + userProject[0].userProfile.userName = userProfileData.data.response.userName; + } else { + throw { + status: HTTP_STATUS_CODE['bad_request'].status, + message: CONSTANTS.apiResponses.USER_PROFILE_NOT_FOUND + }; + } + await kafkaProducersHelper.pushProjectToKafka(userProject[0]); return resolve({ success: true, message: CONSTANTS.apiResponses.PROJECT_CERTIFICATE_GENERATED, @@ -2631,6 +2691,163 @@ module.exports = class UserProjectsHelper { }; +/** + * _subCriteriaValidation. + * @method + * @name _subCriteriaValidation + * @param {Object} conditions - condition data. + * @param {String} expression - validation expression + * @returns {Boolean} validation. +*/ + +function _subCriteriaValidation(conditions, expression, data) { + return new Promise(async (resolve, reject) => { + try { + let conditionKeys = Object.keys(conditions) + let validationResult = []; + + for ( let index = 0; index < conditionKeys.length; index++ ) { + let currentCondition = conditions[conditionKeys[index]]; + // correntCondition contain the prefinal level data + //now pass expression and validation scope to another function which will start the validation procedure + let validation = await _validateCriteriaConditions( currentCondition, data ); + validationResult.push(validation); + } + + let subcriteriaValidation = await _criteriaExpressionValidation( expression, conditionKeys, validationResult ) + return resolve({ + success: subcriteriaValidation + }); + + } catch (error) { + return resolve({ + message: error.message, + success: false, + status: + error.status ? + error.status : HTTP_STATUS_CODE['internal_server_error'].status + }) + } + }) +} + +/** + * _validateCriteriaConditions. + * @method + * @name _validateCriteriaConditions + * @param {Object} condition - condition data. + * @param {String} data - validation data + * @returns {Boolean} validation. +*/ + +function _validateCriteriaConditions(condition, data) { + return new Promise(async (resolve, reject) => { + try { + let result = false; + if ( !condition.function || condition.function == "" ) { + if( condition.scope == CONSTANTS.common.PROJECT ){ + + // let expression = data[condition.key] + condition.operator + condition.value; + if ( condition.key == "completedDate") { + let comparableDates = UTILS.createComparableDates( data[condition.key], condition.value ); + data[condition.key] = comparableDates.dateOne; + condition.value = comparableDates.dateTwo; + } + result = UTILS.operatorValidation( data[condition.key], condition.value, condition.operator ); + + } + } else { + try { + let valueFromProject = 0; + // if: condition is in scope of project and contains a function to check + if ( condition.scope == CONSTANTS.common.PROJECT ) { + valueFromProject = UTILS.getAttachmentCount( data[condition.key], condition.filter ); + } else if ( condition.scope == CONSTANTS.common.TASK_ATTACHMENT ){ + // for task attachment validatiion _id of specific task or "all" key should be passed in an array called taskDetails + let tasksAttachments = []; + let projectTasks = data.tasks; + + if ( projectTasks && projectTasks.length > 0 && condition.taskDetails.length > 0 && condition.taskDetails[0] == "all" ) { + + for ( let tasksIndex = 0; tasksIndex < projectTasks.length; tasksIndex++ ) { + + if( projectTasks[tasksIndex][condition.key] && projectTasks[tasksIndex][condition.key].length > 0 ) + { + tasksAttachments.push(...projectTasks[tasksIndex][condition.key]) + } + } + + } else if ( projectTasks && projectTasks.length > 0 && condition.taskDetails.length > 0 ) { + + // specific task Id or Ids are passed for attachment validation + for ( let tasksIndex = 0; tasksIndex < projectTasks.length; tasksIndex++ ) { + for ( let taskDetailsPointer = 0; taskDetailsPointer < condition.taskDetails.length; taskDetailsPointer++ ) { + // get attachments data of specified task/ tasks + if( projectTasks[tasksIndex]._id == condition.taskDetails[taskDetailsPointer] && projectTasks[tasksIndex][condition.key] && projectTasks[tasksIndex][condition.key].length > 0 ) { + tasksAttachments.push(...projectTasks[tasksIndex][condition.key]) + } + } + + } + + } else { + return resolve(result) + } + if ( !tasksAttachments.length > 0 ) { + return resolve(result) + } + valueFromProject = UTILS.getAttachmentCount( tasksAttachments, condition.filter ); + } + result = UTILS.operatorValidation( valueFromProject, condition.value, condition.operator ); + + } catch (fnError) { + return resolve(result) + } + } + return resolve(result); + } catch (error) { + return resolve({ + message: error.message, + success: false, + status: + error.status ? + error.status : HTTP_STATUS_CODE['internal_server_error'].status + }) + } + }) +} +/** + * _criteriaExpressionValidation + * @method + * @name _criteriaExpressionValidation + * @param {String} expression - criteria expression + * @param {Array} keys - condition keys + * @param {Array} result - condition result + * @returns {Boolean} validation result. +*/ + +function _criteriaExpressionValidation(expression, keys, result) { + return new Promise(async (resolve, reject) => { + try { + + if( expression == "" || + !keys.length > 0 || + !result.length > 0 || + keys.length != result.length ) { + return resolve(false); + } + for ( let pointerToKeys = 0; pointerToKeys < keys.length; pointerToKeys++) { + expression = expression.replace(keys[pointerToKeys],result[pointerToKeys].toString()) + } + let evalResult = eval(expression) + + return resolve(evalResult); + + } catch (error) { + return resolve(false); + } + }) +} /** * Project information. diff --git a/module/userProjects/validator/v1.js b/module/userProjects/validator/v1.js index a0282a23..09dcbfa6 100644 --- a/module/userProjects/validator/v1.js +++ b/module/userProjects/validator/v1.js @@ -28,6 +28,11 @@ module.exports = (req) => { }, certificateReIssue : function () { req.checkParams('_id').exists().withMessage("required project id"); + }, + certificateCallback : function () { + req.checkBody("data").exists().withMessage("data is required"); + req.checkBody("data.transactionId").exists().withMessage("transactionId is required"); + req.checkBody("data.osid").exists().withMessage("osid is required"); } } From 9bb3767094f7befc3311f5a974a09154859186a1 Mon Sep 17 00:00:00 2001 From: VISHNUDAS-tunerlabse Date: Tue, 18 Oct 2022 09:09:07 +0530 Subject: [PATCH 10/92] certificate story --- module/userProjects/helper.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/module/userProjects/helper.js b/module/userProjects/helper.js index b6780b98..aa5a6f91 100644 --- a/module/userProjects/helper.js +++ b/module/userProjects/helper.js @@ -2396,7 +2396,7 @@ module.exports = class UserProjectsHelper { try { // Check criteria is sattisfied if eligible is false if ( data.certificate.eligible == false ) { - let validateCriteria = await this.criteriaValidation(projectUpdated) + let validateCriteria = await this.criteriaValidation(data) if ( validateCriteria ) { data.certificate.eligible = true; data.certificate.message = validateCriteria.message @@ -2404,7 +2404,6 @@ module.exports = class UserProjectsHelper { data.certificate.message = validateCriteria.message } } - // after criteria validation eligibility can change if ( data.certificate.eligible == false ) { return resolve( { @@ -2461,7 +2460,6 @@ module.exports = class UserProjectsHelper { }; const certificateDetails = await certificateService.createCertificate( certificateData ); - if ( certificateDetails.success || certificateDetails.data || certificateDetails.data.ProjectCertificate ) { return resolve({ success:false From b1de579d9f6d766ef4c36be59aacaa317afc455c Mon Sep 17 00:00:00 2001 From: VISHNUDAS-tunerlabse Date: Thu, 20 Oct 2022 18:03:44 +0530 Subject: [PATCH 11/92] certificate story changes --- controllers/v1/userProjects.js | 7 +- envVariables.js | 5 +- generics/constants/api-responses.js | 3 +- .../kafka/consumers/projectCertificate.js | 51 +++-- models/projects.js | 5 +- module/userProjects/helper.js | 188 ++++++++++-------- 6 files changed, 139 insertions(+), 120 deletions(-) diff --git a/controllers/v1/userProjects.js b/controllers/v1/userProjects.js index 663044d0..cc904bef 100644 --- a/controllers/v1/userProjects.js +++ b/controllers/v1/userProjects.js @@ -997,16 +997,11 @@ module.exports = class UserProjects extends Abstract { async certificateCallback(req) { return new Promise(async (resolve, reject) => { try { - let callback = req.body - if ( callback.data.transactionId !== "" && - callback.data.osid !== "" - ) { - let certificateDetails = await userProjectsHelper.certificateCallback( callback.data.transactionId, callback.data.osid ); + let certificateDetails = await userProjectsHelper.certificateCallback( req.body.data.transactionId, req.body.data.osid ); return resolve({ message: certificateDetails.message, result: certificateDetails.data }); - } } catch (error) { return reject({ status: error.status || HTTP_STATUS_CODE.internal_server_error.status, diff --git a/envVariables.js b/envVariables.js index 31447958..6c5a0974 100644 --- a/envVariables.js +++ b/envVariables.js @@ -41,8 +41,9 @@ let enviromentVariables = { "optional" : false }, "SERVICE_NAME" : { - "message" : "Required ml-project-service name", - "optional" : false + "message" : "Form service base url", + "optional" : true, + "default" : "ml-project-service" } } diff --git a/generics/constants/api-responses.js b/generics/constants/api-responses.js index bd0c1d6a..680092c8 100644 --- a/generics/constants/api-responses.js +++ b/generics/constants/api-responses.js @@ -126,5 +126,6 @@ module.exports = { "FAILED_TO_SYNC_PROJECT_ALREADY_SUBMITTED" : "Failed to sync, Project is already Submitted", "SOLUTION_ID_AND_USERPROFILE_REQUIRED": "Required solution Id and userProfile", "PROJECT_WITH_CERTIFICATE_NOT_FOUND": "No certification project found for user", - "PROJECT_CERTIFICATE_GENERATED" : "Successfully generated project certificate" + "PROJECT_CERTIFICATE_GENERATED" : "Successfully generated project certificate", + "TRANSACTION_ID_AND_OSID_REQUIRED" : "Required transactionId and osid" }; diff --git a/generics/kafka/consumers/projectCertificate.js b/generics/kafka/consumers/projectCertificate.js index d01f0d10..1e663057 100644 --- a/generics/kafka/consumers/projectCertificate.js +++ b/generics/kafka/consumers/projectCertificate.js @@ -9,46 +9,43 @@ const userProjectsHelper = require(MODULES_BASE_PATH + "/userProjects/helper"); /** - * submission consumer message received. - * @function - * @name messageReceived - * @param {String} message - consumer data - * @returns {Promise} return a Promise. +* submission consumer message received. +* @function +* @name messageReceived +* @param {String} message - consumer data +* @returns {Promise} return a Promise. */ var messageReceived = function (message) { - return new Promise(async function (resolve, reject) { + try { + // This consumer is consuming from an old topic : PROJECT_CERTIFICATE_TOPIC, which is no more used by data team. ie) using existig topic instead of creating new one. + let parsedMessage = JSON.parse( message.value ); + await userProjectsHelper.generateCertificate( parsedMessage ); + return resolve("Message Received"); + } catch (error) { + return reject(error); + } - try { - // This consumer is consuming from an old topic : PROJECT_CERTIFICATE_TOPIC, which is no more used by data team. ie) using existig topic instead of creating new one. - let parsedMessage = JSON.parse( message.value ); - await userProjectsHelper.generateCertificate( parsedMessage ); - return resolve("Message Received"); - } catch (error) { - return reject(error); - } - - }); + }); }; /** - * If message is not received. - * @function - * @name errorTriggered - * @param {Object} error - error object - * @returns {Promise} return a Promise. +* If message is not received. +* @function +* @name errorTriggered +* @param {Object} error - error object +* @returns {Promise} return a Promise. */ var errorTriggered = function (error) { - return new Promise(function (resolve, reject) { - try { - return resolve(error); - } catch (error) { - return reject(error); - } + try { + return resolve(error); + } catch (error) { + return reject(error); + } }); }; diff --git a/models/projects.js b/models/projects.js index 0d234bc6..738ff6bd 100644 --- a/models/projects.js +++ b/models/projects.js @@ -145,10 +145,7 @@ module.exports = { }, templateUrl : String, status : String, - eligible : { - type : Boolean, - default : false - }, + eligible : Boolean, message : String, issuedOn : Date, criteria : Object diff --git a/module/userProjects/helper.js b/module/userProjects/helper.js index aa5a6f91..4cf0e44b 100644 --- a/module/userProjects/helper.js +++ b/module/userProjects/helper.js @@ -2353,7 +2353,7 @@ module.exports = class UserProjectsHelper { let criteria = data.certificate.criteria; let validationResult = []; let validationMessage = ""; - if ( criteria.conditions ) { + if ( criteria.conditions && Object.keys(criteria.conditions).length > 0 ) { let conditions = criteria.conditions; let conditionKeys = Object.keys(conditions) @@ -2373,6 +2373,9 @@ module.exports = class UserProjectsHelper { message: ( criteriaValidation == false ) ? validationMessage : CONSTANTS.common.PROJECT_CERTIFICATE_GENERATED_SUCCESSFULLY }); } + return resolve({ + success: false + }) } catch (error) { return resolve({ success: false, @@ -2394,106 +2397,120 @@ module.exports = class UserProjectsHelper { static generateCertificate(data) { return new Promise(async (resolve, reject) => { try { - // Check criteria is sattisfied if eligible is false - if ( data.certificate.eligible == false ) { + // if eligible key is not there check criteria for validation + if ( !data.certificate.eligible ) { let validateCriteria = await this.criteriaValidation(data) if ( validateCriteria ) { data.certificate.eligible = true; - data.certificate.message = validateCriteria.message } else { + data.certificate.eligible = false; data.certificate.message = validateCriteria.message } } - // after criteria validation eligibility can change - if ( data.certificate.eligible == false ) { + if ( data.certificate.eligible === true && ( data.certificate.transactionId || data.certificate.osid ) ) { return resolve( { success: false }); } else { - let certificateTemplateDetails = []; - // get downloadable url for certificate template - if ( data.certificate.templateUrl && data.certificate.templateUrl !== "" ) { - let certificateTemplateDownloadableUrl = - await coreService.getDownloadableUrl( - { - filePaths: [data.certificate.templateUrl] + if ( data.certificate.eligible === true ) { + let certificateTemplateDetails = []; + // get downloadable url for certificate template + if ( data.certificate.templateUrl && data.certificate.templateUrl !== "" ) { + let certificateTemplateDownloadableUrl = + await coreService.getDownloadableUrl( + { + filePaths: [data.certificate.templateUrl] + } + ); + if ( certificateTemplateDownloadableUrl.success ) { + data.certificate.templateUrl = certificateTemplateDownloadableUrl.data.url; + } else { + return resolve({ + success:false + }); } - ); - if ( certificateTemplateDownloadableUrl.success ) { - data.certificate.templateUrl = certificateTemplateDownloadableUrl.data.url; - } else { + } + if ( data.certificate.templateId && data.certificate.templateId !== "" ) { + certificateTemplateDetails = await certificateTemplateQueries.certificateTemplateDocument({ + _id : data.certificate.templateId + },["issuer","solutionId","programId"]); + + //certificate template data do not exists. + if ( !certificateTemplateDetails.length > 0 ) { + return resolve({ + success:false + }); + } + } + + //create certificate request body + let certificateData = { + recipient : { + id : data.userId, + name : data.userProfile.userName, + type : data.userProfile.userType + }, + templateUrl : data.certificate.templateUrl, + issuer : certificateTemplateDetails[0].issuer, + status : UTILS.upperCase(data.certificate.status), + projectId : data._id, + projectName : data.title, + programId : certificateTemplateDetails[0].programId, + programName : ( data.programInformation && data.programInformation.name ) ? data.programInformation.name : "", + solutionId : certificateTemplateDetails[0].solutionId, + solutionName : ( data.solutionInformation && data.solutionInformation.name ) ? data.solutionInformation.name : "", + completedDate : data.completedDate + }; + + const certificateDetails = await certificateService.createCertificate( certificateData ); + if ( !certificateDetails.success || !certificateDetails.data || !certificateDetails.data.ProjectCertificate ) { return resolve({ success:false }); } - } - if ( data.certificate.templateId && data.certificate.templateId !== "" ) { - certificateTemplateDetails = await certificateTemplateQueries.certificateTemplateDocument({ - _id : data.certificate.templateId - },["issuer","solutionId","programId"]); + + let updateObject = { + "$set" : {} + }; - //certificate template data do not exists. - if ( !certificateTemplateDetails.length > 0 ) { + // if transaction id is present. + if ( certificateDetails.data.ProjectCertificate.transactionId && + certificateDetails.data.ProjectCertificate.transactionId !== "" + ) { + let transactionIdvalue = certificateDetails.data.ProjectCertificate.transactionId; + transactionIdvalue = transactionIdvalue.split("1-") + updateObject["$set"]["certificate.transactionId"] = transactionIdvalue[1]; + } + + if ( certificateDetails.data.ProjectCertificate.osid && + certificateDetails.data.ProjectCertificate.osid !== "" + ) { + updateObject["$set"]["certificate.osid"] = certificateDetails.data.ProjectCertificate.osid; + } + + if ( Object.keys(updateObject["$set"]) > 0 ) { + let projectDetails = await projectQueries.findOneAndUpdate( + { + _id: data._id + }, + updateObject + ); + + return resolve( { + success: true + }); + } else { return resolve({ success:false }); } - } - - //create certificate request body - let certificateData = { - recipient : { - id : data.userId, - name : data.userProfile.userName, - type : data.userProfile.userType - }, - templateUrl : data.certificate.templateUrl, - issuer : certificateTemplateDetails[0].issuer, - status : UTILS.upperCase(data.certificate.status), - projectId : data._id, - projectName : data.title, - programId : certificateTemplateDetails[0].programId, - programName : ( data.programInformation && data.programInformation.name ) ? data.programInformation.name : "", - solutionId : certificateTemplateDetails[0].solutionId, - solutionName : ( data.solutionInformation && data.solutionInformation.name ) ? data.solutionInformation.name : "", - completedDate : data.completedDate - }; - - const certificateDetails = await certificateService.createCertificate( certificateData ); - if ( certificateDetails.success || certificateDetails.data || certificateDetails.data.ProjectCertificate ) { + + } else { return resolve({ success:false }); } - - let updateObject = { - "$set" : {} - }; - - // if transaction id is present. - if (certificateDetails.data.ProjectCertificate.transactionId && - certificateDetails.data.ProjectCertificate.transactionId !== "" - ) { - updateObject["$set"]["certificate.transactionId"] = certificateDetails.data.ProjectCertificate.transactionId; - } - - if ( certificateDetails.data.ProjectCertificate.osid && - certificateDetails.data.ProjectCertificate.osid !== "" - ) { - updateObject["$set"]["certificate.osid"] = certificateDetails.data.ProjectCertificate.osid; - } - let projectDetails = await projectQueries.findOneAndUpdate( - { - _id: data._id - }, - updateObject - ); - - return resolve( { - success: true - }); } - } catch (error) { return resolve({ success: false, @@ -2516,13 +2533,18 @@ module.exports = class UserProjectsHelper { static certificateCallback(transactionId, osid) { return new Promise(async (resolve, reject) => { try { + if ( transactionId == "" || osid == "" ) { + throw { + status: HTTP_STATUS_CODE["bad_request"].status, + message: CONSTANTS.apiResponses.TRANSACTION_ID_AND_OSID_REQUIRED + } + } let updateObject = { "$set" : {} }; // update osid and eligibility based on transactionId updateObject["$set"]["certificate.osid"] = osid; - updateObject["$set"]["certificate.eligible"] = true; updateObject["$set"]["certificate.message"] = CONSTANTS.common.PROJECT_CERTIFICATE_GENERATED_SUCCESSFULLY; updateObject["$set"]["certificate.issuedOn"] = new Date(); @@ -2668,6 +2690,12 @@ module.exports = class UserProjectsHelper { message: CONSTANTS.apiResponses.USER_PROFILE_NOT_FOUND }; } + if ( userProject[0].certificate.transactionId ) { + delete userProject[0].certificate.transactionId; + } + if ( userProject[0].certificate.osid ) { + delete userProject[0].certificate.osid; + } await kafkaProducersHelper.pushProjectToKafka(userProject[0]); return resolve({ success: true, @@ -2743,7 +2771,7 @@ function _validateCriteriaConditions(condition, data) { try { let result = false; if ( !condition.function || condition.function == "" ) { - if( condition.scope == CONSTANTS.common.PROJECT ){ + if ( condition.scope == CONSTANTS.common.PROJECT ){ // let expression = data[condition.key] + condition.operator + condition.value; if ( condition.key == "completedDate") { @@ -2769,7 +2797,7 @@ function _validateCriteriaConditions(condition, data) { for ( let tasksIndex = 0; tasksIndex < projectTasks.length; tasksIndex++ ) { - if( projectTasks[tasksIndex][condition.key] && projectTasks[tasksIndex][condition.key].length > 0 ) + if ( projectTasks[tasksIndex][condition.key] && projectTasks[tasksIndex][condition.key].length > 0 ) { tasksAttachments.push(...projectTasks[tasksIndex][condition.key]) } @@ -2781,7 +2809,7 @@ function _validateCriteriaConditions(condition, data) { for ( let tasksIndex = 0; tasksIndex < projectTasks.length; tasksIndex++ ) { for ( let taskDetailsPointer = 0; taskDetailsPointer < condition.taskDetails.length; taskDetailsPointer++ ) { // get attachments data of specified task/ tasks - if( projectTasks[tasksIndex]._id == condition.taskDetails[taskDetailsPointer] && projectTasks[tasksIndex][condition.key] && projectTasks[tasksIndex][condition.key].length > 0 ) { + if ( projectTasks[tasksIndex]._id == condition.taskDetails[taskDetailsPointer] && projectTasks[tasksIndex][condition.key] && projectTasks[tasksIndex][condition.key].length > 0 ) { tasksAttachments.push(...projectTasks[tasksIndex][condition.key]) } } @@ -2834,7 +2862,7 @@ function _criteriaExpressionValidation(expression, keys, result) { keys.length != result.length ) { return resolve(false); } - for ( let pointerToKeys = 0; pointerToKeys < keys.length; pointerToKeys++) { + for ( let pointerToKeys = 0; pointerToKeys < keys.length; pointerToKeys++ ) { expression = expression.replace(keys[pointerToKeys],result[pointerToKeys].toString()) } let evalResult = eval(expression) From 5d9394e42404b12203e82b363dba40913bcc1126 Mon Sep 17 00:00:00 2001 From: VISHNUDAS-tunerlabse Date: Thu, 20 Oct 2022 20:32:01 +0530 Subject: [PATCH 12/92] certificate story changes --- module/userProjects/helper.js | 37 +++++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/module/userProjects/helper.js b/module/userProjects/helper.js index 4cf0e44b..9912e114 100644 --- a/module/userProjects/helper.js +++ b/module/userProjects/helper.js @@ -1328,7 +1328,7 @@ module.exports = class UserProjectsHelper { } ); if ( certificateTemplateDownloadableUrl.success ) { - projectDetails.data.certificate.templateUrl = certificateTemplateDownloadableUrl.data.url; + projectDetails.data.certificate.templateUrl = certificateTemplateDownloadableUrl.data[0].url; } } @@ -2353,6 +2353,7 @@ module.exports = class UserProjectsHelper { let criteria = data.certificate.criteria; let validationResult = []; let validationMessage = ""; + let validationExpression = criteria.expression if ( criteria.conditions && Object.keys(criteria.conditions).length > 0 ) { let conditions = criteria.conditions; let conditionKeys = Object.keys(conditions) @@ -2367,7 +2368,7 @@ module.exports = class UserProjectsHelper { validationResult.push(validation.success); ( validation.success == false ) ? validationMessage = validationMessage + " " + currentCondition.validationText : ""; } - + let criteriaValidation = await _criteriaExpressionValidation( validationExpression, conditionKeys, validationResult ) return resolve({ success: criteriaValidation, message: ( criteriaValidation == false ) ? validationMessage : CONSTANTS.common.PROJECT_CERTIFICATE_GENERATED_SUCCESSFULLY @@ -2400,13 +2401,28 @@ module.exports = class UserProjectsHelper { // if eligible key is not there check criteria for validation if ( !data.certificate.eligible ) { let validateCriteria = await this.criteriaValidation(data) - if ( validateCriteria ) { + if ( validateCriteria.success ) { data.certificate.eligible = true; } else { data.certificate.eligible = false; data.certificate.message = validateCriteria.message } } + let updateObject = { + "$set" : {} + }; + updateObject["$set"]["certificate.eligible"] = data.certificate.eligible; + if ( data.certificate.message && data.certificate.message !=="" ) { + updateObject["$set"]["certificate.message"] = data.certificate.message; + } + if ( Object.keys(updateObject["$set"]).length > 0 ) { + await projectQueries.findOneAndUpdate( + { + _id: data._id + }, + updateObject + ); + } if ( data.certificate.eligible === true && ( data.certificate.transactionId || data.certificate.osid ) ) { return resolve( { success: false @@ -2423,7 +2439,7 @@ module.exports = class UserProjectsHelper { } ); if ( certificateTemplateDownloadableUrl.success ) { - data.certificate.templateUrl = certificateTemplateDownloadableUrl.data.url; + data.certificate.templateUrl = certificateTemplateDownloadableUrl.data[0].url; } else { return resolve({ success:false @@ -2453,11 +2469,11 @@ module.exports = class UserProjectsHelper { templateUrl : data.certificate.templateUrl, issuer : certificateTemplateDetails[0].issuer, status : UTILS.upperCase(data.certificate.status), - projectId : data._id, + projectId : (data._id).toString(), projectName : data.title, - programId : certificateTemplateDetails[0].programId, + programId : (certificateTemplateDetails[0].programId).toString(), programName : ( data.programInformation && data.programInformation.name ) ? data.programInformation.name : "", - solutionId : certificateTemplateDetails[0].solutionId, + solutionId : (certificateTemplateDetails[0].solutionId).toString(), solutionName : ( data.solutionInformation && data.solutionInformation.name ) ? data.solutionInformation.name : "", completedDate : data.completedDate }; @@ -2478,7 +2494,8 @@ module.exports = class UserProjectsHelper { certificateDetails.data.ProjectCertificate.transactionId !== "" ) { let transactionIdvalue = certificateDetails.data.ProjectCertificate.transactionId; - transactionIdvalue = transactionIdvalue.split("1-") + transactionIdvalue = transactionIdvalue.split("1-"); + let transactionIdData = transactionIdvalue[1]; updateObject["$set"]["certificate.transactionId"] = transactionIdvalue[1]; } @@ -2487,8 +2504,8 @@ module.exports = class UserProjectsHelper { ) { updateObject["$set"]["certificate.osid"] = certificateDetails.data.ProjectCertificate.osid; } - - if ( Object.keys(updateObject["$set"]) > 0 ) { + updateObject["$set"]["certificate.eligible"] = true; + if ( Object.keys(updateObject["$set"]).length > 0 ) { let projectDetails = await projectQueries.findOneAndUpdate( { _id: data._id From dc0ad53334f9bae8eb9289084f1be0b01e55980d Mon Sep 17 00:00:00 2001 From: VISHNUDAS-tunerlabse Date: Fri, 21 Oct 2022 03:13:25 +0530 Subject: [PATCH 13/92] certificate story change --- module/userProjects/helper.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/module/userProjects/helper.js b/module/userProjects/helper.js index 9912e114..04f0a799 100644 --- a/module/userProjects/helper.js +++ b/module/userProjects/helper.js @@ -2506,7 +2506,7 @@ module.exports = class UserProjectsHelper { } updateObject["$set"]["certificate.eligible"] = true; if ( Object.keys(updateObject["$set"]).length > 0 ) { - let projectDetails = await projectQueries.findOneAndUpdate( + await projectQueries.findOneAndUpdate( { _id: data._id }, @@ -2648,7 +2648,7 @@ module.exports = class UserProjectsHelper { } return resolve({ success: true, - message: CONSTANTS.apiResponses.PROJECT_CERTIFICATE_GENERATED, + message: CONSTANTS.apiResponses.PROJECTS_FETCHED, data : { data : userProject, count : userProject.length, From 7a4ac655f27568a56fcd7e53c05d0ff4af3ec9eb Mon Sep 17 00:00:00 2001 From: VISHNUDAS-tunerlabse Date: Thu, 27 Oct 2022 15:07:42 +0530 Subject: [PATCH 14/92] env updated --- .env.sample | 6 +++++- envVariables.js | 5 +++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/.env.sample b/.env.sample index f41c8cdd..2e79dd8d 100644 --- a/.env.sample +++ b/.env.sample @@ -33,4 +33,8 @@ PROJECT_SUBMISSION_TOPIC = "dev.sl.projects.submissions" USER_SERVICE_URL = "http://user-service:3000" // service used for user profile read location search are using this base url #service name -SERVICE_NAME = ml-project-service // ml-project service name \ No newline at end of file +SERVICE_NAME = ml-project-service // ml-project service name + +# sunbird-rc service +CERTIFICATE_SERVICE_URL = http://registry-service:8081 // sunbird-RC registry service URL + diff --git a/envVariables.js b/envVariables.js index 6c5a0974..1caa006b 100644 --- a/envVariables.js +++ b/envVariables.js @@ -44,6 +44,11 @@ let enviromentVariables = { "message" : "Form service base url", "optional" : true, "default" : "ml-project-service" + }, + "CERTIFICATE_SERVICE_URL" : { + "message" : "Form service base url", + "optional" : true, + "default" : "http://registry-service:8081" } } From 559bdaf6135b4dae03c1d0b79c98c19413d0dd05 Mon Sep 17 00:00:00 2001 From: VISHNUDAS-tunerlabse Date: Fri, 28 Oct 2022 17:42:03 +0530 Subject: [PATCH 15/92] review changes --- envVariables.js | 4 +- generics/helpers/utils.js | 17 +- .../kafka/consumers/projectCertificate.js | 7 +- models/certificateTemplates.js | 3 +- models/projects.js | 9 +- module/certificateValidations/helper.js | 227 +++++++++++++++++ module/userProjects/helper.js | 236 +----------------- 7 files changed, 263 insertions(+), 240 deletions(-) create mode 100644 module/certificateValidations/helper.js diff --git a/envVariables.js b/envVariables.js index 1caa006b..a2da8cfb 100644 --- a/envVariables.js +++ b/envVariables.js @@ -41,12 +41,12 @@ let enviromentVariables = { "optional" : false }, "SERVICE_NAME" : { - "message" : "Form service base url", + "message" : "current service name", "optional" : true, "default" : "ml-project-service" }, "CERTIFICATE_SERVICE_URL" : { - "message" : "Form service base url", + "message" : "certificate service base url", "optional" : true, "default" : "http://registry-service:8081" } diff --git a/generics/helpers/utils.js b/generics/helpers/utils.js index be149348..11834c61 100644 --- a/generics/helpers/utils.js +++ b/generics/helpers/utils.js @@ -316,15 +316,15 @@ function createComparableDates(dateArg1, dateArg2) { /** * count attachments * @function - * @name getAttachmentCount + * @name noOfElementsInArray * @param {Object} data - data to count * @param {Object} filter - filter data * @returns {Number} - attachment count */ -function getAttachmentCount(data, filter) { +function noOfElementsInArray(data, filter = {}) { if ( !filter || !Object.keys(filter).length > 0 ) { - return 0 + return data.length; } if ( !data.length > 0 ) { return 0; @@ -344,7 +344,7 @@ function getAttachmentCount(data, filter) { } /** - * validate lhs and rhs using operator passed as String + * validate lhs and rhs using operator passed as String/ Number * @function * @name operatorValidation * @param {Number or String} valueLhs @@ -353,7 +353,7 @@ function getAttachmentCount(data, filter) { */ function operatorValidation(valueLhs, valueRhs, operator) { - return new Promise(async (resolve, reject) => { + return new Promise(async (resolve, reject) => { let result = false; if (operator == "==" ) { result = (valueLhs == valueRhs) ? true : false @@ -363,12 +363,15 @@ function operatorValidation(valueLhs, valueRhs, operator) { result = (valueLhs > valueRhs) ? true : false } else if (operator == "<" ) { result = (valueLhs < valueRhs) ? true : false + } else if (operator == "<=" ) { + result = (valueLhs <= valueRhs) ? true : false + } else if (operator == ">=" ) { + result = (valueLhs >= valueRhs) ? true : false } return resolve(result) }) } - module.exports = { camelCaseToTitleCase : camelCaseToTitleCase, lowerCase : lowerCase, @@ -385,6 +388,6 @@ module.exports = { checkValidUUID : checkValidUUID, upperCase : upperCase, createComparableDates : createComparableDates, - getAttachmentCount : getAttachmentCount, + noOfElementsInArray : noOfElementsInArray, operatorValidation : operatorValidation }; diff --git a/generics/kafka/consumers/projectCertificate.js b/generics/kafka/consumers/projectCertificate.js index 1e663057..69389e77 100644 --- a/generics/kafka/consumers/projectCertificate.js +++ b/generics/kafka/consumers/projectCertificate.js @@ -21,7 +21,12 @@ var messageReceived = function (message) { try { // This consumer is consuming from an old topic : PROJECT_CERTIFICATE_TOPIC, which is no more used by data team. ie) using existig topic instead of creating new one. let parsedMessage = JSON.parse( message.value ); - await userProjectsHelper.generateCertificate( parsedMessage ); + if ( parsedMessage.status == CONSTANTS.common.SUBMITTED_STATUS && + parsedMessage.certificate && + Object.keys(parsedMessage.certificate).length > 0 + ) { + await userProjectsHelper.generateCertificate( parsedMessage ); + } return resolve("Message Received"); } catch (error) { return reject(error); diff --git a/models/certificateTemplates.js b/models/certificateTemplates.js index 40b55e56..e65899f0 100644 --- a/models/certificateTemplates.js +++ b/models/certificateTemplates.js @@ -9,7 +9,8 @@ module.exports = { }, solutionId: { type : "ObjectId", - index : true + index : true, + unique: true }, programId: "ObjectId", criteria: { diff --git a/models/projects.js b/models/projects.js index 738ff6bd..b9f150d7 100644 --- a/models/projects.js +++ b/models/projects.js @@ -138,10 +138,15 @@ module.exports = { userProfile : Object, certificate : { templateId : "ObjectId", - osid : String, + osid : { + type : String, + index : true, + unique : true + }, transactionId : { type : String, - index : true + index : true, + unique : true }, templateUrl : String, status : String, diff --git a/module/certificateValidations/helper.js b/module/certificateValidations/helper.js new file mode 100644 index 00000000..e550079c --- /dev/null +++ b/module/certificateValidations/helper.js @@ -0,0 +1,227 @@ +/** + * name : helper.js + * author : vishnu + * created-date : 26-Oct-2022 + * Description : certificate validation helper functionality. + */ + +// Dependencies + +/** + * certificateValidationsHelper + * @class +*/ + +module.exports = class certificateValidationsHelper { + + /** + * validate certificate criteria. + * @method + * @name criteriaValidation + * @param {Object} data - project data for certificate creation + * @returns + */ + + static criteriaValidation(data) { + return new Promise(async (resolve, reject) => { + try { + let criteria = data.certificate.criteria; + let validationResult = []; + let validationMessage = ""; + let validationExpression = criteria.expression + if ( criteria.conditions && Object.keys(criteria.conditions).length > 0 ) { + let conditions = criteria.conditions; + let conditionKeys = Object.keys(conditions) + + for ( let index = 0; index < conditionKeys.length; index++ ) { + // correntCondition contain the prefinal level data + let currentCondition = conditions[conditionKeys[index]]; + + //now pass expression and validation scope to another function which will start the validation procedure + let validation = await _subCriteriaValidation( currentCondition.conditions, currentCondition.expression, data ); + + validationResult.push(validation.success); + ( validation.success == false ) ? validationMessage = validationMessage + " " + currentCondition.validationText : ""; + } + let criteriaValidation = await _criteriaExpressionValidation( validationExpression, conditionKeys, validationResult ) + return resolve({ + success: criteriaValidation, + message: ( criteriaValidation == false ) ? validationMessage : CONSTANTS.common.PROJECT_CERTIFICATE_GENERATED_SUCCESSFULLY + }); + } + return resolve({ + success: false + }) + } catch (error) { + return resolve({ + success: false, + message: error.message, + data: {} + }); + } + }) + } +}; + +/** + * _subCriteriaValidation. + * @method + * @name _subCriteriaValidation + * @param {Object} conditions - condition data. + * @param {String} expression - validation expression + * @returns {Boolean} validation. +*/ + +function _subCriteriaValidation(conditions, expression, data) { + return new Promise(async (resolve, reject) => { + try { + let conditionKeys = Object.keys(conditions) + let validationResult = []; + + for ( let index = 0; index < conditionKeys.length; index++ ) { + let currentCondition = conditions[conditionKeys[index]]; + // correntCondition contain the prefinal level data + //now pass expression and validation scope to another function which will start the validation procedure + let validation = await _validateCriteriaConditions( currentCondition, data ); + validationResult.push(validation); + } + + let subcriteriaValidation = await _criteriaExpressionValidation( expression, conditionKeys, validationResult ) + return resolve({ + success: subcriteriaValidation + }); + + } catch (error) { + return resolve({ + message: error.message, + success: false, + status: + error.status ? + error.status : HTTP_STATUS_CODE['internal_server_error'].status + }) + } + }) +} + +/** + * _validateCriteriaConditions. + * @method + * @name _validateCriteriaConditions + * @param {Object} condition - condition data. + * @param {String} data - validation data + * @returns {Boolean} validation. +*/ + +function _validateCriteriaConditions(condition, data) { + return new Promise(async (resolve, reject) => { + try { + let result = false; + if ( !condition.function || condition.function == "" ) { + if ( condition.scope == CONSTANTS.common.PROJECT ){ + + if ( condition.key == "completedDate") { + let comparableDates = UTILS.createComparableDates( data[condition.key], condition.value ); + data[condition.key] = comparableDates.dateOne; + condition.value = comparableDates.dateTwo; + } + result = UTILS.operatorValidation( data[condition.key], condition.value, condition.operator ); + + } + } else { + try { + let valueFromProject = 0; + // if: condition is in scope of project and contains a function to check + if ( condition.scope == CONSTANTS.common.PROJECT ) { + valueFromProject = UTILS.noOfElementsInArray( data[condition.key], condition.filter ); + } else if ( condition.scope == CONSTANTS.common.TASK_ATTACHMENT ){ + // for task attachment validatiion _id of specific task or "all" key should be passed in an array called taskDetails + let tasksAttachments = []; + let projectTasks = data.tasks; + + if ( projectTasks && projectTasks.length > 0 && condition.taskDetails.length > 0 && condition.taskDetails[0] == "all" ) { + + for ( let tasksIndex = 0; tasksIndex < projectTasks.length; tasksIndex++ ) { + + if ( projectTasks[tasksIndex][condition.key] && projectTasks[tasksIndex][condition.key].length > 0 ) + { + tasksAttachments.push(...projectTasks[tasksIndex][condition.key]) + } + } + + } else if ( projectTasks && projectTasks.length > 0 && condition.taskDetails.length > 0 ) { + + // specific task Id or Ids are passed for attachment validation + for ( let tasksIndex = 0; tasksIndex < projectTasks.length; tasksIndex++ ) { + for ( let taskDetailsPointer = 0; taskDetailsPointer < condition.taskDetails.length; taskDetailsPointer++ ) { + // get attachments data of specified task/ tasks + if ( projectTasks[tasksIndex]._id == condition.taskDetails[taskDetailsPointer] && projectTasks[tasksIndex][condition.key] && projectTasks[tasksIndex][condition.key].length > 0 ) { + tasksAttachments.push(...projectTasks[tasksIndex][condition.key]) + } + } + + } + + } else { + return resolve(result) + } + if ( !tasksAttachments.length > 0 ) { + return resolve(result) + } + valueFromProject = UTILS.noOfElementsInArray( tasksAttachments, condition.filter ); + } + result = UTILS.operatorValidation( valueFromProject, condition.value, condition.operator ); + + } catch (fnError) { + return resolve(result) + } + } + return resolve(result); + } catch (error) { + return resolve({ + message: error.message, + success: false, + status: + error.status ? + error.status : HTTP_STATUS_CODE['internal_server_error'].status + }) + } + }) +} +/** + * _criteriaExpressionValidation + * @method + * @name _criteriaExpressionValidation + * @param {String} expression - criteria expression + * @param {Array} keys - condition keys + * @param {Array} result - condition result + * @returns {Boolean} validation result. +*/ + +function _criteriaExpressionValidation(expression, keys, result) { + return new Promise(async (resolve, reject) => { + try { + + if( expression == "" || + !keys.length > 0 || + !result.length > 0 || + keys.length != result.length ) { + return resolve(false); + } + for ( let pointerToKeys = 0; pointerToKeys < keys.length; pointerToKeys++ ) { + expression = expression.replace(keys[pointerToKeys],result[pointerToKeys].toString()) + } + let evalResult = eval(expression) + + return resolve(evalResult); + + } catch (error) { + return resolve(false); + } + }) +} + + + + + + diff --git a/module/userProjects/helper.js b/module/userProjects/helper.js index 04f0a799..27774713 100644 --- a/module/userProjects/helper.js +++ b/module/userProjects/helper.js @@ -25,6 +25,8 @@ const userProfileService = require(GENERICS_FILES_PATH + "/services/users"); const solutionsHelper = require(MODULES_BASE_PATH + "/solutions/helper"); const certificateTemplateQueries = require(DB_QUERY_BASE_PATH + "/certificateTemplates"); const certificateService = require(GENERICS_FILES_PATH + "/services/certificate"); +const certificateValidationsHelper = require(MODULES_BASE_PATH + "/certificateValidations/helper"); +const _ = require("lodash"); /** * UserProjectsHelper @@ -371,15 +373,9 @@ module.exports = class UserProjectsHelper { status: HTTP_STATUS_CODE['bad_request'].status } } - - // push to kafka only if project is submitted and certificate key is present - if ( projectUpdated.status == CONSTANTS.common.SUBMITTED_STATUS && - projectUpdated.certificate && - Object.keys(projectUpdated.certificate).length > 0 - ) { - await kafkaProducersHelper.pushProjectToKafka(projectUpdated); - } - + // push project details to kafka + await kafkaProducersHelper.pushProjectToKafka(projectUpdated); + return resolve({ success: true, message: CONSTANTS.apiResponses.USER_PROJECT_UPDATED, @@ -1172,12 +1168,7 @@ module.exports = class UserProjectsHelper { // create certificate object and add data if certificate template is present. if ( certificateTemplateDetails.length > 0 ) { - projectCreation.data["certificate"] = { - templateId : certificateTemplateDetails[0]._id, - templateUrl : certificateTemplateDetails[0].templateUrl, - status : certificateTemplateDetails[0].status, - criteria : certificateTemplateDetails[0].criteria - } + projectCreation.data["certificate"] = _.pick(certificateTemplateDetails[0], ['_id', 'templateUrl', 'status', 'criteria']); } } @@ -2223,12 +2214,7 @@ module.exports = class UserProjectsHelper { // create certificate object and add data if certificate template is present. if ( certificateTemplateDetails.length > 0 ) { - libraryProjects.data["certificate"] = { - templateId : certificateTemplateDetails[0]._id, - templateUrl : certificateTemplateDetails[0].templateUrl, - status : certificateTemplateDetails[0].status, - criteria : certificateTemplateDetails[0].criteria, - } + libraryProjects.data["certificate"] = _.pick(certificateTemplateDetails[0], ['_id', 'templateUrl', 'status', 'criteria']); } delete libraryProjects.data.solutionInformation.certificateTemplateId; } @@ -2339,54 +2325,6 @@ module.exports = class UserProjectsHelper { }) } - /** - * validate certificate criteria. - * @method - * @name criteriaValidation - * @param {Object} data - project data for certificate creation - * @returns - */ - - static criteriaValidation(data) { - return new Promise(async (resolve, reject) => { - try { - let criteria = data.certificate.criteria; - let validationResult = []; - let validationMessage = ""; - let validationExpression = criteria.expression - if ( criteria.conditions && Object.keys(criteria.conditions).length > 0 ) { - let conditions = criteria.conditions; - let conditionKeys = Object.keys(conditions) - - for ( let index = 0; index < conditionKeys.length; index++ ) { - // correntCondition contain the prefinal level data - let currentCondition = conditions[conditionKeys[index]]; - - //now pass expression and validation scope to another function which will start the validation procedure - let validation = await _subCriteriaValidation( currentCondition.conditions, currentCondition.expression, data ); - - validationResult.push(validation.success); - ( validation.success == false ) ? validationMessage = validationMessage + " " + currentCondition.validationText : ""; - } - let criteriaValidation = await _criteriaExpressionValidation( validationExpression, conditionKeys, validationResult ) - return resolve({ - success: criteriaValidation, - message: ( criteriaValidation == false ) ? validationMessage : CONSTANTS.common.PROJECT_CERTIFICATE_GENERATED_SUCCESSFULLY - }); - } - return resolve({ - success: false - }) - } catch (error) { - return resolve({ - success: false, - message: error.message, - data: {} - }); - } - }) - } - /** * generate project certificate. * @method @@ -2400,7 +2338,7 @@ module.exports = class UserProjectsHelper { try { // if eligible key is not there check criteria for validation if ( !data.certificate.eligible ) { - let validateCriteria = await this.criteriaValidation(data) + let validateCriteria = await certificateValidationsHelper.criteriaValidation(data) if ( validateCriteria.success ) { data.certificate.eligible = true; } else { @@ -2479,6 +2417,7 @@ module.exports = class UserProjectsHelper { }; const certificateDetails = await certificateService.createCertificate( certificateData ); + if ( !certificateDetails.success || !certificateDetails.data || !certificateDetails.data.ProjectCertificate ) { return resolve({ success:false @@ -2734,163 +2673,6 @@ module.exports = class UserProjectsHelper { }; -/** - * _subCriteriaValidation. - * @method - * @name _subCriteriaValidation - * @param {Object} conditions - condition data. - * @param {String} expression - validation expression - * @returns {Boolean} validation. -*/ - -function _subCriteriaValidation(conditions, expression, data) { - return new Promise(async (resolve, reject) => { - try { - let conditionKeys = Object.keys(conditions) - let validationResult = []; - - for ( let index = 0; index < conditionKeys.length; index++ ) { - let currentCondition = conditions[conditionKeys[index]]; - // correntCondition contain the prefinal level data - //now pass expression and validation scope to another function which will start the validation procedure - let validation = await _validateCriteriaConditions( currentCondition, data ); - validationResult.push(validation); - } - - let subcriteriaValidation = await _criteriaExpressionValidation( expression, conditionKeys, validationResult ) - return resolve({ - success: subcriteriaValidation - }); - - } catch (error) { - return resolve({ - message: error.message, - success: false, - status: - error.status ? - error.status : HTTP_STATUS_CODE['internal_server_error'].status - }) - } - }) -} - -/** - * _validateCriteriaConditions. - * @method - * @name _validateCriteriaConditions - * @param {Object} condition - condition data. - * @param {String} data - validation data - * @returns {Boolean} validation. -*/ - -function _validateCriteriaConditions(condition, data) { - return new Promise(async (resolve, reject) => { - try { - let result = false; - if ( !condition.function || condition.function == "" ) { - if ( condition.scope == CONSTANTS.common.PROJECT ){ - - // let expression = data[condition.key] + condition.operator + condition.value; - if ( condition.key == "completedDate") { - let comparableDates = UTILS.createComparableDates( data[condition.key], condition.value ); - data[condition.key] = comparableDates.dateOne; - condition.value = comparableDates.dateTwo; - } - result = UTILS.operatorValidation( data[condition.key], condition.value, condition.operator ); - - } - } else { - try { - let valueFromProject = 0; - // if: condition is in scope of project and contains a function to check - if ( condition.scope == CONSTANTS.common.PROJECT ) { - valueFromProject = UTILS.getAttachmentCount( data[condition.key], condition.filter ); - } else if ( condition.scope == CONSTANTS.common.TASK_ATTACHMENT ){ - // for task attachment validatiion _id of specific task or "all" key should be passed in an array called taskDetails - let tasksAttachments = []; - let projectTasks = data.tasks; - - if ( projectTasks && projectTasks.length > 0 && condition.taskDetails.length > 0 && condition.taskDetails[0] == "all" ) { - - for ( let tasksIndex = 0; tasksIndex < projectTasks.length; tasksIndex++ ) { - - if ( projectTasks[tasksIndex][condition.key] && projectTasks[tasksIndex][condition.key].length > 0 ) - { - tasksAttachments.push(...projectTasks[tasksIndex][condition.key]) - } - } - - } else if ( projectTasks && projectTasks.length > 0 && condition.taskDetails.length > 0 ) { - - // specific task Id or Ids are passed for attachment validation - for ( let tasksIndex = 0; tasksIndex < projectTasks.length; tasksIndex++ ) { - for ( let taskDetailsPointer = 0; taskDetailsPointer < condition.taskDetails.length; taskDetailsPointer++ ) { - // get attachments data of specified task/ tasks - if ( projectTasks[tasksIndex]._id == condition.taskDetails[taskDetailsPointer] && projectTasks[tasksIndex][condition.key] && projectTasks[tasksIndex][condition.key].length > 0 ) { - tasksAttachments.push(...projectTasks[tasksIndex][condition.key]) - } - } - - } - - } else { - return resolve(result) - } - if ( !tasksAttachments.length > 0 ) { - return resolve(result) - } - valueFromProject = UTILS.getAttachmentCount( tasksAttachments, condition.filter ); - } - result = UTILS.operatorValidation( valueFromProject, condition.value, condition.operator ); - - } catch (fnError) { - return resolve(result) - } - } - return resolve(result); - } catch (error) { - return resolve({ - message: error.message, - success: false, - status: - error.status ? - error.status : HTTP_STATUS_CODE['internal_server_error'].status - }) - } - }) -} -/** - * _criteriaExpressionValidation - * @method - * @name _criteriaExpressionValidation - * @param {String} expression - criteria expression - * @param {Array} keys - condition keys - * @param {Array} result - condition result - * @returns {Boolean} validation result. -*/ - -function _criteriaExpressionValidation(expression, keys, result) { - return new Promise(async (resolve, reject) => { - try { - - if( expression == "" || - !keys.length > 0 || - !result.length > 0 || - keys.length != result.length ) { - return resolve(false); - } - for ( let pointerToKeys = 0; pointerToKeys < keys.length; pointerToKeys++ ) { - expression = expression.replace(keys[pointerToKeys],result[pointerToKeys].toString()) - } - let evalResult = eval(expression) - - return resolve(evalResult); - - } catch (error) { - return resolve(false); - } - }) -} /** * Project information. From 2b3c85c776aca28ec8bce3d0f0cb177fe19ba1fc Mon Sep 17 00:00:00 2001 From: VISHNUDAS-tunerlabse Date: Fri, 28 Oct 2022 17:52:20 +0530 Subject: [PATCH 16/92] schema change --- models/certificateTemplates.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/models/certificateTemplates.js b/models/certificateTemplates.js index e65899f0..a6389ce0 100644 --- a/models/certificateTemplates.js +++ b/models/certificateTemplates.js @@ -9,8 +9,8 @@ module.exports = { }, solutionId: { type : "ObjectId", - index : true, - unique: true + unique: true, + index : true }, programId: "ObjectId", criteria: { From 80226487bbeaf7522d875b313ff55c45b23cc193 Mon Sep 17 00:00:00 2001 From: VISHNUDAS-tunerlabse Date: Fri, 28 Oct 2022 18:47:36 +0530 Subject: [PATCH 17/92] transactionId 1- removed --- module/userProjects/helper.js | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/module/userProjects/helper.js b/module/userProjects/helper.js index 27774713..b7433714 100644 --- a/module/userProjects/helper.js +++ b/module/userProjects/helper.js @@ -2433,9 +2433,15 @@ module.exports = class UserProjectsHelper { certificateDetails.data.ProjectCertificate.transactionId !== "" ) { let transactionIdvalue = certificateDetails.data.ProjectCertificate.transactionId; - transactionIdvalue = transactionIdvalue.split("1-"); - let transactionIdData = transactionIdvalue[1]; - updateObject["$set"]["certificate.transactionId"] = transactionIdvalue[1]; + const first2 = transactionIdvalue.slice(0, 2); + + if ( first2 === "1-" ) { + transactionIdvalue = transactionIdvalue.split(/1-(.*)/s) + updateObject["$set"]["certificate.transactionId"] = transactionIdvalue[1]; + } else { + updateObject["$set"]["certificate.transactionId"] = transactionIdvalue; + } + } if ( certificateDetails.data.ProjectCertificate.osid && From fb890b3cc2f4ed0a6233632c9fcf30578d2d90e9 Mon Sep 17 00:00:00 2001 From: VISHNUDAS-tunerlabse Date: Mon, 31 Oct 2022 09:18:19 +0530 Subject: [PATCH 18/92] schema change --- models/certificateTemplates.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/models/certificateTemplates.js b/models/certificateTemplates.js index a6389ce0..22751676 100644 --- a/models/certificateTemplates.js +++ b/models/certificateTemplates.js @@ -2,15 +2,19 @@ module.exports = { name: "certificateTemplates", schema: { templateUrl: String, - issuer: Object, + issuer: { + type : Object, + required : true + }, status: { type : String, - required : true + required : true, + default : "ACTIVE" }, solutionId: { type : "ObjectId", - unique: true, - index : true + index : true, + unique : true }, programId: "ObjectId", criteria: { From 7f61f91e6d5df57e3cbbfb1d08d7aea6c8c5b6f2 Mon Sep 17 00:00:00 2001 From: VISHNUDAS-tunerlabse Date: Wed, 16 Nov 2022 18:00:49 +0530 Subject: [PATCH 19/92] review resolves --- .env.sample | 5 +++ envVariables.js | 22 ++++++++++-- generics/constants/api-responses.js | 7 +++- generics/services/certificate.js | 2 +- models/certificateTemplates.js | 6 +++- module/certificateValidations/helper.js | 22 +++++++----- module/userProjects/helper.js | 47 +++++++++++++------------ 7 files changed, 76 insertions(+), 35 deletions(-) diff --git a/.env.sample b/.env.sample index 2e79dd8d..7ad54dc5 100644 --- a/.env.sample +++ b/.env.sample @@ -38,3 +38,8 @@ SERVICE_NAME = ml-project-service # sunbird-rc service CERTIFICATE_SERVICE_URL = http://registry-service:8081 // sunbird-RC registry service URL +#CERTIFICATE_ISSUER_KID +CERTIFICATE_ISSUER_KID = "1-de2ed8d1-e8d8-40a9-b1ba-7694a16a4c8d" // This issuer Kid is used in sunbird RC end + +PROJECT_CERTIFICATE_ON_OFF = "ON/OFF" // Project certificate enable or disable flag + diff --git a/envVariables.js b/envVariables.js index a2da8cfb..8e520e42 100644 --- a/envVariables.js +++ b/envVariables.js @@ -48,7 +48,25 @@ let enviromentVariables = { "CERTIFICATE_SERVICE_URL" : { "message" : "certificate service base url", "optional" : true, - "default" : "http://registry-service:8081" + "default" : "http://registry-service:8081", + "requiredIf" : { + "key": "PROJECT_CERTIFICATE_ON_OFF", + "operator" : "EQUALS", + "value" : "ON" + } + }, + "PROJECT_CERTIFICATE_ON_OFF" : { + "message" : "Enable/Disable project certification", + "optional" : false + }, + "CERTIFICATE_ISSUER_KID" : { + "message" : "Required certificate issuer kid", + "optional" : true, + "requiredIf" : { + "key": "PROJECT_CERTIFICATE_ON_OFF", + "operator" : "EQUALS", + "value" : "ON" + } } } @@ -62,7 +80,7 @@ module.exports = function() { }; let keyCheckPass = true; - + let validRequiredIfOperators = ["EQUALS","NOT_EQUALS"] if(enviromentVariables[eachEnvironmentVariable].optional === true && enviromentVariables[eachEnvironmentVariable].requiredIf diff --git a/generics/constants/api-responses.js b/generics/constants/api-responses.js index 680092c8..d212f216 100644 --- a/generics/constants/api-responses.js +++ b/generics/constants/api-responses.js @@ -127,5 +127,10 @@ module.exports = { "SOLUTION_ID_AND_USERPROFILE_REQUIRED": "Required solution Id and userProfile", "PROJECT_WITH_CERTIFICATE_NOT_FOUND": "No certification project found for user", "PROJECT_CERTIFICATE_GENERATED" : "Successfully generated project certificate", - "TRANSACTION_ID_AND_OSID_REQUIRED" : "Required transactionId and osid" + "TRANSACTION_ID_AND_OSID_REQUIRED" : "Required transactionId and osid", + "PROJECT_CERTIFICATE_GENERATED_ONCE" : "Certificate generated once", + "DOWNLOADABLE_URL_NOT_FOUND" : "Failed to generate downloadable URL", + "CERTIFICATE_TEMPLATE_NOT_FOUND" : "Certificate template details not found", + "CERTIFICATE_GENERATION_FAILED" : "Certificate generation failed", + "NOT_ELIGIBLE_FOR_CERTIFICATE" : "Project is not eligible for certificate" }; diff --git a/generics/services/certificate.js b/generics/services/certificate.js index 60238069..15ba5970 100644 --- a/generics/services/certificate.js +++ b/generics/services/certificate.js @@ -8,7 +8,7 @@ //dependencies const request = require('request'); const CERTIFICATE_SERVICE_URL = process.env.CERTIFICATE_SERVICE_URL; -const ML_PROJECT_URL = `https://${process.env.SERVICE_NAME}:${process.env.APPLICATION_PORT}`; +const ML_PROJECT_URL = `http://${process.env.SERVICE_NAME}:${process.env.APPLICATION_PORT}`; /** * Project certificate creation diff --git a/models/certificateTemplates.js b/models/certificateTemplates.js index 22751676..0f5ba2d7 100644 --- a/models/certificateTemplates.js +++ b/models/certificateTemplates.js @@ -16,7 +16,11 @@ module.exports = { index : true, unique : true }, - programId: "ObjectId", + programId: { + type : "ObjectId", + index : true, + required : true + }, criteria: { type : Object, required : true diff --git a/module/certificateValidations/helper.js b/module/certificateValidations/helper.js index e550079c..fc9a7a32 100644 --- a/module/certificateValidations/helper.js +++ b/module/certificateValidations/helper.js @@ -25,7 +25,7 @@ module.exports = class certificateValidationsHelper { static criteriaValidation(data) { return new Promise(async (resolve, reject) => { try { - let criteria = data.certificate.criteria; + let criteria = data.certificate.criteria; // criteria conditions for certificate let validationResult = []; let validationMessage = ""; let validationExpression = criteria.expression @@ -43,6 +43,7 @@ module.exports = class certificateValidationsHelper { validationResult.push(validation.success); ( validation.success == false ) ? validationMessage = validationMessage + " " + currentCondition.validationText : ""; } + // validate criteria using defined expression let criteriaValidation = await _criteriaExpressionValidation( validationExpression, conditionKeys, validationResult ) return resolve({ success: criteriaValidation, @@ -77,7 +78,7 @@ function _subCriteriaValidation(conditions, expression, data) { try { let conditionKeys = Object.keys(conditions) let validationResult = []; - + // loop throug conditions of subcriterias for ( let index = 0; index < conditionKeys.length; index++ ) { let currentCondition = conditions[conditionKeys[index]]; // correntCondition contain the prefinal level data @@ -85,7 +86,7 @@ function _subCriteriaValidation(conditions, expression, data) { let validation = await _validateCriteriaConditions( currentCondition, data ); validationResult.push(validation); } - + // validate expression let subcriteriaValidation = await _criteriaExpressionValidation( expression, conditionKeys, validationResult ) return resolve({ success: subcriteriaValidation @@ -116,14 +117,15 @@ function _validateCriteriaConditions(condition, data) { return new Promise(async (resolve, reject) => { try { let result = false; - if ( !condition.function || condition.function == "" ) { + if ( !condition.function || condition.function == "" ) { if ( condition.scope == CONSTANTS.common.PROJECT ){ - + // if validation is on completedDate if ( condition.key == "completedDate") { let comparableDates = UTILS.createComparableDates( data[condition.key], condition.value ); data[condition.key] = comparableDates.dateOne; condition.value = comparableDates.dateTwo; } + // validate prject value with condition value result = UTILS.operatorValidation( data[condition.key], condition.value, condition.operator ); } @@ -132,16 +134,17 @@ function _validateCriteriaConditions(condition, data) { let valueFromProject = 0; // if: condition is in scope of project and contains a function to check if ( condition.scope == CONSTANTS.common.PROJECT ) { + // get count of attachments at project level valueFromProject = UTILS.noOfElementsInArray( data[condition.key], condition.filter ); } else if ( condition.scope == CONSTANTS.common.TASK_ATTACHMENT ){ // for task attachment validatiion _id of specific task or "all" key should be passed in an array called taskDetails let tasksAttachments = []; let projectTasks = data.tasks; - + // check tasks and taskDetails exists if ( projectTasks && projectTasks.length > 0 && condition.taskDetails.length > 0 && condition.taskDetails[0] == "all" ) { - + // loop through tasks to get attachments for ( let tasksIndex = 0; tasksIndex < projectTasks.length; tasksIndex++ ) { - + if ( projectTasks[tasksIndex][condition.key] && projectTasks[tasksIndex][condition.key].length > 0 ) { tasksAttachments.push(...projectTasks[tasksIndex][condition.key]) @@ -167,8 +170,10 @@ function _validateCriteriaConditions(condition, data) { if ( !tasksAttachments.length > 0 ) { return resolve(result) } + // get task attachments count valueFromProject = UTILS.noOfElementsInArray( tasksAttachments, condition.filter ); } + // validate against condition value result = UTILS.operatorValidation( valueFromProject, condition.value, condition.operator ); } catch (fnError) { @@ -207,6 +212,7 @@ function _criteriaExpressionValidation(expression, keys, result) { keys.length != result.length ) { return resolve(false); } + // generate expression string that can be evaluated for ( let pointerToKeys = 0; pointerToKeys < keys.length; pointerToKeys++ ) { expression = expression.replace(keys[pointerToKeys],result[pointerToKeys].toString()) } diff --git a/module/userProjects/helper.js b/module/userProjects/helper.js index b7433714..cc9218f7 100644 --- a/module/userProjects/helper.js +++ b/module/userProjects/helper.js @@ -2336,8 +2336,10 @@ module.exports = class UserProjectsHelper { static generateCertificate(data) { return new Promise(async (resolve, reject) => { try { + // if eligible key is not there check criteria for validation if ( !data.certificate.eligible ) { + // validate certificate data, checking if it passes all criteria let validateCriteria = await certificateValidationsHelper.criteriaValidation(data) if ( validateCriteria.success ) { data.certificate.eligible = true; @@ -2349,6 +2351,7 @@ module.exports = class UserProjectsHelper { let updateObject = { "$set" : {} }; + // update project certificate data updateObject["$set"]["certificate.eligible"] = data.certificate.eligible; if ( data.certificate.message && data.certificate.message !=="" ) { updateObject["$set"]["certificate.message"] = data.certificate.message; @@ -2362,9 +2365,9 @@ module.exports = class UserProjectsHelper { ); } if ( data.certificate.eligible === true && ( data.certificate.transactionId || data.certificate.osid ) ) { - return resolve( { - success: false - }); + throw { + message: CONSTANTS.apiResponses.PROJECT_CERTIFICATE_GENERATED_ONCE + }; } else { if ( data.certificate.eligible === true ) { let certificateTemplateDetails = []; @@ -2379,9 +2382,9 @@ module.exports = class UserProjectsHelper { if ( certificateTemplateDownloadableUrl.success ) { data.certificate.templateUrl = certificateTemplateDownloadableUrl.data[0].url; } else { - return resolve({ - success:false - }); + throw { + message: CONSTANTS.apiResponses.DOWNLOADABLE_URL_NOT_FOUND + }; } } if ( data.certificate.templateId && data.certificate.templateId !== "" ) { @@ -2391,12 +2394,12 @@ module.exports = class UserProjectsHelper { //certificate template data do not exists. if ( !certificateTemplateDetails.length > 0 ) { - return resolve({ - success:false - }); + throw { + message: CONSTANTS.apiResponses.CERTIFICATE_TEMPLATE_NOT_FOUND + }; } } - + certificateTemplateDetails[0].issuer.kid = process.env.CERTIFICATE_ISSUER_KID; //create certificate request body let certificateData = { recipient : { @@ -2419,9 +2422,9 @@ module.exports = class UserProjectsHelper { const certificateDetails = await certificateService.createCertificate( certificateData ); if ( !certificateDetails.success || !certificateDetails.data || !certificateDetails.data.ProjectCertificate ) { - return resolve({ - success:false - }); + throw { + message: CONSTANTS.apiResponses.CERTIFICATE_GENERATION_FAILED + }; } let updateObject = { @@ -2443,7 +2446,7 @@ module.exports = class UserProjectsHelper { } } - + // update project details certificate details if ( certificateDetails.data.ProjectCertificate.osid && certificateDetails.data.ProjectCertificate.osid !== "" ) { @@ -2462,22 +2465,22 @@ module.exports = class UserProjectsHelper { success: true }); } else { - return resolve({ - success:false - }); + throw { + message: CONSTANTS.apiResponses.USER_PROJECT_NOT_UPDATED + }; } } else { - return resolve({ - success:false - }); + throw { + message: CONSTANTS.apiResponses.NOT_ELIGIBLE_FOR_CERTIFICATE + }; } } } catch (error) { return resolve({ success: false, - message: error.message, - data: {} + message: error.message + }); } }) From 675a61936bba6657a57b8e8728072193390df255 Mon Sep 17 00:00:00 2001 From: VISHNUDAS-tunerlabse Date: Wed, 16 Nov 2022 18:13:37 +0530 Subject: [PATCH 20/92] PROJECT_CERTIFICATE_ON_OFF default value added --- envVariables.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/envVariables.js b/envVariables.js index 8e520e42..66214318 100644 --- a/envVariables.js +++ b/envVariables.js @@ -57,7 +57,8 @@ let enviromentVariables = { }, "PROJECT_CERTIFICATE_ON_OFF" : { "message" : "Enable/Disable project certification", - "optional" : false + "optional" : false, + "default" : "ON" }, "CERTIFICATE_ISSUER_KID" : { "message" : "Required certificate issuer kid", From fd1348a7018c3a54966185bf7f998e3c23c6e1e4 Mon Sep 17 00:00:00 2001 From: VISHNUDAS-tunerlabse Date: Fri, 18 Nov 2022 13:33:10 +0530 Subject: [PATCH 21/92] sunbird internal call for issuer-kid added --- envVariables.js | 9 ----- generics/constants/api-responses.js | 3 +- generics/constants/endpoints.js | 3 +- generics/services/certificate.js | 54 ++++++++++++++++++++++++++++- module/userProjects/helper.js | 9 ++++- 5 files changed, 65 insertions(+), 13 deletions(-) diff --git a/envVariables.js b/envVariables.js index 66214318..bafea4e3 100644 --- a/envVariables.js +++ b/envVariables.js @@ -59,15 +59,6 @@ let enviromentVariables = { "message" : "Enable/Disable project certification", "optional" : false, "default" : "ON" - }, - "CERTIFICATE_ISSUER_KID" : { - "message" : "Required certificate issuer kid", - "optional" : true, - "requiredIf" : { - "key": "PROJECT_CERTIFICATE_ON_OFF", - "operator" : "EQUALS", - "value" : "ON" - } } } diff --git a/generics/constants/api-responses.js b/generics/constants/api-responses.js index d212f216..0ce30788 100644 --- a/generics/constants/api-responses.js +++ b/generics/constants/api-responses.js @@ -132,5 +132,6 @@ module.exports = { "DOWNLOADABLE_URL_NOT_FOUND" : "Failed to generate downloadable URL", "CERTIFICATE_TEMPLATE_NOT_FOUND" : "Certificate template details not found", "CERTIFICATE_GENERATION_FAILED" : "Certificate generation failed", - "NOT_ELIGIBLE_FOR_CERTIFICATE" : "Project is not eligible for certificate" + "NOT_ELIGIBLE_FOR_CERTIFICATE" : "Project is not eligible for certificate", + "ISSUER_KID_NOT_FOUND" : "Failed to fetch certificate issuer kid" }; diff --git a/generics/constants/endpoints.js b/generics/constants/endpoints.js index 109dfaf2..14509493 100644 --- a/generics/constants/endpoints.js +++ b/generics/constants/endpoints.js @@ -50,5 +50,6 @@ module.exports = { GET_LOCATION_DATA : "/v1/location/search", CERTIFICATE_CREATE : "/api/v1/ProjectCertificate", PROJECT_CERTIFICATE_API_CALLBACK : "/v1/userProject/certificateCallback", - USER_READ_PRIVATE : "/private/user/v1/read" // !Caution: End point for reading user details without token. Do not use for public work flow + USER_READ_PRIVATE : "/private/user/v1/read", // !Caution: End point for reading user details without token. Do not use for public work flow + GET_CERTIFICATE_KID : "/api/v1/PublicKey/search" }; diff --git a/generics/services/certificate.js b/generics/services/certificate.js index 15ba5970..4ba22669 100644 --- a/generics/services/certificate.js +++ b/generics/services/certificate.js @@ -60,6 +60,58 @@ const createCertificate = function (bodyData) { }) } +/** + * Project certificate issuer-kid + * @function + * @name getCertificateIssuerKid + * @returns {JSON} - Certificate issuer kid details. +*/ + +const getCertificateIssuerKid = function () { + return new Promise(async (resolve, reject) => { + try { + let issuerKidUrl = + CERTIFICATE_SERVICE_URL + CONSTANTS.endpoints.GET_CERTIFICATE_KID; + let bodyData = {"filters": {}}; + + const options = { + headers : { + "Content-Type": "application/json" + }, + json : bodyData + }; + request.post(issuerKidUrl,options,getKidCallback); + + function getKidCallback(err, data) { + let result = { + success : true + }; + + if (err) { + result.success = false; + } else { + let response = data.body; + if( response.length > 0 && response[0].osid && response[0].osid !== "" ) { + result["data"] = response[0].osid; + } else { + result.success = false; + } + } + return resolve(result); + } + setTimeout(function () { + return resolve (result = { + success : false + }); + }, CONSTANTS.common.SERVER_TIME_OUT); + + } catch (error) { + return reject(error); + } + }) +} + module.exports = { - createCertificate : createCertificate + createCertificate : createCertificate, + getCertificateIssuerKid : getCertificateIssuerKid } \ No newline at end of file diff --git a/module/userProjects/helper.js b/module/userProjects/helper.js index cc9218f7..77dc6234 100644 --- a/module/userProjects/helper.js +++ b/module/userProjects/helper.js @@ -2399,7 +2399,14 @@ module.exports = class UserProjectsHelper { }; } } - certificateTemplateDetails[0].issuer.kid = process.env.CERTIFICATE_ISSUER_KID; + // get certificate issuer kid from sunbird-RC + let kidData = await certificateService.getCertificateIssuerKid(); + if( !kidData.success ) { + throw { + message: CONSTANTS.apiResponses.ISSUER_KID_NOT_FOUND + } + } + certificateTemplateDetails[0].issuer.kid = kidData.data; //create certificate request body let certificateData = { recipient : { From c4c6a42e8592c949233716721ef335e3e520a60f Mon Sep 17 00:00:00 2001 From: VISHNUDAS-tunerlabse Date: Sun, 20 Nov 2022 19:34:38 +0530 Subject: [PATCH 22/92] review changes --- .env.sample | 3 - envVariables.js | 19 +- generics/helpers/utils.js | 13 -- models/projects.js | 6 +- module/userProjects/helper.js | 371 ++++++++++++++++++++-------------- 5 files changed, 243 insertions(+), 169 deletions(-) diff --git a/.env.sample b/.env.sample index 7ad54dc5..3692dcec 100644 --- a/.env.sample +++ b/.env.sample @@ -38,8 +38,5 @@ SERVICE_NAME = ml-project-service # sunbird-rc service CERTIFICATE_SERVICE_URL = http://registry-service:8081 // sunbird-RC registry service URL -#CERTIFICATE_ISSUER_KID -CERTIFICATE_ISSUER_KID = "1-de2ed8d1-e8d8-40a9-b1ba-7694a16a4c8d" // This issuer Kid is used in sunbird RC end - PROJECT_CERTIFICATE_ON_OFF = "ON/OFF" // Project certificate enable or disable flag diff --git a/envVariables.js b/envVariables.js index bafea4e3..db946621 100644 --- a/envVariables.js +++ b/envVariables.js @@ -8,6 +8,7 @@ const Log = require("log"); let log = new Log("debug"); let table = require("cli-table"); +const certificateService = require(GENERICS_FILES_PATH + "/services/certificate"); let tableData = new table(); @@ -129,16 +130,30 @@ module.exports = function() { tableObj[eachEnvironmentVariable] = `FAILED - ${eachEnvironmentVariable} is required`; } } - tableData.push(tableObj); }) log.info(tableData.toString()); - + getKid(); return { success : success } } +async function getKid(){ + if ( enviromentVariables["PROJECT_CERTIFICATE_ON_OFF"] && + enviromentVariables["PROJECT_CERTIFICATE_ON_OFF"].default && + enviromentVariables["PROJECT_CERTIFICATE_ON_OFF"].default === "ON" + ) { + // get certificate issuer kid from sunbird-RC + let kidData = await certificateService.getCertificateIssuerKid(); + if( !kidData.success ) { + console.log("Server stoped . Failed to set certificate issuer Kid value") + process.exit(); + } + global.CERTIFICATE_ISSUER_KID = kidData.data + } +}; + diff --git a/generics/helpers/utils.js b/generics/helpers/utils.js index 11834c61..900d00e3 100644 --- a/generics/helpers/utils.js +++ b/generics/helpers/utils.js @@ -265,18 +265,6 @@ function checkValidUUID(uuids) { return validateUUID; } -/** - * convert string to upperCase. - * @function - * @name upperCase - * @param {String} str - * @returns {String} returns a upperCase string. ex:hello , o/p: HELLO -*/ - -function upperCase(str) { - return str.toUpperCase() -} - /** * make dates comparable * @function @@ -386,7 +374,6 @@ module.exports = { revertProjectStatus:revertProjectStatus, revertStatusorNot:revertStatusorNot, checkValidUUID : checkValidUUID, - upperCase : upperCase, createComparableDates : createComparableDates, noOfElementsInArray : noOfElementsInArray, operatorValidation : operatorValidation diff --git a/models/projects.js b/models/projects.js index b9f150d7..483602ec 100644 --- a/models/projects.js +++ b/models/projects.js @@ -153,7 +153,11 @@ module.exports = { eligible : Boolean, message : String, issuedOn : Date, - criteria : Object + criteria : Object, + transactionIdCreatedAt : Date, + reIssuedAt : Date, + prevTransactionId : String, + prevOsid : String } }, compoundIndex: [ diff --git a/module/userProjects/helper.js b/module/userProjects/helper.js index 77dc6234..6e0c712e 100644 --- a/module/userProjects/helper.js +++ b/module/userProjects/helper.js @@ -106,6 +106,7 @@ module.exports = class UserProjectsHelper { static sync(projectId, lastDownloadedAt, data, userId, userToken, appName = "", appVersion = "") { return new Promise(async (resolve, reject) => { try { + const userProject = await projectQueries.projectDocument({ _id: projectId, userId: userId @@ -2333,29 +2334,199 @@ module.exports = class UserProjectsHelper { * @returns {JSON} certificate details. */ - static generateCertificate(data) { + static generateCertificate(data) { return new Promise(async (resolve, reject) => { try { + + // check eligibility of project for certificate creation + let eligibility = await this.checkCertificateEligibility(data); + if (!eligibility ){ + throw { + message: CONSTANTS.apiResponses.NOT_ELIGIBLE_FOR_CERTIFICATE + }; + } + + // create payload for certificate generation + const certificateData = await this.createCertificatePayload(data); + + // call sunbird-RC to create certificate for project + const certificate = await this.createCertificate(certificateData) - // if eligible key is not there check criteria for validation - if ( !data.certificate.eligible ) { - // validate certificate data, checking if it passes all criteria - let validateCriteria = await certificateValidationsHelper.criteriaValidation(data) - if ( validateCriteria.success ) { - data.certificate.eligible = true; + return resolve(certificate); + + } catch (error) { + return resolve({ + success: false, + message: error.message + + }); + } + }) + } + + /** + * check project eligibility for certificate. + * @method + * @name checkCertificateEligibility + * @param {Object} data - project data for certificate creation data. + * @returns {Boolean} certificate eligibilty status. + */ + + static checkCertificateEligibility(data) { + return new Promise(async (resolve, reject) => { + try { + let eligible = false; + let updateObject = { + "$set" : {} + }; + // validate certificate data, checking if it passes all criteria + let validateCriteria = await certificateValidationsHelper.criteriaValidation(data) + if ( validateCriteria.success ) { + eligible = true; + } else { + updateObject["$set"]["certificate.message"] = validateCriteria.message; + } + updateObject["$set"]["certificate.eligible"] = eligible; + + // update project certificate data + await projectQueries.findOneAndUpdate( + { + _id: data._id + }, + updateObject + ); + + return resolve(eligible); + } catch (error) { + return resolve({ + success: false, + message: error.message + + }); + } + }) + } + + /** + * createCertificatePayload. + * @method + * @name createCertificatePayload + * @param {Object} data - project data for certificate creation data. + * @returns {Object} payload for certificate creation. + */ + + static createCertificatePayload(data) { + return new Promise(async (resolve, reject) => { + try { + + // get downloadable url for certificate template + if ( data.certificate.templateUrl && data.certificate.templateUrl !== "" ) { + let certificateTemplateDownloadableUrl = + await coreService.getDownloadableUrl( + { + filePaths: [data.certificate.templateUrl] + } + ); + if ( certificateTemplateDownloadableUrl.success ) { + data.certificate.templateUrl = certificateTemplateDownloadableUrl.data[0].url; } else { - data.certificate.eligible = false; - data.certificate.message = validateCriteria.message + throw { + message: CONSTANTS.apiResponses.DOWNLOADABLE_URL_NOT_FOUND + }; } } + + let certificateTemplateDetails =[]; + if ( data.certificate.templateId && data.certificate.templateId !== "" ) { + certificateTemplateDetails = await certificateTemplateQueries.certificateTemplateDocument({ + _id : data.certificate.templateId + },["issuer","solutionId","programId"]); + + //certificate template data do not exists. + if ( !certificateTemplateDetails.length > 0 ) { + throw { + message: CONSTANTS.apiResponses.CERTIFICATE_TEMPLATE_NOT_FOUND + }; + } + } + + //create certificate request body + let certificateData = { + recipient : { + id : data.userId, + name : data.userProfile.userName, + type : data.userProfile.userType + }, + templateUrl : data.certificate.templateUrl, + issuer : CERTIFICATE_ISSUER_KID, + status : data.certificate.status.toUpperCase(), + projectId : (data._id).toString(), + projectName : data.title, + programId : (certificateTemplateDetails[0].programId).toString(), + programName : ( data.programInformation && data.programInformation.name ) ? data.programInformation.name : "", + solutionId : (certificateTemplateDetails[0].solutionId).toString(), + solutionName : ( data.solutionInformation && data.solutionInformation.name ) ? data.solutionInformation.name : "", + completedDate : data.completedDate + }; + return resolve(certificateData); + + } catch (error) { + return resolve({ + success: false, + message: error.message + + }); + } + }) + } + + /** + * call sunbird-RC for certificate creation. + * @method + * @name createCertificate + * @param {Object} certificateData - payload for certificate creation data. + * @returns {Boolean} certificate creation status. + */ + + static createCertificate(certificateData) { + return new Promise(async (resolve, reject) => { + try { + + const certificateDetails = await certificateService.createCertificate( certificateData ); + if ( !certificateDetails.success || !certificateDetails.data || !certificateDetails.data.ProjectCertificate ) { + throw { + message: CONSTANTS.apiResponses.CERTIFICATE_GENERATION_FAILED + }; + } + let updateObject = { "$set" : {} }; - // update project certificate data - updateObject["$set"]["certificate.eligible"] = data.certificate.eligible; - if ( data.certificate.message && data.certificate.message !=="" ) { - updateObject["$set"]["certificate.message"] = data.certificate.message; + + // if transaction id is present. + if ( certificateDetails.data.ProjectCertificate.transactionId && + certificateDetails.data.ProjectCertificate.transactionId !== "" + ) { + let transactionIdvalue = certificateDetails.data.ProjectCertificate.transactionId; + const first2 = transactionIdvalue.slice(0, 2); + + if ( first2 === "1-" ) { + transactionIdvalue = transactionIdvalue.split(/1-(.*)/s) + updateObject["$set"]["certificate.transactionId"] = transactionIdvalue[1]; + } else { + updateObject["$set"]["certificate.transactionId"] = transactionIdvalue; + } + + } + + // update project details certificate details + if ( certificateDetails.data.ProjectCertificate.osid && + certificateDetails.data.ProjectCertificate.osid !== "" + ) { + updateObject["$set"]["certificate.osid"] = certificateDetails.data.ProjectCertificate.osid; } + updateObject["$set"]["certificate.transactionIdCreatedAt"] = new Date();; + if ( Object.keys(updateObject["$set"]).length > 0 ) { await projectQueries.findOneAndUpdate( { @@ -2364,125 +2535,9 @@ module.exports = class UserProjectsHelper { updateObject ); } - if ( data.certificate.eligible === true && ( data.certificate.transactionId || data.certificate.osid ) ) { - throw { - message: CONSTANTS.apiResponses.PROJECT_CERTIFICATE_GENERATED_ONCE - }; - } else { - if ( data.certificate.eligible === true ) { - let certificateTemplateDetails = []; - // get downloadable url for certificate template - if ( data.certificate.templateUrl && data.certificate.templateUrl !== "" ) { - let certificateTemplateDownloadableUrl = - await coreService.getDownloadableUrl( - { - filePaths: [data.certificate.templateUrl] - } - ); - if ( certificateTemplateDownloadableUrl.success ) { - data.certificate.templateUrl = certificateTemplateDownloadableUrl.data[0].url; - } else { - throw { - message: CONSTANTS.apiResponses.DOWNLOADABLE_URL_NOT_FOUND - }; - } - } - if ( data.certificate.templateId && data.certificate.templateId !== "" ) { - certificateTemplateDetails = await certificateTemplateQueries.certificateTemplateDocument({ - _id : data.certificate.templateId - },["issuer","solutionId","programId"]); - - //certificate template data do not exists. - if ( !certificateTemplateDetails.length > 0 ) { - throw { - message: CONSTANTS.apiResponses.CERTIFICATE_TEMPLATE_NOT_FOUND - }; - } - } - // get certificate issuer kid from sunbird-RC - let kidData = await certificateService.getCertificateIssuerKid(); - if( !kidData.success ) { - throw { - message: CONSTANTS.apiResponses.ISSUER_KID_NOT_FOUND - } - } - certificateTemplateDetails[0].issuer.kid = kidData.data; - //create certificate request body - let certificateData = { - recipient : { - id : data.userId, - name : data.userProfile.userName, - type : data.userProfile.userType - }, - templateUrl : data.certificate.templateUrl, - issuer : certificateTemplateDetails[0].issuer, - status : UTILS.upperCase(data.certificate.status), - projectId : (data._id).toString(), - projectName : data.title, - programId : (certificateTemplateDetails[0].programId).toString(), - programName : ( data.programInformation && data.programInformation.name ) ? data.programInformation.name : "", - solutionId : (certificateTemplateDetails[0].solutionId).toString(), - solutionName : ( data.solutionInformation && data.solutionInformation.name ) ? data.solutionInformation.name : "", - completedDate : data.completedDate - }; - - const certificateDetails = await certificateService.createCertificate( certificateData ); - - if ( !certificateDetails.success || !certificateDetails.data || !certificateDetails.data.ProjectCertificate ) { - throw { - message: CONSTANTS.apiResponses.CERTIFICATE_GENERATION_FAILED - }; - } - - let updateObject = { - "$set" : {} - }; - - // if transaction id is present. - if ( certificateDetails.data.ProjectCertificate.transactionId && - certificateDetails.data.ProjectCertificate.transactionId !== "" - ) { - let transactionIdvalue = certificateDetails.data.ProjectCertificate.transactionId; - const first2 = transactionIdvalue.slice(0, 2); - - if ( first2 === "1-" ) { - transactionIdvalue = transactionIdvalue.split(/1-(.*)/s) - updateObject["$set"]["certificate.transactionId"] = transactionIdvalue[1]; - } else { - updateObject["$set"]["certificate.transactionId"] = transactionIdvalue; - } - - } - // update project details certificate details - if ( certificateDetails.data.ProjectCertificate.osid && - certificateDetails.data.ProjectCertificate.osid !== "" - ) { - updateObject["$set"]["certificate.osid"] = certificateDetails.data.ProjectCertificate.osid; - } - updateObject["$set"]["certificate.eligible"] = true; - if ( Object.keys(updateObject["$set"]).length > 0 ) { - await projectQueries.findOneAndUpdate( - { - _id: data._id - }, - updateObject - ); - - return resolve( { - success: true - }); - } else { - throw { - message: CONSTANTS.apiResponses.USER_PROJECT_NOT_UPDATED - }; - } - - } else { - throw { - message: CONSTANTS.apiResponses.NOT_ELIGIBLE_FOR_CERTIFICATE - }; - } - } + return resolve( { + success: true + }); } catch (error) { return resolve({ success: false, @@ -2524,10 +2579,7 @@ module.exports = class UserProjectsHelper { { "certificate.transactionId" : transactionId }, - updateObject, - { - new: true - } + updateObject ); if ( projectDetails == null || !Object.keys(projectDetails).length > 0 ) { @@ -2567,7 +2619,7 @@ module.exports = class UserProjectsHelper { static certificates(userId) { return new Promise(async (resolve, reject) => { try { - let certificateCount = 0; + // get project details of user which have certificate. const userProject = await projectQueries.projectDocument({ userId: userId, @@ -2592,22 +2644,18 @@ module.exports = class UserProjectsHelper { message: CONSTANTS.apiResponses.PROJECT_WITH_CERTIFICATE_NOT_FOUND } } - // find certificate generated project count - for ( let projectIndex = 0; projectIndex < userProject.length; projectIndex++ ) { - if ( userProject[projectIndex].certificate && - userProject[projectIndex].certificate.osid && - userProject[projectIndex].certificate.osid !== "" - ) { - certificateCount++; - } - } + + let count = _.countBy(userProject, (rec) => { + return (rec.certificate && rec.certificate.osid && rec.certificate.osid !== "" )? 'generated': 'notGenerated'; + }); + return resolve({ success: true, message: CONSTANTS.apiResponses.PROJECTS_FETCHED, data : { data : userProject, count : userProject.length, - certificateCount : certificateCount + certificateCount : count.generated } }); @@ -2646,6 +2694,9 @@ module.exports = class UserProjectsHelper { message: CONSTANTS.apiResponses.USER_PROJECT_NOT_FOUND }; } + let updateObject = { + "$set" : {} + }; // fetch user data using userId of project and calling the profile API let userProfileData = await userProfileService.profileReadPrivate(userProject[0].userId); @@ -2662,13 +2713,33 @@ module.exports = class UserProjectsHelper { message: CONSTANTS.apiResponses.USER_PROFILE_NOT_FOUND }; } + + // create payload for certificate generation + const certificateData = await this.createCertificatePayload(userProject[0]); + + // call sunbird-RC to create certificate for project + const certificate = await this.createCertificate(certificateData); + + if ( !certificate.success ) { + throw { + message: CONSTANTS.apiResponses.CERTIFICATE_GENERATION_FAILED + }; + } + if ( userProject[0].certificate.transactionId ) { - delete userProject[0].certificate.transactionId; + updateObject["$set"]["certificate.prevTransactionId"] = userProject[0].certificate.transactionId } if ( userProject[0].certificate.osid ) { - delete userProject[0].certificate.osid; + updateObject["$set"]["certificate.prevOsid"] = userProject[0].certificate.osid; } - await kafkaProducersHelper.pushProjectToKafka(userProject[0]); + updateObject["$set"]["certificate.reIssuedAt"] = new Date(); + await projectQueries.findOneAndUpdate( + { + _id: userProject[0]._id + }, + updateObject + ); + return resolve({ success: true, message: CONSTANTS.apiResponses.PROJECT_CERTIFICATE_GENERATED, From 0626929bfdd8a60dc0c7b18810b49bf234947cb5 Mon Sep 17 00:00:00 2001 From: VISHNUDAS-tunerlabse Date: Sun, 20 Nov 2022 19:44:17 +0530 Subject: [PATCH 23/92] review changes --- models/projects.js | 11 +++++++---- module/userProjects/helper.js | 8 ++++---- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/models/projects.js b/models/projects.js index 483602ec..254da8d4 100644 --- a/models/projects.js +++ b/models/projects.js @@ -154,10 +154,13 @@ module.exports = { message : String, issuedOn : Date, criteria : Object, - transactionIdCreatedAt : Date, - reIssuedAt : Date, - prevTransactionId : String, - prevOsid : String + originalTransactionInformation :{ + transactionIdCreatedAt : Date, + reIssuedAt : Date, + prevTransactionId : String, + prevOsid : String + } + } }, compoundIndex: [ diff --git a/module/userProjects/helper.js b/module/userProjects/helper.js index 6e0c712e..cb831e7c 100644 --- a/module/userProjects/helper.js +++ b/module/userProjects/helper.js @@ -2525,7 +2525,7 @@ module.exports = class UserProjectsHelper { ) { updateObject["$set"]["certificate.osid"] = certificateDetails.data.ProjectCertificate.osid; } - updateObject["$set"]["certificate.transactionIdCreatedAt"] = new Date();; + updateObject["$set"]["certificate.originalTransactionInformation.transactionIdCreatedAt"] = new Date();; if ( Object.keys(updateObject["$set"]).length > 0 ) { await projectQueries.findOneAndUpdate( @@ -2727,12 +2727,12 @@ module.exports = class UserProjectsHelper { } if ( userProject[0].certificate.transactionId ) { - updateObject["$set"]["certificate.prevTransactionId"] = userProject[0].certificate.transactionId + updateObject["$set"]["certificate.originalTransactionInformation.prevTransactionId"] = userProject[0].certificate.transactionId } if ( userProject[0].certificate.osid ) { - updateObject["$set"]["certificate.prevOsid"] = userProject[0].certificate.osid; + updateObject["$set"]["certificate.originalTransactionInformation.prevOsid"] = userProject[0].certificate.osid; } - updateObject["$set"]["certificate.reIssuedAt"] = new Date(); + updateObject["$set"]["certificate.originalTransactionInformation.reIssuedAt"] = new Date(); await projectQueries.findOneAndUpdate( { _id: userProject[0]._id From 56da30fe36c8a9efe3beae40f965038a3e8973ad Mon Sep 17 00:00:00 2001 From: VISHNUDAS-tunerlabse Date: Mon, 21 Nov 2022 10:53:33 +0530 Subject: [PATCH 24/92] project model changes --- models/projects.js | 8 ++++---- module/userProjects/helper.js | 9 +++++---- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/models/projects.js b/models/projects.js index 254da8d4..bdf827c7 100644 --- a/models/projects.js +++ b/models/projects.js @@ -154,11 +154,11 @@ module.exports = { message : String, issuedOn : Date, criteria : Object, + reIssuedAt : Date, + transactionIdCreatedAt : Date, originalTransactionInformation :{ - transactionIdCreatedAt : Date, - reIssuedAt : Date, - prevTransactionId : String, - prevOsid : String + transactionId : String, + osid : String } } diff --git a/module/userProjects/helper.js b/module/userProjects/helper.js index cb831e7c..6e8e8d4f 100644 --- a/module/userProjects/helper.js +++ b/module/userProjects/helper.js @@ -2525,7 +2525,7 @@ module.exports = class UserProjectsHelper { ) { updateObject["$set"]["certificate.osid"] = certificateDetails.data.ProjectCertificate.osid; } - updateObject["$set"]["certificate.originalTransactionInformation.transactionIdCreatedAt"] = new Date();; + updateObject["$set"]["certificate.transactionIdCreatedAt"] = new Date();; if ( Object.keys(updateObject["$set"]).length > 0 ) { await projectQueries.findOneAndUpdate( @@ -2560,6 +2560,7 @@ module.exports = class UserProjectsHelper { static certificateCallback(transactionId, osid) { return new Promise(async (resolve, reject) => { try { + // callback request structure nested so validating transactionId and osid here instead in validator. if ( transactionId == "" || osid == "" ) { throw { status: HTTP_STATUS_CODE["bad_request"].status, @@ -2727,12 +2728,12 @@ module.exports = class UserProjectsHelper { } if ( userProject[0].certificate.transactionId ) { - updateObject["$set"]["certificate.originalTransactionInformation.prevTransactionId"] = userProject[0].certificate.transactionId + updateObject["$set"]["certificate.originalTransactionInformation.transactionId"] = userProject[0].certificate.transactionId } if ( userProject[0].certificate.osid ) { - updateObject["$set"]["certificate.originalTransactionInformation.prevOsid"] = userProject[0].certificate.osid; + updateObject["$set"]["certificate.originalTransactionInformation.osid"] = userProject[0].certificate.osid; } - updateObject["$set"]["certificate.originalTransactionInformation.reIssuedAt"] = new Date(); + updateObject["$set"]["certificate.reIssuedAt"] = new Date(); await projectQueries.findOneAndUpdate( { _id: userProject[0]._id From 261d78000362b5abed39bbaf24546ace34ff60c4 Mon Sep 17 00:00:00 2001 From: VISHNUDAS-tunerlabse Date: Mon, 21 Nov 2022 18:12:14 +0530 Subject: [PATCH 25/92] envVariable fix --- envVariables.js | 2 ++ generics/services/certificate.js | 9 +++------ 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/envVariables.js b/envVariables.js index db946621..13ef9826 100644 --- a/envVariables.js +++ b/envVariables.js @@ -120,6 +120,8 @@ module.exports = function() { && enviromentVariables[eachEnvironmentVariable].default && enviromentVariables[eachEnvironmentVariable].default != "") { process.env[eachEnvironmentVariable] = enviromentVariables[eachEnvironmentVariable].default; + success = true; + keyCheckPass = true; } if(!keyCheckPass) { diff --git a/generics/services/certificate.js b/generics/services/certificate.js index 4ba22669..d0c510ef 100644 --- a/generics/services/certificate.js +++ b/generics/services/certificate.js @@ -7,8 +7,6 @@ //dependencies const request = require('request'); -const CERTIFICATE_SERVICE_URL = process.env.CERTIFICATE_SERVICE_URL; -const ML_PROJECT_URL = `http://${process.env.SERVICE_NAME}:${process.env.APPLICATION_PORT}`; /** * Project certificate creation @@ -21,11 +19,11 @@ const ML_PROJECT_URL = `http://${process.env.SERVICE_NAME}:${process.env.APPLICA const createCertificate = function (bodyData) { return new Promise(async (resolve, reject) => { try { + const ML_PROJECT_URL = `http://${process.env.SERVICE_NAME}:${process.env.APPLICATION_PORT}`; const callbackUrl = ML_PROJECT_URL + CONSTANTS.endpoints.PROJECT_CERTIFICATE_API_CALLBACK; let certificateCreateUrl = - CERTIFICATE_SERVICE_URL + + process.env.CERTIFICATE_SERVICE_URL + CONSTANTS.endpoints.CERTIFICATE_CREATE + "?mode=async&callback=" + callbackUrl; - const options = { headers : { "content-type": "application/json" @@ -71,7 +69,7 @@ const getCertificateIssuerKid = function () { return new Promise(async (resolve, reject) => { try { let issuerKidUrl = - CERTIFICATE_SERVICE_URL + CONSTANTS.endpoints.GET_CERTIFICATE_KID; + process.env.CERTIFICATE_SERVICE_URL + CONSTANTS.endpoints.GET_CERTIFICATE_KID; let bodyData = {"filters": {}}; const options = { @@ -81,7 +79,6 @@ const getCertificateIssuerKid = function () { json : bodyData }; request.post(issuerKidUrl,options,getKidCallback); - function getKidCallback(err, data) { let result = { success : true From e6e9ac916af72b2cb019e3c480449fda7b2cbc99 Mon Sep 17 00:00:00 2001 From: VISHNUDAS-tunerlabse Date: Wed, 23 Nov 2022 14:38:46 +0530 Subject: [PATCH 26/92] added certificate key to projection --- module/userProjects/helper.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/module/userProjects/helper.js b/module/userProjects/helper.js index 26c6280c..6d3ecbcf 100644 --- a/module/userProjects/helper.js +++ b/module/userProjects/helper.js @@ -1941,7 +1941,8 @@ module.exports = class UserProjectsHelper { "lastDownloadedAt", "hasAcceptedTAndC", "referenceFrom", - "status" + "status", + "certificate" ] ); From a08fca099f513084bd86c9419592485c49152090 Mon Sep 17 00:00:00 2001 From: VISHNUDAS-tunerlabse Date: Thu, 24 Nov 2022 19:31:59 +0530 Subject: [PATCH 27/92] ED-103 staging-fix --- module/userProjects/helper.js | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/module/userProjects/helper.js b/module/userProjects/helper.js index 6d3ecbcf..360690cd 100644 --- a/module/userProjects/helper.js +++ b/module/userProjects/helper.js @@ -1174,7 +1174,8 @@ module.exports = class UserProjectsHelper { // create certificate object and add data if certificate template is present. if ( certificateTemplateDetails.length > 0 ) { - projectCreation.data["certificate"] = _.pick(certificateTemplateDetails[0], ['_id', 'templateUrl', 'status', 'criteria']); + projectCreation.data["certificate"] = _.pick(certificateTemplateDetails[0], ['templateUrl', 'status', 'criteria']); + projectCreation.data["certificate"]["templateId"] = solutionDetails.certificateTemplateId; } } @@ -2225,20 +2226,20 @@ module.exports = class UserProjectsHelper { } // <- Add certificate template data if ( - libraryProjects.data.solutionInformation && - libraryProjects.data.solutionInformation.certificateTemplateId && - libraryProjects.data.solutionInformation.certificateTemplateId !== "" + libraryProjects.data.certificateTemplateId && + libraryProjects.data.certificateTemplateId !== "" ){ // <- Add certificate template details to projectCreation data if present -> const certificateTemplateDetails = await certificateTemplateQueries.certificateTemplateDocument({ - _id : libraryProjects.data.solutionInformation.certificateTemplateId + _id : libraryProjects.data.certificateTemplateId }); // create certificate object and add data if certificate template is present. if ( certificateTemplateDetails.length > 0 ) { - libraryProjects.data["certificate"] = _.pick(certificateTemplateDetails[0], ['_id', 'templateUrl', 'status', 'criteria']); + libraryProjects.data["certificate"] = _.pick(certificateTemplateDetails[0], ['templateUrl', 'status', 'criteria']); } - delete libraryProjects.data.solutionInformation.certificateTemplateId; + libraryProjects.data["certificate"]["templateId"] = libraryProjects.data.certificateTemplateId; + delete libraryProjects.data.certificateTemplateId; } //Fetch user profile information by calling sunbird's user read api. From 3dfb1a33bdc9c1d7419e80b8f0204419fbb88d04 Mon Sep 17 00:00:00 2001 From: VISHNUDAS-tunerlabse Date: Thu, 24 Nov 2022 22:11:01 +0530 Subject: [PATCH 28/92] model change --- models/project-templates.js | 3 ++- models/solutions.js | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/models/project-templates.js b/models/project-templates.js index 4fddac32..e1df6b92 100644 --- a/models/project-templates.js +++ b/models/project-templates.js @@ -112,6 +112,7 @@ module.exports = { 4 : 0, 5 : 0 } - } + }, + certificateTemplateId : "ObjectId" } }; \ No newline at end of file diff --git a/models/solutions.js b/models/solutions.js index c55a0508..dacb3a09 100644 --- a/models/solutions.js +++ b/models/solutions.js @@ -96,6 +96,7 @@ module.exports = { type: Number, default: 1 }, - reportInformation : Object + reportInformation : Object, + certificateTemplateId : "ObjectId" } }; \ No newline at end of file From ac074b89ddf2fdf5f9b4b3c5646f4e83dcb1a7fa Mon Sep 17 00:00:00 2001 From: VISHNUDAS-tunerlabse Date: Thu, 24 Nov 2022 23:31:09 +0530 Subject: [PATCH 29/92] staging fix --- module/userProjects/helper.js | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/module/userProjects/helper.js b/module/userProjects/helper.js index 360690cd..e81723fe 100644 --- a/module/userProjects/helper.js +++ b/module/userProjects/helper.js @@ -2112,7 +2112,7 @@ module.exports = class UserProjectsHelper { "", isATargetedSolution ); - + if ( libraryProjects.data && !Object.keys(libraryProjects.data).length > 0 @@ -2372,7 +2372,7 @@ module.exports = class UserProjectsHelper { const certificateData = await this.createCertificatePayload(data); // call sunbird-RC to create certificate for project - const certificate = await this.createCertificate(certificateData) + const certificate = await this.createCertificate(certificateData, data._id) return resolve(certificate); @@ -2470,6 +2470,7 @@ module.exports = class UserProjectsHelper { message: CONSTANTS.apiResponses.CERTIFICATE_TEMPLATE_NOT_FOUND }; } + certificateTemplateDetails[0].issuer.kid = CERTIFICATE_ISSUER_KID; } //create certificate request body @@ -2480,7 +2481,7 @@ module.exports = class UserProjectsHelper { type : data.userProfile.userType }, templateUrl : data.certificate.templateUrl, - issuer : CERTIFICATE_ISSUER_KID, + issuer : certificateTemplateDetails[0].issuer, status : data.certificate.status.toUpperCase(), projectId : (data._id).toString(), projectName : data.title, @@ -2507,10 +2508,11 @@ module.exports = class UserProjectsHelper { * @method * @name createCertificate * @param {Object} certificateData - payload for certificate creation data. + * @param {string} projectId - project Id. * @returns {Boolean} certificate creation status. */ - static createCertificate(certificateData) { + static createCertificate(certificateData, projectId) { return new Promise(async (resolve, reject) => { try { @@ -2520,7 +2522,7 @@ module.exports = class UserProjectsHelper { message: CONSTANTS.apiResponses.CERTIFICATE_GENERATION_FAILED }; } - + let updateObject = { "$set" : {} }; @@ -2552,7 +2554,7 @@ module.exports = class UserProjectsHelper { if ( Object.keys(updateObject["$set"]).length > 0 ) { await projectQueries.findOneAndUpdate( { - _id: data._id + _id: projectId }, updateObject ); @@ -2741,7 +2743,7 @@ module.exports = class UserProjectsHelper { const certificateData = await this.createCertificatePayload(userProject[0]); // call sunbird-RC to create certificate for project - const certificate = await this.createCertificate(certificateData); + const certificate = await this.createCertificate(certificateData, userProject[0]._id); if ( !certificate.success ) { throw { From 929d9f129a78679609eb4900f6f37ae12029c2b7 Mon Sep 17 00:00:00 2001 From: VISHNUDAS-tunerlabse Date: Fri, 25 Nov 2022 10:49:03 +0530 Subject: [PATCH 30/92] avoid template url from updation --- module/userProjects/helper.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/module/userProjects/helper.js b/module/userProjects/helper.js index e81723fe..dc83f2fa 100644 --- a/module/userProjects/helper.js +++ b/module/userProjects/helper.js @@ -147,7 +147,9 @@ module.exports = class UserProjectsHelper { if(data.userRoleInformation) delete data.userRoleInformation; if(data.userProfile) delete data.userProfile; - + // if certificate is there. only templateUrl is removed from certificate object(). + if( data.certificate.templateUrl) delete data.certificate.templateUrl; + let updateProject = {}; let projectData = await _projectData(data); if (projectData && projectData.success == true) { From f2ddb14e8438a6ef5b92afa38d1da98de01a6cba Mon Sep 17 00:00:00 2001 From: VISHNUDAS-tunerlabse Date: Fri, 25 Nov 2022 11:00:47 +0530 Subject: [PATCH 31/92] remove certificate key from updation --- module/userProjects/helper.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/module/userProjects/helper.js b/module/userProjects/helper.js index dc83f2fa..f4dee69a 100644 --- a/module/userProjects/helper.js +++ b/module/userProjects/helper.js @@ -144,11 +144,11 @@ module.exports = class UserProjectsHelper { } const projectsModel = Object.keys(schemas["projects"].schema); - - if(data.userRoleInformation) delete data.userRoleInformation; - if(data.userProfile) delete data.userProfile; - // if certificate is there. only templateUrl is removed from certificate object(). - if( data.certificate.templateUrl) delete data.certificate.templateUrl; + + let keysToRemoveFromUpdation = ["userRoleInformation","userProfile","certificate"] + keysToRemoveFromUpdation.forEach( key => { + if (data[key])delete data[key]; + }) let updateProject = {}; let projectData = await _projectData(data); From d99d402d0eca21be7f623c2112f5ad799ada3052 Mon Sep 17 00:00:00 2001 From: VISHNUDAS-tunerlabse Date: Fri, 25 Nov 2022 14:53:37 +0530 Subject: [PATCH 32/92] console added to check in staging --- generics/services/certificate.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/generics/services/certificate.js b/generics/services/certificate.js index d0c510ef..7743ef31 100644 --- a/generics/services/certificate.js +++ b/generics/services/certificate.js @@ -19,6 +19,7 @@ const request = require('request'); const createCertificate = function (bodyData) { return new Promise(async (resolve, reject) => { try { + console.log("line 22 payload to RC : ",bodyData) const ML_PROJECT_URL = `http://${process.env.SERVICE_NAME}:${process.env.APPLICATION_PORT}`; const callbackUrl = ML_PROJECT_URL + CONSTANTS.endpoints.PROJECT_CERTIFICATE_API_CALLBACK; let certificateCreateUrl = @@ -32,13 +33,14 @@ const createCertificate = function (bodyData) { }; request.post(certificateCreateUrl,options,certificateCallback); - + console.log("line 35 certificateCreateUrl :", certificateCreateUrl) function certificateCallback(err, data) { let result = { success : true }; - + console.log("line 41 data from RC call :",data); + console.log("line 41 error from RC call :",err); if (err) { result.success = false; } else { From cf6faaf562fe7ac8dda65e3e81898bd8fcd6b540 Mon Sep 17 00:00:00 2001 From: VISHNUDAS-tunerlabse Date: Fri, 25 Nov 2022 17:20:21 +0530 Subject: [PATCH 33/92] console added and template details changes --- generics/services/certificate.js | 23 +++++++++++++++-------- module/project/templates/helper.js | 15 ++++++++++++++- module/userProjects/helper.js | 4 ++-- 3 files changed, 31 insertions(+), 11 deletions(-) diff --git a/generics/services/certificate.js b/generics/services/certificate.js index 7743ef31..b11d7b07 100644 --- a/generics/services/certificate.js +++ b/generics/services/certificate.js @@ -19,7 +19,7 @@ const request = require('request'); const createCertificate = function (bodyData) { return new Promise(async (resolve, reject) => { try { - console.log("line 22 payload to RC : ",bodyData) + const ML_PROJECT_URL = `http://${process.env.SERVICE_NAME}:${process.env.APPLICATION_PORT}`; const callbackUrl = ML_PROJECT_URL + CONSTANTS.endpoints.PROJECT_CERTIFICATE_API_CALLBACK; let certificateCreateUrl = @@ -31,20 +31,21 @@ const createCertificate = function (bodyData) { }, json : bodyData }; - + console.log("requestUrlcertificate create : ",certificateCreateUrl) + console.log("certificateRequestBody : ",bodyData) request.post(certificateCreateUrl,options,certificateCallback); - console.log("line 35 certificateCreateUrl :", certificateCreateUrl) + function certificateCallback(err, data) { let result = { success : true }; - console.log("line 41 data from RC call :",data); - console.log("line 41 error from RC call :",err); + console.log("line 41 error from RC call error :",err.message); if (err) { result.success = false; } else { let response = data.body; + console.log("certificate success response: ",response) if( response.params.status === "SUCCESSFUL" ) { result["data"] = response.result; } else { @@ -55,6 +56,7 @@ const createCertificate = function (bodyData) { } } catch (error) { + console.log("line 58 catch block : ",error.message) return reject(error); } }) @@ -72,24 +74,28 @@ const getCertificateIssuerKid = function () { try { let issuerKidUrl = process.env.CERTIFICATE_SERVICE_URL + CONSTANTS.endpoints.GET_CERTIFICATE_KID; + let bodyData = {"filters": {}}; - + const options = { headers : { "Content-Type": "application/json" }, json : bodyData - }; + }; + console.log("issuer Kid url : ",issuerKidUrl); + console.log("issuer Kid bodyData : ",bodyData); request.post(issuerKidUrl,options,getKidCallback); function getKidCallback(err, data) { let result = { success : true }; - + console.log("KID rc call error : ",err.message) if (err) { result.success = false; } else { let response = data.body; + console.log("KID success response : ",response) if( response.length > 0 && response[0].osid && response[0].osid !== "" ) { result["data"] = response[0].osid; } else { @@ -105,6 +111,7 @@ const getCertificateIssuerKid = function () { }, CONSTANTS.common.SERVER_TIME_OUT); } catch (error) { + console.log("catch error : ",error.message) return reject(error); } }) diff --git a/module/project/templates/helper.js b/module/project/templates/helper.js index 72d7d7b5..6e3404f8 100644 --- a/module/project/templates/helper.js +++ b/module/project/templates/helper.js @@ -22,7 +22,7 @@ const projectTemplateTaskQueries = require(DB_QUERY_BASE_PATH + "/projectTemplat const projectQueries = require(DB_QUERY_BASE_PATH + "/projects"); const projectCategoriesQueries = require(DB_QUERY_BASE_PATH + "/projectCategories"); const solutionsQueries = require(DB_QUERY_BASE_PATH + "/solutions"); - +const certificateTemplateQueries = require(DB_QUERY_BASE_PATH + "/certificateTemplates"); module.exports = class ProjectTemplatesHelper { @@ -1048,6 +1048,19 @@ module.exports = class ProjectTemplatesHelper { message :CONSTANTS.apiResponses.PROJECT_TEMPLATE_NOT_FOUND } } + if ( templateData[0].certificateTemplateId && templateData[0].certificateTemplateId !== "" ){ + let certificateTemplateDetails = await certificateTemplateQueries.certificateTemplateDocument({ + _id : templateData[0].certificateTemplateId + },["criteria"]); + + //certificate template data do not exists. + if ( !certificateTemplateDetails.length > 0 ) { + throw { + message: CONSTANTS.apiResponses.CERTIFICATE_TEMPLATE_NOT_FOUND + }; + } + templateData[0].criteria = certificateTemplateDetails[0].criteria + } if (templateData[0].tasks && templateData[0].tasks.length > 0) { templateData[0].tasks = diff --git a/module/userProjects/helper.js b/module/userProjects/helper.js index f4dee69a..2bcd193a 100644 --- a/module/userProjects/helper.js +++ b/module/userProjects/helper.js @@ -144,7 +144,7 @@ module.exports = class UserProjectsHelper { } const projectsModel = Object.keys(schemas["projects"].schema); - + let keysToRemoveFromUpdation = ["userRoleInformation","userProfile","certificate"] keysToRemoveFromUpdation.forEach( key => { if (data[key])delete data[key]; @@ -2480,7 +2480,7 @@ module.exports = class UserProjectsHelper { recipient : { id : data.userId, name : data.userProfile.userName, - type : data.userProfile.userType + type : data.userProfile.profileUserType.type }, templateUrl : data.certificate.templateUrl, issuer : certificateTemplateDetails[0].issuer, From 785afe5be324345435568a168c0292ed14350d20 Mon Sep 17 00:00:00 2001 From: VISHNUDAS-tunerlabse Date: Fri, 25 Nov 2022 17:49:14 +0530 Subject: [PATCH 34/92] console change --- generics/services/certificate.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/generics/services/certificate.js b/generics/services/certificate.js index b11d7b07..7620cc35 100644 --- a/generics/services/certificate.js +++ b/generics/services/certificate.js @@ -40,9 +40,9 @@ const createCertificate = function (bodyData) { let result = { success : true }; - console.log("line 41 error from RC call error :",err.message); if (err) { result.success = false; + console.log("line 45 error from RC call error :",err.message); } else { let response = data.body; console.log("certificate success response: ",response) @@ -90,8 +90,9 @@ const getCertificateIssuerKid = function () { let result = { success : true }; - console.log("KID rc call error : ",err.message) + if (err) { + console.log("KID rc call error : ",err.message) result.success = false; } else { let response = data.body; From 63dd62f82b9efe618f6ec1a7604542886daaf733 Mon Sep 17 00:00:00 2001 From: Akash Shah Date: Mon, 28 Nov 2022 17:19:47 +0530 Subject: [PATCH 35/92] Make logs as a string. --- generics/services/certificate.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/generics/services/certificate.js b/generics/services/certificate.js index 7620cc35..42f75a15 100644 --- a/generics/services/certificate.js +++ b/generics/services/certificate.js @@ -32,7 +32,7 @@ const createCertificate = function (bodyData) { json : bodyData }; console.log("requestUrlcertificate create : ",certificateCreateUrl) - console.log("certificateRequestBody : ",bodyData) + console.log("certificateRequestBody : ",JSON.stringify(bodyData)) request.post(certificateCreateUrl,options,certificateCallback); function certificateCallback(err, data) { @@ -45,7 +45,7 @@ const createCertificate = function (bodyData) { console.log("line 45 error from RC call error :",err.message); } else { let response = data.body; - console.log("certificate success response: ",response) + console.log("certificate success response: ",JSON.stringify(response)) if( response.params.status === "SUCCESSFUL" ) { result["data"] = response.result; } else { @@ -84,7 +84,7 @@ const getCertificateIssuerKid = function () { json : bodyData }; console.log("issuer Kid url : ",issuerKidUrl); - console.log("issuer Kid bodyData : ",bodyData); + console.log("issuer Kid bodyData : ",JSON.stringify(bodyData)); request.post(issuerKidUrl,options,getKidCallback); function getKidCallback(err, data) { let result = { @@ -96,7 +96,7 @@ const getCertificateIssuerKid = function () { result.success = false; } else { let response = data.body; - console.log("KID success response : ",response) + console.log("KID success response : ",JSON.stringify(response)) if( response.length > 0 && response[0].osid && response[0].osid !== "" ) { result["data"] = response[0].osid; } else { From 31c8b7e381dd021a302e2a77292d98a8fd5feba6 Mon Sep 17 00:00:00 2001 From: Akash Shah Date: Mon, 28 Nov 2022 17:38:48 +0530 Subject: [PATCH 36/92] Log raw data. --- generics/services/certificate.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/generics/services/certificate.js b/generics/services/certificate.js index 42f75a15..7538eaeb 100644 --- a/generics/services/certificate.js +++ b/generics/services/certificate.js @@ -36,7 +36,7 @@ const createCertificate = function (bodyData) { request.post(certificateCreateUrl,options,certificateCallback); function certificateCallback(err, data) { - + console.log("line 39 raw data from RC call :",JSON.stringify(data)); let result = { success : true }; From 290753284f0a75a8bdd63f7074dbcd696d1efc14 Mon Sep 17 00:00:00 2001 From: VISHNUDAS-tunerlabse Date: Fri, 2 Dec 2022 17:06:42 +0530 Subject: [PATCH 37/92] certificate creation sync mode enabled --- generics/constants/endpoints.js | 2 +- generics/services/certificate.js | 8 ++++---- module/userProjects/helper.js | 12 +++++++----- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/generics/constants/endpoints.js b/generics/constants/endpoints.js index 14509493..c8a5bfaf 100644 --- a/generics/constants/endpoints.js +++ b/generics/constants/endpoints.js @@ -49,7 +49,7 @@ module.exports = { USER_READ_V5 : "/v5/user/read", GET_LOCATION_DATA : "/v1/location/search", CERTIFICATE_CREATE : "/api/v1/ProjectCertificate", - PROJECT_CERTIFICATE_API_CALLBACK : "/v1/userProject/certificateCallback", + PROJECT_CERTIFICATE_API_CALLBACK : "/v1/userProjects/certificateCallback", USER_READ_PRIVATE : "/private/user/v1/read", // !Caution: End point for reading user details without token. Do not use for public work flow GET_CERTIFICATE_KID : "/api/v1/PublicKey/search" }; diff --git a/generics/services/certificate.js b/generics/services/certificate.js index 7620cc35..01f7b110 100644 --- a/generics/services/certificate.js +++ b/generics/services/certificate.js @@ -21,18 +21,18 @@ const createCertificate = function (bodyData) { try { const ML_PROJECT_URL = `http://${process.env.SERVICE_NAME}:${process.env.APPLICATION_PORT}`; - const callbackUrl = ML_PROJECT_URL + CONSTANTS.endpoints.PROJECT_CERTIFICATE_API_CALLBACK; + // const callbackUrl = ML_PROJECT_URL + CONSTANTS.endpoints.PROJECT_CERTIFICATE_API_CALLBACK; let certificateCreateUrl = process.env.CERTIFICATE_SERVICE_URL + - CONSTANTS.endpoints.CERTIFICATE_CREATE + "?mode=async&callback=" + callbackUrl; + CONSTANTS.endpoints.CERTIFICATE_CREATE //+ "?mode=async&callback=" + callbackUrl; const options = { headers : { "content-type": "application/json" }, json : bodyData }; - console.log("requestUrlcertificate create : ",certificateCreateUrl) - console.log("certificateRequestBody : ",bodyData) + console.log("bodyData : ",bodyData) + console.log("certificateCreateUrl : ",certificateCreateUrl) request.post(certificateCreateUrl,options,certificateCallback); function certificateCallback(err, data) { diff --git a/module/userProjects/helper.js b/module/userProjects/helper.js index 2bcd193a..19eb734c 100644 --- a/module/userProjects/helper.js +++ b/module/userProjects/helper.js @@ -2372,7 +2372,7 @@ module.exports = class UserProjectsHelper { // create payload for certificate generation const certificateData = await this.createCertificatePayload(data); - + // call sunbird-RC to create certificate for project const certificate = await this.createCertificate(certificateData, data._id) @@ -2518,7 +2518,8 @@ module.exports = class UserProjectsHelper { return new Promise(async (resolve, reject) => { try { - const certificateDetails = await certificateService.createCertificate( certificateData ); + const certificateDetails = await certificateService.createCertificate( certificateData ); + if ( !certificateDetails.success || !certificateDetails.data || !certificateDetails.data.ProjectCertificate ) { throw { message: CONSTANTS.apiResponses.CERTIFICATE_GENERATION_FAILED @@ -2552,14 +2553,15 @@ module.exports = class UserProjectsHelper { updateObject["$set"]["certificate.osid"] = certificateDetails.data.ProjectCertificate.osid; } updateObject["$set"]["certificate.transactionIdCreatedAt"] = new Date();; - + if ( Object.keys(updateObject["$set"]).length > 0 ) { - await projectQueries.findOneAndUpdate( + let updatedProject = await projectQueries.findOneAndUpdate( { _id: projectId }, updateObject ); + await kafkaProducersHelper.pushProjectToKafka(updatedProject); } return resolve( { success: true @@ -2615,7 +2617,7 @@ module.exports = class UserProjectsHelper { message: CONSTANTS.apiResponses.PROJECT_NOT_FOUND } } - + await kafkaProducersHelper.pushProjectToKafka(projectDetails); return resolve({ success: true, message: CONSTANTS.apiResponses.PROJECT_CERTIFICATE_GENERATED, From ca40f5ed98d3764668711c99c4e0354482315ac5 Mon Sep 17 00:00:00 2001 From: VISHNUDAS-tunerlabse Date: Mon, 5 Dec 2022 11:59:13 +0530 Subject: [PATCH 38/92] issuedOn key added for sync mode --- module/userProjects/helper.js | 1 + 1 file changed, 1 insertion(+) diff --git a/module/userProjects/helper.js b/module/userProjects/helper.js index 19eb734c..0b77dabf 100644 --- a/module/userProjects/helper.js +++ b/module/userProjects/helper.js @@ -2551,6 +2551,7 @@ module.exports = class UserProjectsHelper { certificateDetails.data.ProjectCertificate.osid !== "" ) { updateObject["$set"]["certificate.osid"] = certificateDetails.data.ProjectCertificate.osid; + updateObject["$set"]["certificate.issuedOn"] = new Date(); } updateObject["$set"]["certificate.transactionIdCreatedAt"] = new Date();; From f715e06e789463097a7788f4053336d97afcfe10 Mon Sep 17 00:00:00 2001 From: VISHNUDAS-tunerlabse Date: Mon, 12 Dec 2022 12:13:57 +0530 Subject: [PATCH 39/92] async mode enabled, certificate list change to pass complete templateUrl --- generics/services/certificate.js | 4 ++-- module/userProjects/helper.js | 22 ++++++++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/generics/services/certificate.js b/generics/services/certificate.js index 01f7b110..1eeb6289 100644 --- a/generics/services/certificate.js +++ b/generics/services/certificate.js @@ -21,10 +21,10 @@ const createCertificate = function (bodyData) { try { const ML_PROJECT_URL = `http://${process.env.SERVICE_NAME}:${process.env.APPLICATION_PORT}`; - // const callbackUrl = ML_PROJECT_URL + CONSTANTS.endpoints.PROJECT_CERTIFICATE_API_CALLBACK; + const callbackUrl = ML_PROJECT_URL + CONSTANTS.endpoints.PROJECT_CERTIFICATE_API_CALLBACK; let certificateCreateUrl = process.env.CERTIFICATE_SERVICE_URL + - CONSTANTS.endpoints.CERTIFICATE_CREATE //+ "?mode=async&callback=" + callbackUrl; + CONSTANTS.endpoints.CERTIFICATE_CREATE + "?mode=async&callback=" + callbackUrl; const options = { headers : { "content-type": "application/json" diff --git a/module/userProjects/helper.js b/module/userProjects/helper.js index 0b77dabf..f0aa3c7e 100644 --- a/module/userProjects/helper.js +++ b/module/userProjects/helper.js @@ -2674,6 +2674,28 @@ module.exports = class UserProjectsHelper { message: CONSTANTS.apiResponses.PROJECT_WITH_CERTIFICATE_NOT_FOUND } } + //loop through user projects and get downloadable url for templateUrl if osid is present. + for( let userProjectPointer = 0; userProjectPointer < userProject.length; userProjectPointer++ ) { + if ( userProject[userProjectPointer].certificate.osid && + userProject[userProjectPointer].certificate.osid !== "" && + userProject[userProjectPointer].certificate.templateUrl && + userProject[userProjectPointer].certificate.templateUrl !== "" + ){ + let certificateTemplateDownloadableUrl = + await coreService.getDownloadableUrl( + { + filePaths: [userProject[userProjectPointer].certificate.templateUrl] + } + ); + if ( certificateTemplateDownloadableUrl.success ) { + userProject[userProjectPointer].certificate.templateUrl = certificateTemplateDownloadableUrl.data[0].url; + } else { + throw { + message: CONSTANTS.apiResponses.DOWNLOADABLE_URL_NOT_FOUND + }; + } + } + } let count = _.countBy(userProject, (rec) => { return (rec.certificate && rec.certificate.osid && rec.certificate.osid !== "" )? 'generated': 'notGenerated'; From a1136c191b7834968dd321deba76b8ec61904af9 Mon Sep 17 00:00:00 2001 From: VISHNUDAS-tunerlabse Date: Mon, 12 Dec 2022 13:37:32 +0530 Subject: [PATCH 40/92] code optimized- downloadable templateUrl --- module/userProjects/helper.js | 40 +++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/module/userProjects/helper.js b/module/userProjects/helper.js index f0aa3c7e..ab51762d 100644 --- a/module/userProjects/helper.js +++ b/module/userProjects/helper.js @@ -2674,28 +2674,40 @@ module.exports = class UserProjectsHelper { message: CONSTANTS.apiResponses.PROJECT_WITH_CERTIFICATE_NOT_FOUND } } + let templateFilePath = []; //loop through user projects and get downloadable url for templateUrl if osid is present. for( let userProjectPointer = 0; userProjectPointer < userProject.length; userProjectPointer++ ) { if ( userProject[userProjectPointer].certificate.osid && userProject[userProjectPointer].certificate.osid !== "" && userProject[userProjectPointer].certificate.templateUrl && userProject[userProjectPointer].certificate.templateUrl !== "" - ){ - let certificateTemplateDownloadableUrl = - await coreService.getDownloadableUrl( - { - filePaths: [userProject[userProjectPointer].certificate.templateUrl] - } - ); - if ( certificateTemplateDownloadableUrl.success ) { - userProject[userProjectPointer].certificate.templateUrl = certificateTemplateDownloadableUrl.data[0].url; - } else { - throw { - message: CONSTANTS.apiResponses.DOWNLOADABLE_URL_NOT_FOUND - }; - } + ) { + templateFilePath.push(userProject[userProjectPointer].certificate.templateUrl); } } + + if( templateFilePath.length > 0 ) { + + let certificateTemplateDownloadableUrl = + await coreService.getDownloadableUrl( + { + filePaths: templateFilePath + } + ); + if ( !certificateTemplateDownloadableUrl.success ) { + throw { + message: CONSTANTS.apiResponses.DOWNLOADABLE_URL_NOT_FOUND + }; + } + // map downloadable templateUrl to corresponding project data + userProject.forEach(projectData => { + var itemFromUrlArray = certificateTemplateDownloadableUrl.data.find(item=> item.filePath == projectData.certificate.templateUrl); + if (itemFromUrlArray) { + projectData.certificate.templateUrl = itemFromUrlArray.url; + } + } + ) + } let count = _.countBy(userProject, (rec) => { return (rec.certificate && rec.certificate.osid && rec.certificate.osid !== "" )? 'generated': 'notGenerated'; From 9f694a566afb507cacfdb40fddd5f21ff4ff4adc Mon Sep 17 00:00:00 2001 From: VISHNUDAS-tunerlabse Date: Mon, 12 Dec 2022 15:34:01 +0530 Subject: [PATCH 41/92] generate complete templateUrl capability added to userAssigned fn --- module/userProjects/helper.js | 45 ++++++++++++++++++++++++++++++++--- 1 file changed, 42 insertions(+), 3 deletions(-) diff --git a/module/userProjects/helper.js b/module/userProjects/helper.js index ab51762d..fcf67ea1 100644 --- a/module/userProjects/helper.js +++ b/module/userProjects/helper.js @@ -1927,7 +1927,7 @@ module.exports = class UserProjectsHelper { query["referenceFrom"] = CONSTANTS.common.LINK; } } - + let projects = await this.projects( query, pageSize, @@ -1948,7 +1948,7 @@ module.exports = class UserProjectsHelper { "certificate" ] ); - + let totalCount = 0; let data = []; @@ -1956,9 +1956,11 @@ module.exports = class UserProjectsHelper { totalCount = projects.data.count; data = projects.data.data; - + if( data.length > 0 ) { + let templateFilePath = []; data.forEach( projectData => { + projectData.name = projectData.title; @@ -1974,7 +1976,44 @@ module.exports = class UserProjectsHelper { projectData.type = CONSTANTS.common.IMPROVEMENT_PROJECT; delete projectData.title; + + if (projectData.certificate && + projectData.certificate.osid && + projectData.certificate.osid !== "" && + projectData.certificate.templateUrl && + projectData.certificate.templateUrl !== "" + ) { + templateFilePath.push(projectData.certificate.templateUrl); + } + }); + + if( templateFilePath.length > 0 ) { + + let certificateTemplateDownloadableUrl = + await coreService.getDownloadableUrl( + { + filePaths: templateFilePath + } + ); + if ( !certificateTemplateDownloadableUrl.success ) { + throw { + message: CONSTANTS.apiResponses.DOWNLOADABLE_URL_NOT_FOUND + }; + } + // map downloadable templateUrl to corresponding project data + data.forEach(projectData => { + if (projectData.certificate) { + var itemFromUrlArray = certificateTemplateDownloadableUrl.data.find(item=> item.filePath == projectData.certificate.templateUrl); + if (itemFromUrlArray) { + projectData.certificate.templateUrl = itemFromUrlArray.url; + } + } + } + + ) + } + } } From 9c16a6bff58e817c3d1d5b878510ea145d333c55 Mon Sep 17 00:00:00 2001 From: VISHNUDAS-tunerlabse Date: Mon, 12 Dec 2022 18:13:22 +0530 Subject: [PATCH 42/92] consoles removed --- envVariables.js | 2 +- generics/services/certificate.js | 12 ------------ 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/envVariables.js b/envVariables.js index 13ef9826..ef6073d8 100644 --- a/envVariables.js +++ b/envVariables.js @@ -44,7 +44,7 @@ let enviromentVariables = { "SERVICE_NAME" : { "message" : "current service name", "optional" : true, - "default" : "ml-project-service" + "default" : "ml-projects-service" }, "CERTIFICATE_SERVICE_URL" : { "message" : "certificate service base url", diff --git a/generics/services/certificate.js b/generics/services/certificate.js index 3bd0f997..446333f1 100644 --- a/generics/services/certificate.js +++ b/generics/services/certificate.js @@ -32,22 +32,16 @@ const createCertificate = function (bodyData) { json : bodyData }; - console.log("bodyData : ",bodyData) - console.log("certificateCreateUrl : ",certificateCreateUrl) - request.post(certificateCreateUrl,options,certificateCallback); function certificateCallback(err, data) { - console.log("line 39 raw data from RC call :",JSON.stringify(data)); let result = { success : true }; if (err) { result.success = false; - console.log("line 45 error from RC call error :",err.message); } else { let response = data.body; - console.log("certificate success response: ",JSON.stringify(response)) if( response.params.status === "SUCCESSFUL" ) { result["data"] = response.result; } else { @@ -58,7 +52,6 @@ const createCertificate = function (bodyData) { } } catch (error) { - console.log("line 58 catch block : ",error.message) return reject(error); } }) @@ -85,8 +78,6 @@ const getCertificateIssuerKid = function () { }, json : bodyData }; - console.log("issuer Kid url : ",issuerKidUrl); - console.log("issuer Kid bodyData : ",JSON.stringify(bodyData)); request.post(issuerKidUrl,options,getKidCallback); function getKidCallback(err, data) { let result = { @@ -94,11 +85,9 @@ const getCertificateIssuerKid = function () { }; if (err) { - console.log("KID rc call error : ",err.message) result.success = false; } else { let response = data.body; - console.log("KID success response : ",JSON.stringify(response)) if( response.length > 0 && response[0].osid && response[0].osid !== "" ) { result["data"] = response[0].osid; } else { @@ -114,7 +103,6 @@ const getCertificateIssuerKid = function () { }, CONSTANTS.common.SERVER_TIME_OUT); } catch (error) { - console.log("catch error : ",error.message) return reject(error); } }) From 89cfb4c877b8e588ed97b501d9c4f1273b9cc51f Mon Sep 17 00:00:00 2001 From: VISHNUDAS-tunerlabse Date: Mon, 12 Dec 2022 18:28:17 +0530 Subject: [PATCH 43/92] console removed --- generics/services/certificate.js | 12 ++++++++++++ generics/services/report.js | 3 --- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/generics/services/certificate.js b/generics/services/certificate.js index 446333f1..3bd0f997 100644 --- a/generics/services/certificate.js +++ b/generics/services/certificate.js @@ -32,16 +32,22 @@ const createCertificate = function (bodyData) { json : bodyData }; + console.log("bodyData : ",bodyData) + console.log("certificateCreateUrl : ",certificateCreateUrl) + request.post(certificateCreateUrl,options,certificateCallback); function certificateCallback(err, data) { + console.log("line 39 raw data from RC call :",JSON.stringify(data)); let result = { success : true }; if (err) { result.success = false; + console.log("line 45 error from RC call error :",err.message); } else { let response = data.body; + console.log("certificate success response: ",JSON.stringify(response)) if( response.params.status === "SUCCESSFUL" ) { result["data"] = response.result; } else { @@ -52,6 +58,7 @@ const createCertificate = function (bodyData) { } } catch (error) { + console.log("line 58 catch block : ",error.message) return reject(error); } }) @@ -78,6 +85,8 @@ const getCertificateIssuerKid = function () { }, json : bodyData }; + console.log("issuer Kid url : ",issuerKidUrl); + console.log("issuer Kid bodyData : ",JSON.stringify(bodyData)); request.post(issuerKidUrl,options,getKidCallback); function getKidCallback(err, data) { let result = { @@ -85,9 +94,11 @@ const getCertificateIssuerKid = function () { }; if (err) { + console.log("KID rc call error : ",err.message) result.success = false; } else { let response = data.body; + console.log("KID success response : ",JSON.stringify(response)) if( response.length > 0 && response[0].osid && response[0].osid !== "" ) { result["data"] = response[0].osid; } else { @@ -103,6 +114,7 @@ const getCertificateIssuerKid = function () { }, CONSTANTS.common.SERVER_TIME_OUT); } catch (error) { + console.log("catch error : ",error.message) return reject(error); } }) diff --git a/generics/services/report.js b/generics/services/report.js index 765e9ea7..75e58b9a 100644 --- a/generics/services/report.js +++ b/generics/services/report.js @@ -118,8 +118,6 @@ const projectAndTaskReport = function (token, input, projectPdf) { const url = reportsUrl + CONSTANTS.endpoints.PROJECT_AND_TASK_REPORT + "?projectPdf=" + projectPdf; - - console.log("--- url is- ----",url); let options = { headers : { @@ -147,7 +145,6 @@ const projectAndTaskReport = function (token, input, projectPdf) { } } catch (error) { - console.log("catch error",error); return reject(error); } }) From 0b8699106e1d7241172fa4fdd39fb0d750c1a54f Mon Sep 17 00:00:00 2001 From: Akash Shah Date: Mon, 12 Dec 2022 21:18:54 +0530 Subject: [PATCH 44/92] Add additional logs. --- generics/services/certificate.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/generics/services/certificate.js b/generics/services/certificate.js index 3bd0f997..8c98fdc2 100644 --- a/generics/services/certificate.js +++ b/generics/services/certificate.js @@ -38,7 +38,7 @@ const createCertificate = function (bodyData) { request.post(certificateCreateUrl,options,certificateCallback); function certificateCallback(err, data) { - console.log("line 39 raw data from RC call :",JSON.stringify(data)); + console.log("line 41 raw data from RC call :",JSON.stringify(data)); let result = { success : true }; @@ -89,6 +89,7 @@ const getCertificateIssuerKid = function () { console.log("issuer Kid bodyData : ",JSON.stringify(bodyData)); request.post(issuerKidUrl,options,getKidCallback); function getKidCallback(err, data) { + console.log("line 92 raw data from KID call :",JSON.stringify(data)); let result = { success : true }; From 747b8f696d1385fa8e9e8672cc62c6995d2e8bf3 Mon Sep 17 00:00:00 2001 From: VISHNUDAS-tunerlabse Date: Tue, 13 Dec 2022 13:04:10 +0530 Subject: [PATCH 45/92] removed global varaiable assign and check of issuer kid --- envVariables.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/envVariables.js b/envVariables.js index ef6073d8..013e7547 100644 --- a/envVariables.js +++ b/envVariables.js @@ -49,7 +49,7 @@ let enviromentVariables = { "CERTIFICATE_SERVICE_URL" : { "message" : "certificate service base url", "optional" : true, - "default" : "http://registry-service:8081", + "default" : "http://11.3.8.129/registry-service", "requiredIf" : { "key": "PROJECT_CERTIFICATE_ON_OFF", "operator" : "EQUALS", @@ -150,10 +150,11 @@ async function getKid(){ // get certificate issuer kid from sunbird-RC let kidData = await certificateService.getCertificateIssuerKid(); if( !kidData.success ) { - console.log("Server stoped . Failed to set certificate issuer Kid value") - process.exit(); + console.log("failed to get kid value from registry service") + // console.log("Server stoped . Failed to set certificate issuer Kid value") + // process.exit(); } - global.CERTIFICATE_ISSUER_KID = kidData.data + // global.CERTIFICATE_ISSUER_KID = kidData.data } }; From 3f326f4f409a104eb23fcd39da8e82be9ecede20 Mon Sep 17 00:00:00 2001 From: VISHNUDAS-tunerlabse Date: Tue, 13 Dec 2022 13:08:19 +0530 Subject: [PATCH 46/92] chenge in envVariables --- envVariables.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/envVariables.js b/envVariables.js index 013e7547..df8525ca 100644 --- a/envVariables.js +++ b/envVariables.js @@ -49,7 +49,7 @@ let enviromentVariables = { "CERTIFICATE_SERVICE_URL" : { "message" : "certificate service base url", "optional" : true, - "default" : "http://11.3.8.129/registry-service", + "default" : "http://registry-service:8081", "requiredIf" : { "key": "PROJECT_CERTIFICATE_ON_OFF", "operator" : "EQUALS", From e5754ba510aab3f94fefa6bf11e6c572db5bd7d9 Mon Sep 17 00:00:00 2001 From: VISHNUDAS-tunerlabse Date: Tue, 13 Dec 2022 15:19:50 +0530 Subject: [PATCH 47/92] console added to check issue --- controllers/v1/userProjects.js | 2 ++ envVariables.js | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/controllers/v1/userProjects.js b/controllers/v1/userProjects.js index cc904bef..e674ed07 100644 --- a/controllers/v1/userProjects.js +++ b/controllers/v1/userProjects.js @@ -1104,6 +1104,8 @@ module.exports = class UserProjects extends Abstract { return new Promise(async (resolve, reject) => { try { // ReIssue certificate of given project : projectId is passed as param + // This console has to be removed- adding to check the Issuer kid value in case rancher doesn't display console while deployment + console.log("+++++CERTIFICATE_ISSUER_KID+++++ : ",CERTIFICATE_ISSUER_KID) let projectDetails = await userProjectsHelper.certificateReIssue( req.params._id, ); diff --git a/envVariables.js b/envVariables.js index df8525ca..f828914a 100644 --- a/envVariables.js +++ b/envVariables.js @@ -150,9 +150,12 @@ async function getKid(){ // get certificate issuer kid from sunbird-RC let kidData = await certificateService.getCertificateIssuerKid(); if( !kidData.success ) { - console.log("failed to get kid value from registry service") + console.log("failed to get kid value from registry service : ",kidData) // console.log("Server stoped . Failed to set certificate issuer Kid value") // process.exit(); + } else { + console.log("Kid data fetched successfully : ",kidData.data) + global.CERTIFICATE_ISSUER_KID = kidData.data } // global.CERTIFICATE_ISSUER_KID = kidData.data } From c0b83ba17ebb696f6c2b9eb89bc37bbafbf93fb3 Mon Sep 17 00:00:00 2001 From: VISHNUDAS-tunerlabse Date: Tue, 13 Dec 2022 15:42:13 +0530 Subject: [PATCH 48/92] console added to debug --- envVariables.js | 1 + 1 file changed, 1 insertion(+) diff --git a/envVariables.js b/envVariables.js index f828914a..a36d5e04 100644 --- a/envVariables.js +++ b/envVariables.js @@ -157,6 +157,7 @@ async function getKid(){ console.log("Kid data fetched successfully : ",kidData.data) global.CERTIFICATE_ISSUER_KID = kidData.data } + console.log(JSON.stringify(kidData)) // global.CERTIFICATE_ISSUER_KID = kidData.data } }; From 95a4dee62f3175c83519fd5a230ef3e22009900b Mon Sep 17 00:00:00 2001 From: VISHNUDAS-tunerlabse Date: Tue, 13 Dec 2022 16:46:42 +0530 Subject: [PATCH 49/92] console is added to check callback call, kafka condition added to avoid looping --- generics/kafka/consumers/projectCertificate.js | 3 ++- module/userProjects/helper.js | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/generics/kafka/consumers/projectCertificate.js b/generics/kafka/consumers/projectCertificate.js index 69389e77..bdb28983 100644 --- a/generics/kafka/consumers/projectCertificate.js +++ b/generics/kafka/consumers/projectCertificate.js @@ -23,7 +23,8 @@ var messageReceived = function (message) { let parsedMessage = JSON.parse( message.value ); if ( parsedMessage.status == CONSTANTS.common.SUBMITTED_STATUS && parsedMessage.certificate && - Object.keys(parsedMessage.certificate).length > 0 + Object.keys(parsedMessage.certificate).length > 0 && + !parsedMessage.certificate.osid ) { await userProjectsHelper.generateCertificate( parsedMessage ); } diff --git a/module/userProjects/helper.js b/module/userProjects/helper.js index fcf67ea1..0d7e5b74 100644 --- a/module/userProjects/helper.js +++ b/module/userProjects/helper.js @@ -2628,6 +2628,11 @@ module.exports = class UserProjectsHelper { static certificateCallback(transactionId, osid) { return new Promise(async (resolve, reject) => { try { + // adding comments to check call back is called properly or not + console.log("<==================callback called====================>",transactionId,osid) + console.log("transactionId :",transactionId) + console.log("osid :",osid) + console.log("<==================callback called====================>") // callback request structure nested so validating transactionId and osid here instead in validator. if ( transactionId == "" || osid == "" ) { throw { From c042ad250f92a24d30f8ee073bb0b798da158dc4 Mon Sep 17 00:00:00 2001 From: VISHNUDAS-tunerlabse Date: Tue, 13 Dec 2022 16:59:39 +0530 Subject: [PATCH 50/92] kafka consumer check for eligible key absent added --- generics/kafka/consumers/projectCertificate.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/generics/kafka/consumers/projectCertificate.js b/generics/kafka/consumers/projectCertificate.js index bdb28983..66823a75 100644 --- a/generics/kafka/consumers/projectCertificate.js +++ b/generics/kafka/consumers/projectCertificate.js @@ -24,7 +24,7 @@ var messageReceived = function (message) { if ( parsedMessage.status == CONSTANTS.common.SUBMITTED_STATUS && parsedMessage.certificate && Object.keys(parsedMessage.certificate).length > 0 && - !parsedMessage.certificate.osid + !parsedMessage.certificate.eligible ) { await userProjectsHelper.generateCertificate( parsedMessage ); } From fb4fe9d2a0bbdd0849b9f616ab106e7114c201bf Mon Sep 17 00:00:00 2001 From: VISHNUDAS-tunerlabse Date: Tue, 13 Dec 2022 17:39:54 +0530 Subject: [PATCH 51/92] console added to check callback && updated rc response check condition --- controllers/v1/userProjects.js | 2 ++ generics/services/certificate.js | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/controllers/v1/userProjects.js b/controllers/v1/userProjects.js index e674ed07..052a4f9b 100644 --- a/controllers/v1/userProjects.js +++ b/controllers/v1/userProjects.js @@ -997,6 +997,8 @@ module.exports = class UserProjects extends Abstract { async certificateCallback(req) { return new Promise(async (resolve, reject) => { try { + //console request body to check if callback is coming or not and to check any structural change is there or not + console.log("-------------callback request body------------",JSON.stringify(req.body)) let certificateDetails = await userProjectsHelper.certificateCallback( req.body.data.transactionId, req.body.data.osid ); return resolve({ message: certificateDetails.message, diff --git a/generics/services/certificate.js b/generics/services/certificate.js index 3bd0f997..6fd193c6 100644 --- a/generics/services/certificate.js +++ b/generics/services/certificate.js @@ -48,7 +48,7 @@ const createCertificate = function (bodyData) { } else { let response = data.body; console.log("certificate success response: ",JSON.stringify(response)) - if( response.params.status === "SUCCESSFUL" ) { + if( response.params && response.params.status && response.params.status === "SUCCESSFUL" ) { result["data"] = response.result; } else { result.success = false; From 8b3de75febf4aa5c1326574e479f019dc7dfd6a4 Mon Sep 17 00:00:00 2001 From: VISHNUDAS-tunerlabse Date: Tue, 13 Dec 2022 21:38:23 +0530 Subject: [PATCH 52/92] kafka push for sync mode removed --- generics/constants/api-responses.js | 3 ++- module/userProjects/helper.js | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/generics/constants/api-responses.js b/generics/constants/api-responses.js index 0ce30788..7404a87a 100644 --- a/generics/constants/api-responses.js +++ b/generics/constants/api-responses.js @@ -133,5 +133,6 @@ module.exports = { "CERTIFICATE_TEMPLATE_NOT_FOUND" : "Certificate template details not found", "CERTIFICATE_GENERATION_FAILED" : "Certificate generation failed", "NOT_ELIGIBLE_FOR_CERTIFICATE" : "Project is not eligible for certificate", - "ISSUER_KID_NOT_FOUND" : "Failed to fetch certificate issuer kid" + "ISSUER_KID_NOT_FOUND" : "Failed to fetch certificate issuer kid", + "PROJECT_SUBMITTED_FOR_REISSUE" : "Submitted for project certificate reIssue" }; diff --git a/module/userProjects/helper.js b/module/userProjects/helper.js index 0d7e5b74..f1ba9f0c 100644 --- a/module/userProjects/helper.js +++ b/module/userProjects/helper.js @@ -2601,7 +2601,6 @@ module.exports = class UserProjectsHelper { }, updateObject ); - await kafkaProducersHelper.pushProjectToKafka(updatedProject); } return resolve( { success: true @@ -2850,7 +2849,7 @@ module.exports = class UserProjectsHelper { return resolve({ success: true, - message: CONSTANTS.apiResponses.PROJECT_CERTIFICATE_GENERATED, + message: CONSTANTS.apiResponses.PROJECT_SUBMITTED_FOR_REISSUE, data : { _id : userProject[0]._id } From 691259b2bb8a01176b0727f5e5e4037a08276270 Mon Sep 17 00:00:00 2001 From: VISHNUDAS-tunerlabse Date: Thu, 15 Dec 2022 12:33:16 +0530 Subject: [PATCH 53/92] guest access path updated with certificateCallback --- generics/middleware/authenticator.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/generics/middleware/authenticator.js b/generics/middleware/authenticator.js index b3599204..b34628ab 100644 --- a/generics/middleware/authenticator.js +++ b/generics/middleware/authenticator.js @@ -49,7 +49,7 @@ module.exports = async function (req, res, next, token = "") { // Allow search endpoints for non-logged in users. let guestAccess = false; - let guestAccessPaths = ["/dataPipeline/","/templates/details"]; + let guestAccessPaths = ["/dataPipeline/","/templates/details","userProjects/certificateCallback"]; await Promise.all(guestAccessPaths.map(async function (path) { if (req.path.includes(path)) { guestAccess = true; From 88e5586b5fa71775b8fe3555479daf68e6187b4e Mon Sep 17 00:00:00 2001 From: VISHNUDAS-tunerlabse Date: Tue, 20 Dec 2022 17:17:37 +0530 Subject: [PATCH 54/92] referenceId added to project tasks --- module/certificateValidations/helper.js | 6 +++--- module/userProjects/helper.js | 5 +++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/module/certificateValidations/helper.js b/module/certificateValidations/helper.js index fc9a7a32..82a591cb 100644 --- a/module/certificateValidations/helper.js +++ b/module/certificateValidations/helper.js @@ -152,12 +152,12 @@ function _validateCriteriaConditions(condition, data) { } } else if ( projectTasks && projectTasks.length > 0 && condition.taskDetails.length > 0 ) { - - // specific task Id or Ids are passed for attachment validation + + // specific task Id( from projectTemplates ) or Ids are passed for attachment validation for ( let tasksIndex = 0; tasksIndex < projectTasks.length; tasksIndex++ ) { for ( let taskDetailsPointer = 0; taskDetailsPointer < condition.taskDetails.length; taskDetailsPointer++ ) { // get attachments data of specified task/ tasks - if ( projectTasks[tasksIndex]._id == condition.taskDetails[taskDetailsPointer] && projectTasks[tasksIndex][condition.key] && projectTasks[tasksIndex][condition.key].length > 0 ) { + if ( projectTasks[tasksIndex].referenceId == condition.taskDetails[taskDetailsPointer] && projectTasks[tasksIndex][condition.key] && projectTasks[tasksIndex][condition.key].length > 0 ) { tasksAttachments.push(...projectTasks[tasksIndex][condition.key]) } } diff --git a/module/userProjects/helper.js b/module/userProjects/helper.js index f1ba9f0c..6393859d 100644 --- a/module/userProjects/helper.js +++ b/module/userProjects/helper.js @@ -379,6 +379,7 @@ module.exports = class UserProjectsHelper { status: HTTP_STATUS_CODE['bad_request'].status } } + // push project details to kafka await kafkaProducersHelper.pushProjectToKafka(projectUpdated); @@ -1376,7 +1377,6 @@ module.exports = class UserProjectsHelper { static userAssignedProjectCreation(templateId, userId, userToken) { return new Promise(async (resolve, reject) => { try { - const projectTemplateData = await projectTemplateQueries.templateDocument({ status: CONSTANTS.common.PUBLISHED, @@ -1415,7 +1415,7 @@ module.exports = class UserProjectsHelper { await projectTemplatesHelper.tasksAndSubTasks( projectTemplateData[0]._id ); - + if (tasksAndSubTasks.length > 0) { result.tasks = _projectTask(tasksAndSubTasks); @@ -3130,6 +3130,7 @@ function _projectTask(tasks, isImportedFromLibrary = false, parentTaskId = "") { singleTask.isDeletable = true; } + singleTask.referenceId = singleTask._id; singleTask.createdAt = singleTask.createdAt ? singleTask.createdAt : new Date(); singleTask.updatedAt = new Date(); singleTask._id = UTILS.isValidMongoId(singleTask._id.toString()) ? uuidv4() : singleTask._id; From c4bdbcf5710e3c10e75064d18bf6c2545f771f8b Mon Sep 17 00:00:00 2001 From: VISHNUDAS-tunerlabse Date: Wed, 21 Dec 2022 13:13:39 +0530 Subject: [PATCH 55/92] comparing with task externalId --- module/certificateValidations/helper.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module/certificateValidations/helper.js b/module/certificateValidations/helper.js index 82a591cb..e1d0df51 100644 --- a/module/certificateValidations/helper.js +++ b/module/certificateValidations/helper.js @@ -157,7 +157,7 @@ function _validateCriteriaConditions(condition, data) { for ( let tasksIndex = 0; tasksIndex < projectTasks.length; tasksIndex++ ) { for ( let taskDetailsPointer = 0; taskDetailsPointer < condition.taskDetails.length; taskDetailsPointer++ ) { // get attachments data of specified task/ tasks - if ( projectTasks[tasksIndex].referenceId == condition.taskDetails[taskDetailsPointer] && projectTasks[tasksIndex][condition.key] && projectTasks[tasksIndex][condition.key].length > 0 ) { + if ( projectTasks[tasksIndex].externalId == condition.taskDetails[taskDetailsPointer] && projectTasks[tasksIndex][condition.key] && projectTasks[tasksIndex][condition.key].length > 0 ) { tasksAttachments.push(...projectTasks[tasksIndex][condition.key]) } } From 141ceafcb0df875c631c576599d96a026f02dfc1 Mon Sep 17 00:00:00 2001 From: VISHNUDAS-tunerlabse Date: Wed, 21 Dec 2022 13:50:49 +0530 Subject: [PATCH 56/92] refereceid key removed --- module/userProjects/helper.js | 1 - 1 file changed, 1 deletion(-) diff --git a/module/userProjects/helper.js b/module/userProjects/helper.js index 6393859d..cb0daa73 100644 --- a/module/userProjects/helper.js +++ b/module/userProjects/helper.js @@ -3130,7 +3130,6 @@ function _projectTask(tasks, isImportedFromLibrary = false, parentTaskId = "") { singleTask.isDeletable = true; } - singleTask.referenceId = singleTask._id; singleTask.createdAt = singleTask.createdAt ? singleTask.createdAt : new Date(); singleTask.updatedAt = new Date(); singleTask._id = UTILS.isValidMongoId(singleTask._id.toString()) ? uuidv4() : singleTask._id; From 947d9a99e7455651176f34d56f60311e3b6d4bf6 Mon Sep 17 00:00:00 2001 From: VISHNUDAS-tunerlabse Date: Wed, 21 Dec 2022 15:59:58 +0530 Subject: [PATCH 57/92] reference id replacement issue resolved --- module/certificateValidations/helper.js | 2 +- module/userProjects/helper.js | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/module/certificateValidations/helper.js b/module/certificateValidations/helper.js index e1d0df51..82a591cb 100644 --- a/module/certificateValidations/helper.js +++ b/module/certificateValidations/helper.js @@ -157,7 +157,7 @@ function _validateCriteriaConditions(condition, data) { for ( let tasksIndex = 0; tasksIndex < projectTasks.length; tasksIndex++ ) { for ( let taskDetailsPointer = 0; taskDetailsPointer < condition.taskDetails.length; taskDetailsPointer++ ) { // get attachments data of specified task/ tasks - if ( projectTasks[tasksIndex].externalId == condition.taskDetails[taskDetailsPointer] && projectTasks[tasksIndex][condition.key] && projectTasks[tasksIndex][condition.key].length > 0 ) { + if ( projectTasks[tasksIndex].referenceId == condition.taskDetails[taskDetailsPointer] && projectTasks[tasksIndex][condition.key] && projectTasks[tasksIndex][condition.key].length > 0 ) { tasksAttachments.push(...projectTasks[tasksIndex][condition.key]) } } diff --git a/module/userProjects/helper.js b/module/userProjects/helper.js index cb0daa73..1490327d 100644 --- a/module/userProjects/helper.js +++ b/module/userProjects/helper.js @@ -3120,7 +3120,7 @@ function _attachmentInformation ( attachmentWithSourcePath = [], linkAttachments function _projectTask(tasks, isImportedFromLibrary = false, parentTaskId = "") { tasks.forEach(singleTask => { - + singleTask.externalId = singleTask.externalId ? singleTask.externalId : singleTask.name.toLowerCase(); singleTask.type = singleTask.type ? singleTask.type : CONSTANTS.common.SIMPLE_TASK_TYPE; singleTask.status = singleTask.status ? singleTask.status : CONSTANTS.common.NOT_STARTED_STATUS; @@ -3129,7 +3129,9 @@ function _projectTask(tasks, isImportedFromLibrary = false, parentTaskId = "") { if (!singleTask.hasOwnProperty("isDeletable")) { singleTask.isDeletable = true; } - + if ( UTILS.isValidMongoId(singleTask._id.toString()) ) { + singleTask.referenceId = singleTask._id; + } singleTask.createdAt = singleTask.createdAt ? singleTask.createdAt : new Date(); singleTask.updatedAt = new Date(); singleTask._id = UTILS.isValidMongoId(singleTask._id.toString()) ? uuidv4() : singleTask._id; @@ -3168,7 +3170,7 @@ function _projectTask(tasks, isImportedFromLibrary = false, parentTaskId = "") { } }) - + return tasks; } From a138d29a6f2fe7a5d173feb64dd39f3274e71a09 Mon Sep 17 00:00:00 2001 From: VISHNUDAS-tunerlabse Date: Wed, 21 Dec 2022 16:19:59 +0530 Subject: [PATCH 58/92] referenceId replacement fix --- module/userProjects/helper.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/module/userProjects/helper.js b/module/userProjects/helper.js index 1490327d..329c9927 100644 --- a/module/userProjects/helper.js +++ b/module/userProjects/helper.js @@ -121,6 +121,7 @@ module.exports = class UserProjectsHelper { "appInformation", "status" ]); + if (!userProject.length > 0) { throw { @@ -3130,7 +3131,7 @@ function _projectTask(tasks, isImportedFromLibrary = false, parentTaskId = "") { singleTask.isDeletable = true; } if ( UTILS.isValidMongoId(singleTask._id.toString()) ) { - singleTask.referenceId = singleTask._id; + singleTask.referenceId = singleTask._id.toString(); } singleTask.createdAt = singleTask.createdAt ? singleTask.createdAt : new Date(); singleTask.updatedAt = new Date(); From 6c6f0ffcc912e8e64273e453ac26d05cb340941a Mon Sep 17 00:00:00 2001 From: VISHNUDAS-tunerlabse Date: Wed, 21 Dec 2022 17:10:18 +0530 Subject: [PATCH 59/92] completedDate added to projection --- module/userProjects/helper.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/module/userProjects/helper.js b/module/userProjects/helper.js index 329c9927..fe55695c 100644 --- a/module/userProjects/helper.js +++ b/module/userProjects/helper.js @@ -2709,7 +2709,8 @@ module.exports = class UserProjectsHelper { "certificate.status", "certificate.eligible", "certificate.message", - "certificate.issuedOn" + "certificate.issuedOn", + "completedDate" ]); if ( !userProject.length > 0 ) { From 5741c0f0c52b6c3d7ca4b5f63a10d1885fc86675 Mon Sep 17 00:00:00 2001 From: VISHNUDAS-tunerlabse Date: Wed, 28 Dec 2022 19:54:26 +0530 Subject: [PATCH 60/92] console added to check kid issue --- module/userProjects/helper.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/module/userProjects/helper.js b/module/userProjects/helper.js index fe55695c..5929bff9 100644 --- a/module/userProjects/helper.js +++ b/module/userProjects/helper.js @@ -2482,7 +2482,7 @@ module.exports = class UserProjectsHelper { static createCertificatePayload(data) { return new Promise(async (resolve, reject) => { try { - + console.log("Certificate issuer Kid: ",CERTIFICATE_ISSUER_KID) // get downloadable url for certificate template if ( data.certificate.templateUrl && data.certificate.templateUrl !== "" ) { let certificateTemplateDownloadableUrl = @@ -2536,6 +2536,7 @@ module.exports = class UserProjectsHelper { return resolve(certificateData); } catch (error) { + console.log("error:",error.message) return resolve({ success: false, message: error.message From 70bf0b5936e0a0efeebec1bc3ff969af4432d9e7 Mon Sep 17 00:00:00 2001 From: VISHNUDAS-tunerlabse Date: Wed, 28 Dec 2022 20:08:24 +0530 Subject: [PATCH 61/92] ISSUER_KID check added from env --- envVariables.js | 3 +++ routes/index.js | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/envVariables.js b/envVariables.js index a36d5e04..307f75b3 100644 --- a/envVariables.js +++ b/envVariables.js @@ -151,6 +151,9 @@ async function getKid(){ let kidData = await certificateService.getCertificateIssuerKid(); if( !kidData.success ) { console.log("failed to get kid value from registry service : ",kidData) + if( process.env.CERTIFICATE_ISSUER_KID && process.env.CERTIFICATE_ISSUER_KID != "" ) { + global.CERTIFICATE_ISSUER_KID = process.env.CERTIFICATE_ISSUER_KID; + } // console.log("Server stoped . Failed to set certificate issuer Kid value") // process.exit(); } else { diff --git a/routes/index.js b/routes/index.js index cad0fe2a..d9ada824 100644 --- a/routes/index.js +++ b/routes/index.js @@ -89,7 +89,7 @@ module.exports = function (app) { } console.log('-------------------Response log starts here-------------------'); - console.log(result); + console.log(JSON.stringify(result)); console.log('-------------------Response log ends here-------------------'); } catch (error) { From 03e9784e2be18398aa76c9685817ca40c1def32c Mon Sep 17 00:00:00 2001 From: VISHNUDAS-tunerlabse Date: Thu, 29 Dec 2022 13:37:48 +0530 Subject: [PATCH 62/92] solution model updated --- models/solutions.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/models/solutions.js b/models/solutions.js index dacb3a09..536c67c3 100644 --- a/models/solutions.js +++ b/models/solutions.js @@ -97,6 +97,8 @@ module.exports = { default: 1 }, reportInformation : Object, - certificateTemplateId : "ObjectId" + certificateTemplateId : "ObjectId", + rootOrganisations : Array, + createdFor : Array } }; \ No newline at end of file From 53f1db10ec32b6122b095eb4b1fe8a95f7f13cbc Mon Sep 17 00:00:00 2001 From: VISHNUDAS-tunerlabse Date: Tue, 3 Jan 2023 12:35:57 +0530 Subject: [PATCH 63/92] setUserProfileInProject migration script added --- .../setUserProfileInProjects.js | 407 ++++++++++++++++++ 1 file changed, 407 insertions(+) create mode 100644 migrations/userProfileAndRoleMismatchInProjects/setUserProfileInProjects.js diff --git a/migrations/userProfileAndRoleMismatchInProjects/setUserProfileInProjects.js b/migrations/userProfileAndRoleMismatchInProjects/setUserProfileInProjects.js new file mode 100644 index 00000000..c739d924 --- /dev/null +++ b/migrations/userProfileAndRoleMismatchInProjects/setUserProfileInProjects.js @@ -0,0 +1,407 @@ +/** + * name : updateUserProfileInProjects.js + * author : Priyanka Pradeep + * created-date : 10-Nov-2022 + * Description : Migration script for update userProfile in project + */ + + const path = require("path"); + let rootPath = path.join(__dirname, '../../') + require('dotenv').config({ path: rootPath+'/.env' }) + + let _ = require("lodash"); + let mongoUrl = process.env.MONGODB_URL; + let dbName = mongoUrl.split("/").pop(); + let url = mongoUrl.split(dbName)[0]; + var MongoClient = require('mongodb').MongoClient; + var ObjectId = require('mongodb').ObjectID; + + var fs = require('fs'); + const request = require('request'); + + const userServiceUrl = "http://learner-service:9000"; + const endPoint = "/v1/location/search"; + const userReadEndpoint = "/private/user/v1/read"; + +(async () => { + + let connection = await MongoClient.connect(url, { useNewUrlParser: true }); + let db = connection.db(dbName); + try { + + let updatedProjectIds = []; + + //get all projects id where user profile is not there. + let projectDocument = await db.collection('projects').find({ + userRoleInformation: {$exists : true}, + userProfile: {$exists : false}, + }).project({ "_id": 1}).toArray(); + + + let chunkOfProjectDocument = _.chunk(projectDocument, 10); + let projectIds; + + for (let pointerToProject = 0; pointerToProject < chunkOfProjectDocument.length; pointerToProject++) { + + projectIds = await chunkOfProjectDocument[pointerToProject].map( + projectDoc => { + return projectDoc._id; + } + ); + + //get user ids of project without profile + let userIdsWithoutProfile = await db.collection('projects').find({ + _id: { $in : projectIds } + }).project({ + "_id" : 1, + "userId" : 1 + }).toArray(); + + //loop userIds- These user profiles are absent in project + for ( let count = 0; count < userIdsWithoutProfile.length; count++ ) { + + let projectIdWithoutUserProfile = userIdsWithoutProfile[count]._id; + let userId = userIdsWithoutProfile[count].userId; + + //call profile api to get user profile + let profile = await profileReadPrivate(userId); + + // update project with profile + if( profile.success && profile.data && profile.data.response ) { + let updateObject = { + "$set" : {} + }; + updateObject["$set"]["userProfile"] = profile.data.response; + + await db.collection('projects').findOneAndUpdate({ + "_id" : projectIdWithoutUserProfile + },updateObject); + } + + } + + + let projectDocuments = await db.collection('projects').find({ + _id: { $in : projectIds}, + userProfile: {$exists : true} + }).project({ + "_id": 1, + "userRoleInformation" : 1, + "userProfile" : 1 + }).toArray(); + + //loop all projects + for ( let count = 0; count < projectDocuments.length; count++ ) { + + let project = projectDocuments[count]; + let userProfile = project.userProfile; + + + let updateUserProfileRoleInformation = false; // Flag to see if roleInformation i.e. userProfile.profileUserTypes has to be updated based on userRoleInfromation.roles + + if(project.userRoleInformation.role) { // Check if userRoleInformation has role value. + let rolesInUserRoleInformation = project.userRoleInformation.role.split(","); // userRoleInfomration.role can be multiple with comma separated. + + let resetCurrentUserProfileRoles = false; // Flag to reset current userProfile.profileUserTypes i.e. if current role in profile is not at all there in userRoleInformation.roles + // Check if userProfile.profileUserTypes exists and is an array of length > 0 + if(userProfile.profileUserTypes && Array.isArray(userProfile.profileUserTypes) && userProfile.profileUserTypes.length >0) { + + // Loop through current roles in userProfile.profileUserTypes + for (let pointerToCurrentProfileUserTypes = 0; pointerToCurrentProfileUserTypes < userProfile.profileUserTypes.length; pointerToCurrentProfileUserTypes++) { + const currentProfileUserType = userProfile.profileUserTypes[pointerToCurrentProfileUserTypes]; + + if(currentProfileUserType.subType && currentProfileUserType.subType !== null) { // If the role has a subType + + // Check if subType exists in userRoleInformation role, if not means profile data is old and should be reset. + if(!project.userRoleInformation.role.toUpperCase().includes(currentProfileUserType.subType.toUpperCase())) { + resetCurrentUserProfileRoles = true; // Reset userProfile.profileUserTypes + break; + } + } else { // If the role subType is null or is not there + + // Check if type exists in userRoleInformation role, if not means profile data is old and should be reset. + if(!project.userRoleInformation.role.toUpperCase().includes(currentProfileUserType.type.toUpperCase())) { + resetCurrentUserProfileRoles = true; // Reset userProfile.profileUserTypes + break; + } + } + } + } + if(resetCurrentUserProfileRoles) { // Reset userProfile.profileUserTypes + userProfile.profileUserTypes = new Array; + } + + // Loop through each subRole in userRoleInformation + for (let pointerToRolesInUserInformation = 0; pointerToRolesInUserInformation < rolesInUserRoleInformation.length; pointerToRolesInUserInformation++) { + const subRole = rolesInUserRoleInformation[pointerToRolesInUserInformation]; + + // Check if userProfile.profileUserTypes exists and is an array of length > 0 + if(userProfile.profileUserTypes && Array.isArray(userProfile.profileUserTypes) && userProfile.profileUserTypes.length >0) { + if(!_.find(userProfile.profileUserTypes, { 'type': subRole.toLowerCase() }) && !_.find(userProfile.profileUserTypes, { 'subType': subRole.toLowerCase() })) { + updateUserProfileRoleInformation = true; // Need to update userProfile.profileUserTypes + if(subRole.toUpperCase() === "TEACHER") { // If subRole is not teacher + userProfile.profileUserTypes.push({ + "subType" : null, + "type" : "teacher" + }) + } else { // If subRole is not teacher + userProfile.profileUserTypes.push({ + "subType" : subRole.toLowerCase(), + "type" : "administrator" + }) + } + } + } else { // Make a new entry if userProfile.profileUserTypes is empty or does not exist. + updateUserProfileRoleInformation = true; // Need to update userProfile.profileUserTypes + userProfile.profileUserTypes = new Array; + if(subRole.toUpperCase() === "TEACHER") { // If subRole is teacher + userProfile.profileUserTypes.push({ + "subType" : null, + "type" : "teacher" + }) + } else { // If subRole is not teacher + userProfile.profileUserTypes.push({ + "subType" : subRole.toLowerCase(), + "type" : "administrator" + }) + } + } + } + } + + // Create location only object from userRoleInformation + let userRoleInformationLocationObject = _.omit(project.userRoleInformation, ['role']); + + // All location keys from userRoleInformation + let userRoleInfomrationLocationKeys = Object.keys(userRoleInformationLocationObject); + + let updateUserProfileLocationInformation = false; // Flag to see if userLocations i.e. userProfile.userLocations has to be updated based on userRoleInfromation location values + + // Loop through all location keys. + for (let pointerToUserRoleInfromationLocationKeys = 0; pointerToUserRoleInfromationLocationKeys < userRoleInfomrationLocationKeys.length; pointerToUserRoleInfromationLocationKeys++) { + + const locationType = userRoleInfomrationLocationKeys[pointerToUserRoleInfromationLocationKeys]; // e.g. state, district, school + const locationValue = userRoleInformationLocationObject[locationType]; // Location UUID values or school code. + + // Check if userProfile.userLocations exists and is an array of length > 0 + if(userProfile.userLocations && Array.isArray(userProfile.userLocations) && userProfile.userLocations.length >0) { + + if(locationType === "school") { // If location type school exist check if same is there in userProfile.userLocations + if(!_.find(userProfile.userLocations, { 'type': "school", 'code': locationValue })) { + updateUserProfileLocationInformation = true; // School does not exist in userProfile.userLocations, update entire userProfile.userLocations + break; + } + } else { // Check if location type is there in userProfile.userLocations and has same value as userRoleInformation + if(!_.find(userProfile.userLocations, { 'type': locationType, 'id': locationValue })) { + updateUserProfileLocationInformation = true; // Location does not exist in userProfile.userLocations, update entire userProfile.userLocations + break; + } + } + } else { + updateUserProfileLocationInformation = true; + break; + } + } + + + if(userProfile.userLocations && Array.isArray(userProfile.userLocations) && userProfile.userLocations.length >0) { + if(userProfile.userLocations.length != userRoleInfomrationLocationKeys.length) { + updateUserProfileLocationInformation = true; + } + } + + // If userProfile.userLocations has to be updated, get all values and set in userProfile. + if(updateUserProfileLocationInformation) { + + //update userLocations in userProfile + let locationIds = []; + let locationCodes = []; + let userLocations = new Array; + + userRoleInfomrationLocationKeys.forEach( requestedDataKey => { + if (checkIfValidUUID(userRoleInformationLocationObject[requestedDataKey])) { + locationIds.push(userRoleInformationLocationObject[requestedDataKey]); + } else { + locationCodes.push(userRoleInformationLocationObject[requestedDataKey]); + } + }) + + //query for fetch location using id + if ( locationIds.length > 0 ) { + let locationQuery = { + "id" : locationIds + } + + let entityData = await locationSearch(locationQuery); + if ( entityData.success ) { + userLocations = entityData.data; + } + } + + // query for fetch location using code + if ( locationCodes.length > 0 ) { + let codeQuery = { + "code" : locationCodes + } + + let entityData = await locationSearch(codeQuery); + if ( entityData.success ) { + userLocations = userLocations.concat(entityData.data); + } + } + + if ( userLocations.length > 0 ) { + userProfile["userLocations"] = userLocations; + } + } + + + //update projects if userProfile role or location information is incorrect + if ( updateUserProfileRoleInformation || updateUserProfileLocationInformation ) { + + let updateObject = { + "$set" : {} + }; + if(updateUserProfileRoleInformation) { + updateObject["$set"]["userProfile.profileUserTypes"] = userProfile.profileUserTypes; + updateObject["$set"]["userProfile.userRoleMismatchFoundAndUpdated"] = true; + } + if(updateUserProfileLocationInformation) { + updateObject["$set"]["userProfile.userLocations"] = userProfile.userLocations; + updateObject["$set"]["userProfile.userLocationsMismatchFoundAndUpdated"] = true; + } + + await db.collection('projects').findOneAndUpdate({ + "_id" : project._id + },updateObject); + + updatedProjectIds.push(project._id.toString()); + } + + } + + //write updated project ids to file + fs.writeFile( + 'updatedProjectIds.json', + + JSON.stringify(updatedProjectIds), + + function (err) { + if (err) { + console.error('Crap happens'); + } + } + ); + } + + function locationSearch ( filterData ) { + return new Promise(async (resolve, reject) => { + try { + + let bodyData={}; + bodyData["request"] = {}; + bodyData["request"]["filters"] = filterData; + const url = userServiceUrl + endPoint; + const options = { + headers : { + "content-type": "application/json" + }, + json : bodyData + }; + + request.post(url,options,requestCallback); + + let result = { + success : true + }; + + function requestCallback(err, data) { + if (err) { + result.success = false; + } else { + let response = data.body; + if( response.responseCode === "OK" && + response.result && + response.result.response && + response.result.response.length > 0 + ) { + let entityResult = new Array; + response.result.response.map(entityData => { + let entity = _.omit(entityData, ['identifier']); + entityResult.push(entity); + }) + result["data"] = entityResult; + result["count"] = response.result.count; + } else { + result.success = false; + } + } + return resolve(result); + } + + setTimeout(function () { + return resolve (result = { + success : false + }); + }, 5000); + + } catch (error) { + return reject(error); + } + }) + } + + function profileReadPrivate (userId) { + return new Promise(async (resolve, reject) => { + try { + // <--- Important : This url endpoint is private do not use it for regular workflows ---> + let url = userServiceUrl + userReadEndpoint + "/" + userId; + const options = { + headers : { + "content-type": "application/json" + } + }; + request.get(url,options,userReadCallback); + let result = { + success : true + }; + function userReadCallback(err, data) { + if (err) { + result.success = false; + } else { + + let response = JSON.parse(data.body); + if( response.responseCode === "OK" ) { + result["data"] = response.result; + } else { + result.success = false; + } + + } + return resolve(result); + } + setTimeout(function () { + return resolve (result = { + success : false + }); + }, 5000); + + } catch (error) { + return reject(error); + } + }) + } + + console.log("Updated Project Count : ", updatedProjectIds.length) + console.log("completed") + connection.close(); + } + catch (error) { + console.log(error) + } +})().catch(err => console.error(err)); + +function checkIfValidUUID(value) { + const regexExp = /^[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}$/gi; + return regexExp.test(value); +} \ No newline at end of file From 9d3fc2c16791ee5b423403223160e3478572ea59 Mon Sep 17 00:00:00 2001 From: VISHNUDAS-tunerlabse Date: Tue, 3 Jan 2023 12:58:50 +0530 Subject: [PATCH 64/92] subfolder changed --- .../setUserProfileInProjects.js | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename migrations/{userProfileAndRoleMismatchInProjects => userProfileAbsentInProjects}/setUserProfileInProjects.js (100%) diff --git a/migrations/userProfileAndRoleMismatchInProjects/setUserProfileInProjects.js b/migrations/userProfileAbsentInProjects/setUserProfileInProjects.js similarity index 100% rename from migrations/userProfileAndRoleMismatchInProjects/setUserProfileInProjects.js rename to migrations/userProfileAbsentInProjects/setUserProfileInProjects.js From d41d3c20c21a548393e47cb91a2e15acc792439a Mon Sep 17 00:00:00 2001 From: ankitshahu Date: Wed, 18 Jan 2023 17:45:10 +0530 Subject: [PATCH 65/92] changed from userName to firstName and lastName --- module/userProjects/helper.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module/userProjects/helper.js b/module/userProjects/helper.js index 5929bff9..24887b07 100644 --- a/module/userProjects/helper.js +++ b/module/userProjects/helper.js @@ -2519,7 +2519,7 @@ module.exports = class UserProjectsHelper { let certificateData = { recipient : { id : data.userId, - name : data.userProfile.userName, + name : `${data.userProfile.firstName} ${data.userProfile.lastName}`, type : data.userProfile.profileUserType.type }, templateUrl : data.certificate.templateUrl, From a10b6dffe6c60a7746076bb0b7164e7d46bbbd45 Mon Sep 17 00:00:00 2001 From: ankitshahu Date: Fri, 20 Jan 2023 11:34:21 +0530 Subject: [PATCH 66/92] Fixing reissue certificate --- module/userProjects/helper.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/module/userProjects/helper.js b/module/userProjects/helper.js index 24887b07..51618b9d 100644 --- a/module/userProjects/helper.js +++ b/module/userProjects/helper.js @@ -2813,10 +2813,11 @@ module.exports = class UserProjectsHelper { if ( userProfileData.success && userProfileData.data && userProfileData.data.response && - userProfileData.data.response.userName && - userProfileData.data.response.userName !== "" + userProfileData.data.response.firstName && + userProfileData.data.response.lastName ) { - userProject[0].userProfile.userName = userProfileData.data.response.userName; + userProject[0].userProfile.firstName = userProfileData.data.response.firstName; + userProject[0].userProfile.lastName = userProfileData.data.response.lastName; } else { throw { status: HTTP_STATUS_CODE['bad_request'].status, From 4f7cf552a07ece8b3dcac897be8b32f6940d9f05 Mon Sep 17 00:00:00 2001 From: ankitshahu Date: Fri, 20 Jan 2023 12:13:20 +0530 Subject: [PATCH 67/92] added only first name in validation --- module/userProjects/helper.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module/userProjects/helper.js b/module/userProjects/helper.js index 51618b9d..1d7cf343 100644 --- a/module/userProjects/helper.js +++ b/module/userProjects/helper.js @@ -2814,7 +2814,7 @@ module.exports = class UserProjectsHelper { userProfileData.data && userProfileData.data.response && userProfileData.data.response.firstName && - userProfileData.data.response.lastName + userProfileData.data.response.firstName !== "" ) { userProject[0].userProfile.firstName = userProfileData.data.response.firstName; userProject[0].userProfile.lastName = userProfileData.data.response.lastName; From 33195e6ccfaf490c228b64ddd24d5a25625d0f5b Mon Sep 17 00:00:00 2001 From: ankitshahu Date: Fri, 20 Jan 2023 12:53:08 +0530 Subject: [PATCH 68/92] Debug ED-1078 --- module/userProjects/helper.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/module/userProjects/helper.js b/module/userProjects/helper.js index 1d7cf343..9e6072c5 100644 --- a/module/userProjects/helper.js +++ b/module/userProjects/helper.js @@ -2810,6 +2810,7 @@ module.exports = class UserProjectsHelper { // fetch user data using userId of project and calling the profile API let userProfileData = await userProfileService.profileReadPrivate(userProject[0].userId); + console.log("userProfileData>>>",JSON.stringify(userProfileData)) if ( userProfileData.success && userProfileData.data && userProfileData.data.response && @@ -2843,6 +2844,15 @@ module.exports = class UserProjectsHelper { if ( userProject[0].certificate.osid ) { updateObject["$set"]["certificate.originalTransactionInformation.osid"] = userProject[0].certificate.osid; } + + if (userProject[0].userProfile.firstName ) { + updateObject["$set"]["userProfile.firstName"] = userProject[0].userProfile.firstName; + } + + if (userProject[0].userProfile.lastName ) { + updateObject["$set"]["userProfile.lastName"] = userProject[0].userProfile.lastName; + } + updateObject["$set"]["certificate.reIssuedAt"] = new Date(); await projectQueries.findOneAndUpdate( { From f95c8ba6ca3acdf6430f1bd254bfe3a716c18caf Mon Sep 17 00:00:00 2001 From: ankitshahu Date: Fri, 20 Jan 2023 16:15:40 +0530 Subject: [PATCH 69/92] removed console log --- module/userProjects/helper.js | 1 - 1 file changed, 1 deletion(-) diff --git a/module/userProjects/helper.js b/module/userProjects/helper.js index 9e6072c5..839cd416 100644 --- a/module/userProjects/helper.js +++ b/module/userProjects/helper.js @@ -2810,7 +2810,6 @@ module.exports = class UserProjectsHelper { // fetch user data using userId of project and calling the profile API let userProfileData = await userProfileService.profileReadPrivate(userProject[0].userId); - console.log("userProfileData>>>",JSON.stringify(userProfileData)) if ( userProfileData.success && userProfileData.data && userProfileData.data.response && From 896993a3bac42fd708564d648df3f660e3ab2498 Mon Sep 17 00:00:00 2001 From: VISHNUDAS-tunerlabse Date: Mon, 23 Jan 2023 19:11:39 +0530 Subject: [PATCH 70/92] projectName length restricted --- module/userProjects/helper.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/module/userProjects/helper.js b/module/userProjects/helper.js index 839cd416..fc46cf9b 100644 --- a/module/userProjects/helper.js +++ b/module/userProjects/helper.js @@ -2483,6 +2483,11 @@ module.exports = class UserProjectsHelper { return new Promise(async (resolve, reject) => { try { console.log("Certificate issuer Kid: ",CERTIFICATE_ISSUER_KID) + + if(data.title.length > 75) { + data.title = data.title.substring(0, 75) + '...'; + } + // get downloadable url for certificate template if ( data.certificate.templateUrl && data.certificate.templateUrl !== "" ) { let certificateTemplateDownloadableUrl = From c50395f61fccad153325c41fec24078f7b2b8baa Mon Sep 17 00:00:00 2001 From: ankitshahu Date: Wed, 1 Feb 2023 18:36:09 +0530 Subject: [PATCH 71/92] Added Project Update Script --- .../updatePrivateProgramInProject.js | 152 ++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 migrations/privateProgramInProjects/updatePrivateProgramInProject.js diff --git a/migrations/privateProgramInProjects/updatePrivateProgramInProject.js b/migrations/privateProgramInProjects/updatePrivateProgramInProject.js new file mode 100644 index 00000000..b7763913 --- /dev/null +++ b/migrations/privateProgramInProjects/updatePrivateProgramInProject.js @@ -0,0 +1,152 @@ +/** + * name : updateUserProfileInProjects.js + * author : Priyanka Pradeep + * created-date : 10-Nov-2022 + * Description : Migration script for update userProfile in project + */ + + const path = require("path"); + let rootPath = path.join(__dirname, '../../') + require('dotenv').config({ path: rootPath+'/.env' }) + + let _ = require("lodash"); + let mongoUrl = process.env.MONGODB_URL; + let dbName = mongoUrl.split("/").pop(); + let url = mongoUrl.split(dbName)[0]; + var MongoClient = require('mongodb').MongoClient; + var ObjectId = require('mongodb').ObjectID; + + var fs = require('fs'); + + +(async () => { + + let connection = await MongoClient.connect(url, { useNewUrlParser: true }); + let db = connection.db(dbName); + try { + + let updatedProjectIds = []; + let deletedSolutionIds = []; + let deletedProgramIds = []; + + + + //get all projects id where user profile is not there. + let projectDocument = await db.collection('projects').find({ + userRoleInformation: {$exists : false}, + isAPrivateProgram: true, + }).project({_id:1}).toArray(); + + + let chunkOfProjectDocument = _.chunk(projectDocument, 10); + // console.log(chunkOfProjectDocument) + let projectIds; + + for (let pointerToProject = 0; pointerToProject < chunkOfProjectDocument.length; pointerToProject++) { + projectIds = await chunkOfProjectDocument[pointerToProject].map( + projectDoc => { + return projectDoc._id; + } + ); + + + // get project documents from projects collection in Array + let projectDocuments = await db.collection('projects').find({ + _id: { $in :projectIds } + }).project({}).toArray(); + //iterate project documents one by one + for(let counter = 0; counter < projectDocuments.length; counter++) { + + + if(projectDocuments[counter].hasOwnProperty("solutionId")){ + // find solution document form solution collection + let solutionDocument = await db.collection('solutions').find({ + _id: projectDocuments[counter].solutionId + }).project({}).toArray({}) + //find program document form program collection + if(solutionDocument[0].hasOwnProperty("parentSolutionId")){ + + // find parent solution document in same collection + let parentSolutionDocument = await db.collection('solutions').find({ + _id: solutionDocument[0].parentSolutionId}).project({}).toArray({}); + //varibale to update project document + let updateProjectDocument = { + "$set" : {} + }; + updateProjectDocument["$set"]["solutionId"] = parentSolutionDocument[0]._id + updateProjectDocument["$set"]["isAPrivateProgram"] = parentSolutionDocument[0].isAPrivateProgram + updateProjectDocument["$set"]["solutionInformation"] = { + name: parentSolutionDocument[0].name, + description: parentSolutionDocument[0].description, + externalId: parentSolutionDocument[0].externalId, + _id: parentSolutionDocument[0]._id, + } + updateProjectDocument["$set"]["solutionExternalId"] = parentSolutionDocument[0].externalId, + updateProjectDocument["$set"]["programId"] = parentSolutionDocument[0].programId, + updateProjectDocument["$set"]["programExternalId"] = parentSolutionDocument[0].programExternalId + updateProjectDocument["$set"]["programInformation"] = { + _id : parentSolutionDocument[0].programId, + name : parentSolutionDocument[0].programName, + externalId : parentSolutionDocument[0].programExternalId, + description : parentSolutionDocument[0].programDescription, + isAPrivateProgram : parentSolutionDocument[0].isAPrivateProgram + } + if(projectDocument[counter].hasOwnProperty("userProfile")) + { + let userLocations = projectDocuments[counter].userProfile.userLocations + let userRoleInfomration = {} + //get data in userRoleInfomration key + for(let i = 0; i < userLocations.length; i++){ + if(userLocations[i].type !== "school"){ + userRoleInfomration[userLocations[i].type] = userLocations[i].id + }else{ + userRoleInfomration[userLocations[i].type] = userLocations[i].code + } + } + userRoleInfomration.Role = projectDocuments[counter].userProfile.profileUserType.subType ? projectDocuments[counter].userProfile.profileUserType.subType.toUpperCase() : projectDocuments[counter].userProfile.profileUserType.type.toUpperCase() + updateProjectDocument["$set"]["userRoleInformation"] = userRoleInfomration + } + + + //push all updated and deleted id in arrays and save in file + updatedProjectIds.push(projectDocuments[counter]._id) + deletedSolutionIds.push(projectDocuments[counter].solutionId) + deletedProgramIds.push(projectDocuments[counter].programId) + + //update project documents + await db.collection('projects').findOneAndUpdate({ + "_id" : projectDocuments[counter]._id + },updateProjectDocument); + } + } + + + } + + + + + + //write updated project ids to file + fs.writeFile( + 'updatedProjectIds.json', + + JSON.stringify({updatedProjectIds: updatedProjectIds,deletedProgramIds: deletedProgramIds,deletedSolutionIds: deletedSolutionIds}), + + function (err) { + if (err) { + console.error('Crap happens'); + } + } + ); + } + console.log("Updated Project Count : ", updatedProjectIds.length) + console.log("deleted program Count : ", deletedProgramIds.length) + console.log("deleted solutionId Count : ", deletedSolutionIds.length) + console.log("completed") + connection.close(); + } + catch (error) { + console.log(error) + } +})().catch(err => console.error(err)); \ No newline at end of file From 306b44aa9fdb3eae7144580cc0854b083ed49962 Mon Sep 17 00:00:00 2001 From: ankitshahu Date: Wed, 1 Feb 2023 18:38:07 +0530 Subject: [PATCH 72/92] Added Project Update Script --- .../updatePrivateProgramInProject.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/migrations/privateProgramInProjects/updatePrivateProgramInProject.js b/migrations/privateProgramInProjects/updatePrivateProgramInProject.js index b7763913..6003722e 100644 --- a/migrations/privateProgramInProjects/updatePrivateProgramInProject.js +++ b/migrations/privateProgramInProjects/updatePrivateProgramInProject.js @@ -1,8 +1,8 @@ /** - * name : updateUserProfileInProjects.js - * author : Priyanka Pradeep + * name : updatePrivateProgramInProject.js + * author : Ankit Shahu * created-date : 10-Nov-2022 - * Description : Migration script for update userProfile in project + * Description : Migration script for update project */ const path = require("path"); From 85631c99241f0d011f0b6090ef6e7589c4663fa8 Mon Sep 17 00:00:00 2001 From: ankitshahu Date: Thu, 2 Feb 2023 09:34:01 +0530 Subject: [PATCH 73/92] add delete also --- .../updatePrivateProgramInProject.js | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/migrations/privateProgramInProjects/updatePrivateProgramInProject.js b/migrations/privateProgramInProjects/updatePrivateProgramInProject.js index 6003722e..01fc4d58 100644 --- a/migrations/privateProgramInProjects/updatePrivateProgramInProject.js +++ b/migrations/privateProgramInProjects/updatePrivateProgramInProject.js @@ -58,7 +58,7 @@ for(let counter = 0; counter < projectDocuments.length; counter++) { - if(projectDocuments[counter].hasOwnProperty("solutionId")){ + if(projectDocuments[counter].hasOwnProperty("solutionId") && projectDocuments[counter].isAPrivateProgram){ // find solution document form solution collection let solutionDocument = await db.collection('solutions').find({ _id: projectDocuments[counter].solutionId @@ -119,9 +119,19 @@ },updateProjectDocument); } } - - } + + + await db.collection('solutions').deleteMany({ + _id: { + $in: deletedSolutionIds + } + }) + await db.collection('programs').deleteMany({ + _id: { + $in: deletedProgramIds + } + }) From 19a65074570eb6d06576d6ec1aa0e7be3c743d1a Mon Sep 17 00:00:00 2001 From: ankitshahu Date: Thu, 2 Feb 2023 09:38:43 +0530 Subject: [PATCH 74/92] add delete also --- .../updatePrivateProgramInProject.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/migrations/privateProgramInProjects/updatePrivateProgramInProject.js b/migrations/privateProgramInProjects/updatePrivateProgramInProject.js index 01fc4d58..76df459a 100644 --- a/migrations/privateProgramInProjects/updatePrivateProgramInProject.js +++ b/migrations/privateProgramInProjects/updatePrivateProgramInProject.js @@ -61,10 +61,12 @@ if(projectDocuments[counter].hasOwnProperty("solutionId") && projectDocuments[counter].isAPrivateProgram){ // find solution document form solution collection let solutionDocument = await db.collection('solutions').find({ - _id: projectDocuments[counter].solutionId + _id: projectDocuments[counter].solutionId, + parentSolutionId : {$exists:true}, + isAPrivateProgram : true }).project({}).toArray({}) //find program document form program collection - if(solutionDocument[0].hasOwnProperty("parentSolutionId")){ + if(solutionDocument.length == 1){ // find parent solution document in same collection let parentSolutionDocument = await db.collection('solutions').find({ From 00fb7086c5786569af7aa0b9240c362509c798ac Mon Sep 17 00:00:00 2001 From: ankitshahu Date: Thu, 2 Feb 2023 19:21:38 +0530 Subject: [PATCH 75/92] Added Both Script --- .../deleteAndUpdateAttachmentsInProject.js | 216 ++++++++++++++++++ .../updatePrivateProgramInProject.js | 20 +- 2 files changed, 225 insertions(+), 11 deletions(-) create mode 100644 migrations/privateProgramInProjects/deleteAndUpdateAttachmentsInProject.js diff --git a/migrations/privateProgramInProjects/deleteAndUpdateAttachmentsInProject.js b/migrations/privateProgramInProjects/deleteAndUpdateAttachmentsInProject.js new file mode 100644 index 00000000..348b8ece --- /dev/null +++ b/migrations/privateProgramInProjects/deleteAndUpdateAttachmentsInProject.js @@ -0,0 +1,216 @@ +const path = require("path"); +let rootPath = path.join(__dirname, '../../') +require('dotenv').config({ path: rootPath+'/.env' }) + +let _ = require("lodash"); +let mongoUrl = process.env.MONGODB_URL; +let dbName = mongoUrl.split("/").pop(); +let url = mongoUrl.split(dbName)[0]; +var MongoClient = require('mongodb').MongoClient; +var ObjectId = require('mongodb').ObjectID; +var request = require('request'); +var fs = require('fs'); +const { at } = require("lodash"); + +const filePathUrl = "https://samikshaprod.blob.core.windows.net/samiksha/"; + + +(async () => { + + let connection = await MongoClient.connect(url, { useNewUrlParser: true }); + let db = connection.db(dbName); + try { + // check project attachments for isUploaded = false data and remove the attachment object + let collectionDocs = await db.collection("projectsss").find({ + "$or": [ + { + "attachments.isUploaded": { + "$exists": true + },"attachments.isUploaded": false + }, + { + "tasks.attachments.isUploaded": { + "$exists": true + },"tasks.attachments.isUploaded": false + } + ] + }).project({_id:1}).toArray(); + + //varibale to store all projectIds which are updated + let projectIds = []; + collectionDocs.forEach( eachDoc => { + projectIds.push(eachDoc._id); + }) + + let chunkOfProjectIds = _.chunk(projectIds, 5); + let UpdatedProjectId = [] + //loop project chunks + for ( let chunkPointer = 0; chunkPointer < chunkOfProjectIds.length; chunkPointer++ ) { + + //chunk of project ids + let projectId = chunkOfProjectIds[chunkPointer]; + + //loop for chunk of project id in chunk + for ( let projectIdpointer = 0 ; projectIdpointer < projectId.length; projectIdpointer++ ) { + let id = projectId[projectIdpointer]; + + //pull project Data from DB + let pullProjectData = await db.collection("projectsss").find({ + _id: id + }).project({}).toArray({}) + + //store in varibale to avoid using [0] + pullProjectData = pullProjectData[0] + + //update Object + let updateObject = { + "$set" : {} + } + + //check if project Document has attachments or not if present then checks length of attachment it should be greater than 0 + if( pullProjectData.hasOwnProperty('attachments') && pullProjectData.attachments.length > 0 ) { + + //varibale to store updated attachment objects + let attachmentsPresent = [] + + //for loop for each attachment + for(let j = 0; j< pullProjectData.attachments.length; j++){ + + //check if isUploaded key is present or not and isUploaded should be false and checks type of attachment + if(pullProjectData.attachments[j].hasOwnProperty("isUploaded") && !pullProjectData.attachments[j].isUploaded && pullProjectData.attachments[j].type !== "link"){ + + //checks if document is present or not + let documentExist = await getDocumentStatus( pullProjectData.attachments[j].sourcePath) + //if present then push object to new array and update isUploaded to true + if(documentExist.success){ + pullProjectData.attachments[j].isUploaded = true + attachmentsPresent.push(pullProjectData.attachments[j]) + } + }else{ + //if isUploaded is not present then push object to new array and if type is link then also + attachmentsPresent.push(pullProjectData.attachments[j]) + } + } + //assign attachmentPresent object to update variable + updateObject['$set']['attachments'] = attachmentsPresent + } + + //check if project has tasks available and length of tasks array should be greater than 0 + if( pullProjectData.hasOwnProperty('tasks') && pullProjectData.tasks.length > 0){ + + + //varibale to store updated task objects + let newTaskWithValidatedAttachments = [] + //for loop for each task + for(let j = 0; j< pullProjectData.tasks.length; j++){ + + //store task object in task + let task = pullProjectData.tasks[j] + + //checks if task object has key attachment present or not + if(task.hasOwnProperty("attachments")){ + + //varible to store attachment object of task + let attachmentsPresent = [] + + + //for loop for each attachment in each task + for(let k = 0; k < task.attachments.length; k++){ + + //check if isUploaded key is present or not and isUploaded should be false and checks type + if(task.attachments[k].hasOwnProperty("isUploaded") && !task.attachments[k].isUploaded && task.attachments[k].type !== "link"){ + + //checks if document is present or not + let documentExist = await getDocumentStatus( task.attachments[k].sourcePath) + //if present then push object to new array and update isUploaded to true + if(documentExist.success){ + task.attachments[k].isUploaded = true + attachmentsPresent.push(task.attachments[k]) + } + }else{ + //if isUploaded is not present then push object to new array and if type is link then also + attachmentsPresent.push(task.attachments[k]) + } + } + //assign attachmentPresent array to task attachments + task.attachments = attachmentsPresent + } + //push task to new array of tasks key + newTaskWithValidatedAttachments.push(task) + } + //assign all valid task to task for update + updateObject['$set']['tasks'] = newTaskWithValidatedAttachments + } + //push project id which is validated + UpdatedProjectId.push(id) + //update project with new varibales + await db.collection("projectsss").updateOne({_id:id},updateObject) + } + + + } + console.log(UpdatedProjectId) + + //function to check if document exists + function getDocumentStatus (sourcePath) { + return new Promise(async (resolve, reject) => { + try { + // <--- Important : This url endpoint is private do not use it for regular workflows ---> + let url = filePathUrl + sourcePath; + const options = { + headers : { + } + }; + request.get(url,options,userReadCallback); + let result = { + success : true + }; + function userReadCallback(err, data) { + if (err) { + result.success = false; + } else { + // let response = JSON.parse(data.body); + console.log(data.statusCode) + if( data.statusCode === 200 ) { + result.success = true; + } else { + result.success = false; + } + + } + return resolve(result); + } + setTimeout(function () { + return resolve (result = { + success : false + }); + }, 5000); + + } catch (error) { + return reject(error); + } + }) + } + + fs.writeFile( + 'updatedProjectWithAttachments.json', + + JSON.stringify({updatedProjectIds: UpdatedProjectId}), + + function (err) { + if (err) { + console.error('Crap happens'); + } + } + ); + + + console.log("finished project attachment deletion based on isUploaded:= false, completed...") + connection.close(); + } + catch (error) { + console.log(error) + } +})().catch(err => console.error(err)); + + diff --git a/migrations/privateProgramInProjects/updatePrivateProgramInProject.js b/migrations/privateProgramInProjects/updatePrivateProgramInProject.js index 76df459a..d0496beb 100644 --- a/migrations/privateProgramInProjects/updatePrivateProgramInProject.js +++ b/migrations/privateProgramInProjects/updatePrivateProgramInProject.js @@ -119,21 +119,19 @@ await db.collection('projects').findOneAndUpdate({ "_id" : projectDocuments[counter]._id },updateProjectDocument); + + await db.collection('solutions').deleteOne({ + _id: projectDocuments[counter].solutionId + }) + await db.collection('programs').deleteOne({ + _id: projectDocuments[counter].programId + }) } } } - await db.collection('solutions').deleteMany({ - _id: { - $in: deletedSolutionIds - } - }) - await db.collection('programs').deleteMany({ - _id: { - $in: deletedProgramIds - } - }) + @@ -141,7 +139,7 @@ //write updated project ids to file fs.writeFile( - 'updatedProjectIds.json', + 'updatedProjectIdsAll.json', JSON.stringify({updatedProjectIds: updatedProjectIds,deletedProgramIds: deletedProgramIds,deletedSolutionIds: deletedSolutionIds}), From 2ec50de5f621c4407512b3bdbe9d35e7993f1718 Mon Sep 17 00:00:00 2001 From: ankitshahu Date: Fri, 3 Feb 2023 09:19:06 +0530 Subject: [PATCH 76/92] Added Both Script --- .../deleteAndUpdateAttachmentsInProject.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/migrations/privateProgramInProjects/deleteAndUpdateAttachmentsInProject.js b/migrations/privateProgramInProjects/deleteAndUpdateAttachmentsInProject.js index 348b8ece..00ae1b91 100644 --- a/migrations/privateProgramInProjects/deleteAndUpdateAttachmentsInProject.js +++ b/migrations/privateProgramInProjects/deleteAndUpdateAttachmentsInProject.js @@ -21,7 +21,7 @@ const filePathUrl = "https://samikshaprod.blob.core.windows.net/samiksha/"; let db = connection.db(dbName); try { // check project attachments for isUploaded = false data and remove the attachment object - let collectionDocs = await db.collection("projectsss").find({ + let collectionDocs = await db.collection("projects").find({ "$or": [ { "attachments.isUploaded": { @@ -55,7 +55,7 @@ const filePathUrl = "https://samikshaprod.blob.core.windows.net/samiksha/"; let id = projectId[projectIdpointer]; //pull project Data from DB - let pullProjectData = await db.collection("projectsss").find({ + let pullProjectData = await db.collection("projects").find({ _id: id }).project({}).toArray({}) @@ -144,7 +144,7 @@ const filePathUrl = "https://samikshaprod.blob.core.windows.net/samiksha/"; //push project id which is validated UpdatedProjectId.push(id) //update project with new varibales - await db.collection("projectsss").updateOne({_id:id},updateObject) + await db.collection("projects").updateOne({_id:id},updateObject) } From bf2be1b6612ddc9775625f0e8a89d1dcb6485ebb Mon Sep 17 00:00:00 2001 From: ankitshahu Date: Fri, 3 Feb 2023 09:19:54 +0530 Subject: [PATCH 77/92] Added Both Script --- .../deleteAndUpdateAttachmentsInProject.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/migrations/privateProgramInProjects/deleteAndUpdateAttachmentsInProject.js b/migrations/privateProgramInProjects/deleteAndUpdateAttachmentsInProject.js index 00ae1b91..747d3bac 100644 --- a/migrations/privateProgramInProjects/deleteAndUpdateAttachmentsInProject.js +++ b/migrations/privateProgramInProjects/deleteAndUpdateAttachmentsInProject.js @@ -1,3 +1,10 @@ +/** + * name : updatePrivateProgramInProject.js + * author : Ankit Shahu + * created-date : 10-Nov-2022 + * Description : Migration script for update project + */ + const path = require("path"); let rootPath = path.join(__dirname, '../../') require('dotenv').config({ path: rootPath+'/.env' }) From 27697e6290c3793c4ae97bdf9c554be3c7b0cccf Mon Sep 17 00:00:00 2001 From: ankitshahu Date: Fri, 3 Feb 2023 09:20:42 +0530 Subject: [PATCH 78/92] Added Both Script --- .../deleteAndUpdateAttachmentsInProject.js | 2 +- .../privateProgramInProjects/updatePrivateProgramInProject.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/migrations/privateProgramInProjects/deleteAndUpdateAttachmentsInProject.js b/migrations/privateProgramInProjects/deleteAndUpdateAttachmentsInProject.js index 747d3bac..e48ec9a8 100644 --- a/migrations/privateProgramInProjects/deleteAndUpdateAttachmentsInProject.js +++ b/migrations/privateProgramInProjects/deleteAndUpdateAttachmentsInProject.js @@ -1,7 +1,7 @@ /** * name : updatePrivateProgramInProject.js * author : Ankit Shahu - * created-date : 10-Nov-2022 + * created-date : 02-Feb-2023 * Description : Migration script for update project */ diff --git a/migrations/privateProgramInProjects/updatePrivateProgramInProject.js b/migrations/privateProgramInProjects/updatePrivateProgramInProject.js index d0496beb..39d3e70f 100644 --- a/migrations/privateProgramInProjects/updatePrivateProgramInProject.js +++ b/migrations/privateProgramInProjects/updatePrivateProgramInProject.js @@ -1,7 +1,7 @@ /** * name : updatePrivateProgramInProject.js * author : Ankit Shahu - * created-date : 10-Nov-2022 + * created-date : 02-Feb-2023 * Description : Migration script for update project */ From 1db856c3666ed5ee0f3e95b896c1b6cd97492e07 Mon Sep 17 00:00:00 2001 From: ankitshahu Date: Fri, 3 Feb 2023 15:17:39 +0530 Subject: [PATCH 79/92] Updated PR --- .../migratePrivateProjectToPublicProgram.js | 166 +++++++++++++++ .../removeAttachmentsNotUploadedInCloud.js | 200 ++++++++++++++++++ 2 files changed, 366 insertions(+) create mode 100644 migrations/updateProjectDocuments/migratePrivateProjectToPublicProgram.js create mode 100644 migrations/updateProjectDocuments/removeAttachmentsNotUploadedInCloud.js diff --git a/migrations/updateProjectDocuments/migratePrivateProjectToPublicProgram.js b/migrations/updateProjectDocuments/migratePrivateProjectToPublicProgram.js new file mode 100644 index 00000000..50aa2eab --- /dev/null +++ b/migrations/updateProjectDocuments/migratePrivateProjectToPublicProgram.js @@ -0,0 +1,166 @@ +/** + * name : updatePrivateProgramInProject.js + * author : Ankit Shahu + * created-date : 02-Feb-2023 + * Description : Migration script for update project + */ + + const path = require("path"); + let rootPath = path.join(__dirname, '../../') + require('dotenv').config({ path: rootPath+'/.env' }) + + let _ = require("lodash"); + let mongoUrl = process.env.MONGODB_URL; + let dbName = mongoUrl.split("/").pop(); + let url = mongoUrl.split(dbName)[0]; + var MongoClient = require('mongodb').MongoClient; + var ObjectId = require('mongodb').ObjectID; + + var fs = require('fs'); + + +(async () => { + + let connection = await MongoClient.connect(url, { useNewUrlParser: true }); + let db = connection.db(dbName); + try { + + let updatedProjectIds = []; + let deletedSolutionIds = []; + let deletedProgramIds = []; + + + + //get all projectss id where user profile is not there. + let projectDocument = await db.collection('projectss').find({ + userRoleInformation: {$exists : false}, + isAPrivateProgram: true, + }).project({_id:1,userProfile:1}).toArray(); + + + let chunkOfProjectDocument = _.chunk(projectDocument, 10); + // console.log(chunkOfProjectDocument) + let projectIds; + + for (let pointerToProject = 0; pointerToProject < chunkOfProjectDocument.length; pointerToProject++) { + projectIds = await chunkOfProjectDocument[pointerToProject].map( + projectDoc => { + return projectDoc._id; + } + ); + + + // get project documents from projectss collection in Array + let projectDocuments = await db.collection('projectss').find({ + _id: { $in :projectIds } + }).project({}).toArray(); + //iterate project documents one by one + for(let counter = 0; counter < projectDocuments.length; counter++) { + + + if(projectDocuments[counter].hasOwnProperty("solutionId") && projectDocuments[counter].isAPrivateProgram){ + // find solution document form solution collection + let solutionDocument = await db.collection('solutionss').find({ + _id: projectDocuments[counter].solutionId, + parentSolutionId : {$exists:true}, + isAPrivateProgram : true + }).project({}).toArray({}) + //find program document form program collection + if(solutionDocument.length == 1){ + + // find parent solution document in same collection + let parentSolutionDocument = await db.collection('solutionss').find({ + _id: solutionDocument[0].parentSolutionId}).project({}).toArray({}); + //varibale to update project document + let updateProjectDocument = { + "$set" : {} + }; + updateProjectDocument["$set"]["solutionId"] = parentSolutionDocument[0]._id + updateProjectDocument["$set"]["isAPrivateProgram"] = parentSolutionDocument[0].isAPrivateProgram + updateProjectDocument["$set"]["solutionInformation"] = { + name: parentSolutionDocument[0].name, + description: parentSolutionDocument[0].description, + externalId: parentSolutionDocument[0].externalId, + _id: parentSolutionDocument[0]._id, + } + updateProjectDocument["$set"]["solutionExternalId"] = parentSolutionDocument[0].externalId, + updateProjectDocument["$set"]["programId"] = parentSolutionDocument[0].programId, + updateProjectDocument["$set"]["programExternalId"] = parentSolutionDocument[0].programExternalId + updateProjectDocument["$set"]["programInformation"] = { + _id : parentSolutionDocument[0].programId, + name : parentSolutionDocument[0].programName, + externalId : parentSolutionDocument[0].programExternalId, + description : parentSolutionDocument[0].programDescription, + isAPrivateProgram : parentSolutionDocument[0].isAPrivateProgram + } + if(projectDocument[counter].hasOwnProperty("userProfile")) + { + let userLocations = projectDocuments[counter].userProfile.userLocations + let userRoleInfomration = {} + //get data in userRoleInfomration key + for(let userLocationCounter = 0; userLocationCounter < userLocations.length; userLocationCounter++){ + if(userLocations[userLocationCounter].type !== "school"){ + userRoleInfomration[userLocations[userLocationCounter].type] = userLocations[userLocationCounter].id + }else{ + userRoleInfomration[userLocations[userLocationCounter].type] = userLocations[userLocationCounter].code + } + } + let Roles = "" + for(let roleCounter = 0; roleCounter < projectDocuments[counter].userProfile.profileUserTypes.length; roleCounter++){ + Roles = Roles !== "" ? Roles+"," : Roles + Roles += (projectDocuments[counter].userProfile.profileUserTypes[roleCounter].subType ? projectDocuments[counter].userProfile.profileUserTypes[roleCounter].subType.toUpperCase() : projectDocuments[counter].userProfile.profileUserTypes[roleCounter].type.toUpperCase()) + } + userRoleInfomration.Role = Roles + updateProjectDocument["$set"]["userRoleInformation"] = userRoleInfomration + } + + //push all updated and deleted id in arrays and save in file + updatedProjectIds.push(projectDocuments[counter]._id) + deletedSolutionIds.push(projectDocuments[counter].solutionId) + deletedProgramIds.push(projectDocuments[counter].programId) + + // update project documents + await db.collection('projectss').findOneAndUpdate({ + "_id" : projectDocuments[counter]._id + },updateProjectDocument); + + await db.collection('solutionss').deleteOne({ + _id: projectDocuments[counter].solutionId + }) + await db.collection('programs').deleteOne({ + _id: projectDocuments[counter].programId + }) + } + } + } + + + + + + + + + //write updated project ids to file + fs.writeFile( + 'updatedProjectIdsAll.json', + + JSON.stringify({updatedProjectIds: updatedProjectIds,deletedProgramIds: deletedProgramIds,deletedSolutionIds: deletedSolutionIds}), + + function (err) { + if (err) { + console.error('Crap happens'); + } + } + ); + } + console.log("Updated Project Count : ", updatedProjectIds.length) + console.log("deleted program Count : ", deletedProgramIds.length) + console.log("deleted solutionId Count : ", deletedSolutionIds.length) + console.log("completed") + connection.close(); + } + catch (error) { + console.log(error) + } +})().catch(err => console.error(err)); \ No newline at end of file diff --git a/migrations/updateProjectDocuments/removeAttachmentsNotUploadedInCloud.js b/migrations/updateProjectDocuments/removeAttachmentsNotUploadedInCloud.js new file mode 100644 index 00000000..6557e485 --- /dev/null +++ b/migrations/updateProjectDocuments/removeAttachmentsNotUploadedInCloud.js @@ -0,0 +1,200 @@ +/** + * name : updatePrivateProgramInProject.js + * author : Ankit Shahu + * created-date : 02-Feb-2023 + * Description : Migration script for update project + */ + +const path = require("path"); +let rootPath = path.join(__dirname, '../../') +require('dotenv').config({ path: rootPath+'/.env' }) + +let _ = require("lodash"); +let mongoUrl = process.env.MONGODB_URL; +let dbName = mongoUrl.split("/").pop(); +let url = mongoUrl.split(dbName)[0]; +var MongoClient = require('mongodb').MongoClient; +var ObjectId = require('mongodb').ObjectID; +var request = require('request'); +var fs = require('fs'); +const { at } = require("lodash"); + +const filePathUrl = "https://samikshaprod.blob.core.windows.net/samiksha/"; + + +(async () => { + + let connection = await MongoClient.connect(url, { useNewUrlParser: true }); + let db = connection.db(dbName); + try { + // check project attachments for isUploaded = false data and remove the attachment object + let collectionDocs = await db.collection('projectsss').find({ + "$or": [ + { + "attachments.isUploaded": false + }, + { + "tasks.attachments.isUploaded": false + } + ] + }).project({_id:1}).toArray(); + + //varibale to store all projectIds which are updated + let projectIds = []; + collectionDocs.forEach( eachDoc => { + projectIds.push(eachDoc._id); + }) + + let chunkOfProjectIds = _.chunk(projectIds, 10); + let UpdatedProjectId = [] + //loop project chunks + for ( let chunkPointer = 0; chunkPointer < chunkOfProjectIds.length; chunkPointer++ ) { + + //chunk of project ids + let projectId = chunkOfProjectIds[chunkPointer]; + + //loop for chunk of project id in chunk + for ( let projectIdpointer = 0 ; projectIdpointer < projectId.length; projectIdpointer++ ) { + let id = projectId[projectIdpointer]; + + //pull project Data from DB + let pullProjectData = await db.collection('projectsss').find({ + _id: id + }).project({}).toArray({}) + + //store in varibale to avoid using [0] + pullProjectData = pullProjectData[0] + + //update Object + let updateObject = { + "$set" : {} + } + + //check if project Document has attachments or not if present then checks length of attachment it should be greater than 0 + if( pullProjectData.hasOwnProperty('attachments') && pullProjectData.attachments.length > 0 ) { + + //assign attachmentPresent object to update variable + updateObject['$set']['attachments'] = await validatedAttachments( pullProjectData.attachments) + } + + //check if project has tasks available and length of tasks array should be greater than 0 + if( pullProjectData.hasOwnProperty('tasks') && pullProjectData.tasks.length > 0){ + + + //varibale to store updated task objects + let newTaskWithValidatedAttachments = [] + //for loop for each task + for(let taskCounter = 0; taskCounter< pullProjectData.tasks.length; taskCounter++){ + + //store task object in task + let task = pullProjectData.tasks[taskCounter] + + //checks if task object has key attachment present or not + if(task.hasOwnProperty("attachments")){ + + //assign attachmentPresent array to task attachments + task.attachments = await validatedAttachments(task.attachments) + } + //push task to new array of tasks key + newTaskWithValidatedAttachments.push(task) + } + //assign all valid task to task for update + updateObject['$set']['tasks'] = newTaskWithValidatedAttachments + } + //push project id which is validated + + UpdatedProjectId.push(id) + //update project with new varibales + await db.collection('projectsss').updateOne({_id:id},updateObject) + } + + + } + console.log(UpdatedProjectId) + + async function validatedAttachments(attachments){ + //varibale to store updated attachment objects + let attachmentsPresent = [] + + //for loop for each attachment + for(let attachmentCounter = 0; attachmentCounter< attachments.length; attachmentCounter++){ + + //check if isUploaded key is present or not and isUploaded should be false and checks type of attachment + if(attachments[attachmentCounter].hasOwnProperty("isUploaded") && !attachments[attachmentCounter].isUploaded && attachments[attachmentCounter].type !== "link"){ + + //checks if document is present or not + let documentExist = await getDocumentStatus(attachments[attachmentCounter].sourcePath) + //if present then push object to new array and update isUploaded to true + if(documentExist.success){ + attachments[attachmentCounter].isUploaded = true + attachmentsPresent.push(attachments[attachmentCounter]) + } + }else{ + //if isUploaded is not present then push object to new array and if type is link then also + attachmentsPresent.push(attachments[attachmentCounter]) + } + } + + return attachmentsPresent; + } + + //function to check if document exists + function getDocumentStatus (sourcePath) { + return new Promise(async (resolve, reject) => { + try { + let url = filePathUrl + sourcePath; + const options = { + headers : { + } + }; + request.get(url,options,userReadCallback); + let result = { + success : true + }; + function userReadCallback(err, data) { + if (err) { + result.success = false; + } else { + if( data.statusCode === 200 ) { + result.success = true; + } else { + result.success = false; + } + + } + return resolve(result); + } + setTimeout(function () { + return resolve (result = { + success : false + }); + }, 5000); + + } catch (error) { + return reject(error); + } + }) + } + + fs.writeFile( + 'updatedProjectWithAttachments.json', + + JSON.stringify({updatedProjectIds: UpdatedProjectId}), + + function (err) { + if (err) { + console.error('Crap happens'); + } + } + ); + + console.log("Updated Projects", UpdatedProjectId.length) + console.log("finished project attachment deletion based on isUploaded:= false, completed...") + connection.close(); + } + catch (error) { + console.log(error) + } +})().catch(err => console.error(err)); + + From 56d73c3cdfb06516ccf0db1f56af036662d865f0 Mon Sep 17 00:00:00 2001 From: ankitshahu Date: Fri, 3 Feb 2023 15:21:49 +0530 Subject: [PATCH 80/92] Done with Scripts --- .../deleteAndUpdateAttachmentsInProject.js | 223 ------------------ .../updatePrivateProgramInProject.js | 162 ------------- 2 files changed, 385 deletions(-) delete mode 100644 migrations/privateProgramInProjects/deleteAndUpdateAttachmentsInProject.js delete mode 100644 migrations/privateProgramInProjects/updatePrivateProgramInProject.js diff --git a/migrations/privateProgramInProjects/deleteAndUpdateAttachmentsInProject.js b/migrations/privateProgramInProjects/deleteAndUpdateAttachmentsInProject.js deleted file mode 100644 index e48ec9a8..00000000 --- a/migrations/privateProgramInProjects/deleteAndUpdateAttachmentsInProject.js +++ /dev/null @@ -1,223 +0,0 @@ -/** - * name : updatePrivateProgramInProject.js - * author : Ankit Shahu - * created-date : 02-Feb-2023 - * Description : Migration script for update project - */ - -const path = require("path"); -let rootPath = path.join(__dirname, '../../') -require('dotenv').config({ path: rootPath+'/.env' }) - -let _ = require("lodash"); -let mongoUrl = process.env.MONGODB_URL; -let dbName = mongoUrl.split("/").pop(); -let url = mongoUrl.split(dbName)[0]; -var MongoClient = require('mongodb').MongoClient; -var ObjectId = require('mongodb').ObjectID; -var request = require('request'); -var fs = require('fs'); -const { at } = require("lodash"); - -const filePathUrl = "https://samikshaprod.blob.core.windows.net/samiksha/"; - - -(async () => { - - let connection = await MongoClient.connect(url, { useNewUrlParser: true }); - let db = connection.db(dbName); - try { - // check project attachments for isUploaded = false data and remove the attachment object - let collectionDocs = await db.collection("projects").find({ - "$or": [ - { - "attachments.isUploaded": { - "$exists": true - },"attachments.isUploaded": false - }, - { - "tasks.attachments.isUploaded": { - "$exists": true - },"tasks.attachments.isUploaded": false - } - ] - }).project({_id:1}).toArray(); - - //varibale to store all projectIds which are updated - let projectIds = []; - collectionDocs.forEach( eachDoc => { - projectIds.push(eachDoc._id); - }) - - let chunkOfProjectIds = _.chunk(projectIds, 5); - let UpdatedProjectId = [] - //loop project chunks - for ( let chunkPointer = 0; chunkPointer < chunkOfProjectIds.length; chunkPointer++ ) { - - //chunk of project ids - let projectId = chunkOfProjectIds[chunkPointer]; - - //loop for chunk of project id in chunk - for ( let projectIdpointer = 0 ; projectIdpointer < projectId.length; projectIdpointer++ ) { - let id = projectId[projectIdpointer]; - - //pull project Data from DB - let pullProjectData = await db.collection("projects").find({ - _id: id - }).project({}).toArray({}) - - //store in varibale to avoid using [0] - pullProjectData = pullProjectData[0] - - //update Object - let updateObject = { - "$set" : {} - } - - //check if project Document has attachments or not if present then checks length of attachment it should be greater than 0 - if( pullProjectData.hasOwnProperty('attachments') && pullProjectData.attachments.length > 0 ) { - - //varibale to store updated attachment objects - let attachmentsPresent = [] - - //for loop for each attachment - for(let j = 0; j< pullProjectData.attachments.length; j++){ - - //check if isUploaded key is present or not and isUploaded should be false and checks type of attachment - if(pullProjectData.attachments[j].hasOwnProperty("isUploaded") && !pullProjectData.attachments[j].isUploaded && pullProjectData.attachments[j].type !== "link"){ - - //checks if document is present or not - let documentExist = await getDocumentStatus( pullProjectData.attachments[j].sourcePath) - //if present then push object to new array and update isUploaded to true - if(documentExist.success){ - pullProjectData.attachments[j].isUploaded = true - attachmentsPresent.push(pullProjectData.attachments[j]) - } - }else{ - //if isUploaded is not present then push object to new array and if type is link then also - attachmentsPresent.push(pullProjectData.attachments[j]) - } - } - //assign attachmentPresent object to update variable - updateObject['$set']['attachments'] = attachmentsPresent - } - - //check if project has tasks available and length of tasks array should be greater than 0 - if( pullProjectData.hasOwnProperty('tasks') && pullProjectData.tasks.length > 0){ - - - //varibale to store updated task objects - let newTaskWithValidatedAttachments = [] - //for loop for each task - for(let j = 0; j< pullProjectData.tasks.length; j++){ - - //store task object in task - let task = pullProjectData.tasks[j] - - //checks if task object has key attachment present or not - if(task.hasOwnProperty("attachments")){ - - //varible to store attachment object of task - let attachmentsPresent = [] - - - //for loop for each attachment in each task - for(let k = 0; k < task.attachments.length; k++){ - - //check if isUploaded key is present or not and isUploaded should be false and checks type - if(task.attachments[k].hasOwnProperty("isUploaded") && !task.attachments[k].isUploaded && task.attachments[k].type !== "link"){ - - //checks if document is present or not - let documentExist = await getDocumentStatus( task.attachments[k].sourcePath) - //if present then push object to new array and update isUploaded to true - if(documentExist.success){ - task.attachments[k].isUploaded = true - attachmentsPresent.push(task.attachments[k]) - } - }else{ - //if isUploaded is not present then push object to new array and if type is link then also - attachmentsPresent.push(task.attachments[k]) - } - } - //assign attachmentPresent array to task attachments - task.attachments = attachmentsPresent - } - //push task to new array of tasks key - newTaskWithValidatedAttachments.push(task) - } - //assign all valid task to task for update - updateObject['$set']['tasks'] = newTaskWithValidatedAttachments - } - //push project id which is validated - UpdatedProjectId.push(id) - //update project with new varibales - await db.collection("projects").updateOne({_id:id},updateObject) - } - - - } - console.log(UpdatedProjectId) - - //function to check if document exists - function getDocumentStatus (sourcePath) { - return new Promise(async (resolve, reject) => { - try { - // <--- Important : This url endpoint is private do not use it for regular workflows ---> - let url = filePathUrl + sourcePath; - const options = { - headers : { - } - }; - request.get(url,options,userReadCallback); - let result = { - success : true - }; - function userReadCallback(err, data) { - if (err) { - result.success = false; - } else { - // let response = JSON.parse(data.body); - console.log(data.statusCode) - if( data.statusCode === 200 ) { - result.success = true; - } else { - result.success = false; - } - - } - return resolve(result); - } - setTimeout(function () { - return resolve (result = { - success : false - }); - }, 5000); - - } catch (error) { - return reject(error); - } - }) - } - - fs.writeFile( - 'updatedProjectWithAttachments.json', - - JSON.stringify({updatedProjectIds: UpdatedProjectId}), - - function (err) { - if (err) { - console.error('Crap happens'); - } - } - ); - - - console.log("finished project attachment deletion based on isUploaded:= false, completed...") - connection.close(); - } - catch (error) { - console.log(error) - } -})().catch(err => console.error(err)); - - diff --git a/migrations/privateProgramInProjects/updatePrivateProgramInProject.js b/migrations/privateProgramInProjects/updatePrivateProgramInProject.js deleted file mode 100644 index 39d3e70f..00000000 --- a/migrations/privateProgramInProjects/updatePrivateProgramInProject.js +++ /dev/null @@ -1,162 +0,0 @@ -/** - * name : updatePrivateProgramInProject.js - * author : Ankit Shahu - * created-date : 02-Feb-2023 - * Description : Migration script for update project - */ - - const path = require("path"); - let rootPath = path.join(__dirname, '../../') - require('dotenv').config({ path: rootPath+'/.env' }) - - let _ = require("lodash"); - let mongoUrl = process.env.MONGODB_URL; - let dbName = mongoUrl.split("/").pop(); - let url = mongoUrl.split(dbName)[0]; - var MongoClient = require('mongodb').MongoClient; - var ObjectId = require('mongodb').ObjectID; - - var fs = require('fs'); - - -(async () => { - - let connection = await MongoClient.connect(url, { useNewUrlParser: true }); - let db = connection.db(dbName); - try { - - let updatedProjectIds = []; - let deletedSolutionIds = []; - let deletedProgramIds = []; - - - - //get all projects id where user profile is not there. - let projectDocument = await db.collection('projects').find({ - userRoleInformation: {$exists : false}, - isAPrivateProgram: true, - }).project({_id:1}).toArray(); - - - let chunkOfProjectDocument = _.chunk(projectDocument, 10); - // console.log(chunkOfProjectDocument) - let projectIds; - - for (let pointerToProject = 0; pointerToProject < chunkOfProjectDocument.length; pointerToProject++) { - projectIds = await chunkOfProjectDocument[pointerToProject].map( - projectDoc => { - return projectDoc._id; - } - ); - - - // get project documents from projects collection in Array - let projectDocuments = await db.collection('projects').find({ - _id: { $in :projectIds } - }).project({}).toArray(); - //iterate project documents one by one - for(let counter = 0; counter < projectDocuments.length; counter++) { - - - if(projectDocuments[counter].hasOwnProperty("solutionId") && projectDocuments[counter].isAPrivateProgram){ - // find solution document form solution collection - let solutionDocument = await db.collection('solutions').find({ - _id: projectDocuments[counter].solutionId, - parentSolutionId : {$exists:true}, - isAPrivateProgram : true - }).project({}).toArray({}) - //find program document form program collection - if(solutionDocument.length == 1){ - - // find parent solution document in same collection - let parentSolutionDocument = await db.collection('solutions').find({ - _id: solutionDocument[0].parentSolutionId}).project({}).toArray({}); - //varibale to update project document - let updateProjectDocument = { - "$set" : {} - }; - updateProjectDocument["$set"]["solutionId"] = parentSolutionDocument[0]._id - updateProjectDocument["$set"]["isAPrivateProgram"] = parentSolutionDocument[0].isAPrivateProgram - updateProjectDocument["$set"]["solutionInformation"] = { - name: parentSolutionDocument[0].name, - description: parentSolutionDocument[0].description, - externalId: parentSolutionDocument[0].externalId, - _id: parentSolutionDocument[0]._id, - } - updateProjectDocument["$set"]["solutionExternalId"] = parentSolutionDocument[0].externalId, - updateProjectDocument["$set"]["programId"] = parentSolutionDocument[0].programId, - updateProjectDocument["$set"]["programExternalId"] = parentSolutionDocument[0].programExternalId - updateProjectDocument["$set"]["programInformation"] = { - _id : parentSolutionDocument[0].programId, - name : parentSolutionDocument[0].programName, - externalId : parentSolutionDocument[0].programExternalId, - description : parentSolutionDocument[0].programDescription, - isAPrivateProgram : parentSolutionDocument[0].isAPrivateProgram - } - if(projectDocument[counter].hasOwnProperty("userProfile")) - { - let userLocations = projectDocuments[counter].userProfile.userLocations - let userRoleInfomration = {} - //get data in userRoleInfomration key - for(let i = 0; i < userLocations.length; i++){ - if(userLocations[i].type !== "school"){ - userRoleInfomration[userLocations[i].type] = userLocations[i].id - }else{ - userRoleInfomration[userLocations[i].type] = userLocations[i].code - } - } - userRoleInfomration.Role = projectDocuments[counter].userProfile.profileUserType.subType ? projectDocuments[counter].userProfile.profileUserType.subType.toUpperCase() : projectDocuments[counter].userProfile.profileUserType.type.toUpperCase() - updateProjectDocument["$set"]["userRoleInformation"] = userRoleInfomration - } - - - //push all updated and deleted id in arrays and save in file - updatedProjectIds.push(projectDocuments[counter]._id) - deletedSolutionIds.push(projectDocuments[counter].solutionId) - deletedProgramIds.push(projectDocuments[counter].programId) - - //update project documents - await db.collection('projects').findOneAndUpdate({ - "_id" : projectDocuments[counter]._id - },updateProjectDocument); - - await db.collection('solutions').deleteOne({ - _id: projectDocuments[counter].solutionId - }) - await db.collection('programs').deleteOne({ - _id: projectDocuments[counter].programId - }) - } - } - } - - - - - - - - - //write updated project ids to file - fs.writeFile( - 'updatedProjectIdsAll.json', - - JSON.stringify({updatedProjectIds: updatedProjectIds,deletedProgramIds: deletedProgramIds,deletedSolutionIds: deletedSolutionIds}), - - function (err) { - if (err) { - console.error('Crap happens'); - } - } - ); - } - console.log("Updated Project Count : ", updatedProjectIds.length) - console.log("deleted program Count : ", deletedProgramIds.length) - console.log("deleted solutionId Count : ", deletedSolutionIds.length) - console.log("completed") - connection.close(); - } - catch (error) { - console.log(error) - } -})().catch(err => console.error(err)); \ No newline at end of file From 5af2f00ce707b852b7a008261a79f7bd5a8aa6a9 Mon Sep 17 00:00:00 2001 From: ankitshahu Date: Fri, 3 Feb 2023 15:24:04 +0530 Subject: [PATCH 81/92] Done with Scripts --- .../migratePrivateProjectToPublicProgram.js | 12 ++++++------ .../removeAttachmentsNotUploadedInCloud.js | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/migrations/updateProjectDocuments/migratePrivateProjectToPublicProgram.js b/migrations/updateProjectDocuments/migratePrivateProjectToPublicProgram.js index 50aa2eab..632be931 100644 --- a/migrations/updateProjectDocuments/migratePrivateProjectToPublicProgram.js +++ b/migrations/updateProjectDocuments/migratePrivateProjectToPublicProgram.js @@ -32,7 +32,7 @@ //get all projectss id where user profile is not there. - let projectDocument = await db.collection('projectss').find({ + let projectDocument = await db.collection('projects').find({ userRoleInformation: {$exists : false}, isAPrivateProgram: true, }).project({_id:1,userProfile:1}).toArray(); @@ -51,7 +51,7 @@ // get project documents from projectss collection in Array - let projectDocuments = await db.collection('projectss').find({ + let projectDocuments = await db.collection('projects').find({ _id: { $in :projectIds } }).project({}).toArray(); //iterate project documents one by one @@ -60,7 +60,7 @@ if(projectDocuments[counter].hasOwnProperty("solutionId") && projectDocuments[counter].isAPrivateProgram){ // find solution document form solution collection - let solutionDocument = await db.collection('solutionss').find({ + let solutionDocument = await db.collection('solutions').find({ _id: projectDocuments[counter].solutionId, parentSolutionId : {$exists:true}, isAPrivateProgram : true @@ -69,7 +69,7 @@ if(solutionDocument.length == 1){ // find parent solution document in same collection - let parentSolutionDocument = await db.collection('solutionss').find({ + let parentSolutionDocument = await db.collection('solutions').find({ _id: solutionDocument[0].parentSolutionId}).project({}).toArray({}); //varibale to update project document let updateProjectDocument = { @@ -120,11 +120,11 @@ deletedProgramIds.push(projectDocuments[counter].programId) // update project documents - await db.collection('projectss').findOneAndUpdate({ + await db.collection('projects').findOneAndUpdate({ "_id" : projectDocuments[counter]._id },updateProjectDocument); - await db.collection('solutionss').deleteOne({ + await db.collection('solutions').deleteOne({ _id: projectDocuments[counter].solutionId }) await db.collection('programs').deleteOne({ diff --git a/migrations/updateProjectDocuments/removeAttachmentsNotUploadedInCloud.js b/migrations/updateProjectDocuments/removeAttachmentsNotUploadedInCloud.js index 6557e485..e94f429f 100644 --- a/migrations/updateProjectDocuments/removeAttachmentsNotUploadedInCloud.js +++ b/migrations/updateProjectDocuments/removeAttachmentsNotUploadedInCloud.js @@ -28,7 +28,7 @@ const filePathUrl = "https://samikshaprod.blob.core.windows.net/samiksha/"; let db = connection.db(dbName); try { // check project attachments for isUploaded = false data and remove the attachment object - let collectionDocs = await db.collection('projectsss').find({ + let collectionDocs = await db.collection('projects').find({ "$or": [ { "attachments.isUploaded": false @@ -58,7 +58,7 @@ const filePathUrl = "https://samikshaprod.blob.core.windows.net/samiksha/"; let id = projectId[projectIdpointer]; //pull project Data from DB - let pullProjectData = await db.collection('projectsss').find({ + let pullProjectData = await db.collection('projects').find({ _id: id }).project({}).toArray({}) @@ -105,7 +105,7 @@ const filePathUrl = "https://samikshaprod.blob.core.windows.net/samiksha/"; UpdatedProjectId.push(id) //update project with new varibales - await db.collection('projectsss').updateOne({_id:id},updateObject) + await db.collection('projects').updateOne({_id:id},updateObject) } From da2a6025157a5becaf254435315a48e8e85487d8 Mon Sep 17 00:00:00 2001 From: ankitshahu Date: Fri, 3 Feb 2023 21:41:20 +0530 Subject: [PATCH 82/92] Updated Code --- .../removeAttachmentsNotUploadedInCloud.js | 93 +++++++++++-------- .../updatedProjectWithAttachments.json | 1 + 2 files changed, 53 insertions(+), 41 deletions(-) create mode 100644 migrations/updateProjectDocuments/updatedProjectWithAttachments.json diff --git a/migrations/updateProjectDocuments/removeAttachmentsNotUploadedInCloud.js b/migrations/updateProjectDocuments/removeAttachmentsNotUploadedInCloud.js index e94f429f..a29b551a 100644 --- a/migrations/updateProjectDocuments/removeAttachmentsNotUploadedInCloud.js +++ b/migrations/updateProjectDocuments/removeAttachmentsNotUploadedInCloud.js @@ -4,7 +4,7 @@ * created-date : 02-Feb-2023 * Description : Migration script for update project */ - + "use strict"; const path = require("path"); let rootPath = path.join(__dirname, '../../') require('dotenv').config({ path: rootPath+'/.env' }) @@ -58,12 +58,15 @@ const filePathUrl = "https://samikshaprod.blob.core.windows.net/samiksha/"; let id = projectId[projectIdpointer]; //pull project Data from DB - let pullProjectData = await db.collection('projects').find({ + let pullProjectDataDB = await db.collection('projects').find({ _id: id }).project({}).toArray({}) //store in varibale to avoid using [0] - pullProjectData = pullProjectData[0] + const pullProjectData = pullProjectDataDB[0] + + const allTasksBeforeValidation = JSON.stringify(pullProjectData.tasks) + let allAttachmentsBeforeValidation = "" //update Object let updateObject = { @@ -72,56 +75,74 @@ const filePathUrl = "https://samikshaprod.blob.core.windows.net/samiksha/"; //check if project Document has attachments or not if present then checks length of attachment it should be greater than 0 if( pullProjectData.hasOwnProperty('attachments') && pullProjectData.attachments.length > 0 ) { - + allAttachmentsBeforeValidation = JSON.stringify(pullProjectData.attachments) //assign attachmentPresent object to update variable - updateObject['$set']['attachments'] = await validatedAttachments( pullProjectData.attachments) + updateObject['$set']['attachments'] = await validatedAttachments(pullProjectData.attachments) } //check if project has tasks available and length of tasks array should be greater than 0 if( pullProjectData.hasOwnProperty('tasks') && pullProjectData.tasks.length > 0){ - - //varibale to store updated task objects - let newTaskWithValidatedAttachments = [] + let tasks = pullProjectData.tasks + updateObject['$set']['tasks'] = [...tasks] + //for loop for each task - for(let taskCounter = 0; taskCounter< pullProjectData.tasks.length; taskCounter++){ - - //store task object in task - let task = pullProjectData.tasks[taskCounter] + for(let taskCounter = 0; taskCounter< tasks.length; taskCounter++){ //checks if task object has key attachment present or not - if(task.hasOwnProperty("attachments")){ - + if(tasks[taskCounter].hasOwnProperty("attachments") && tasks[taskCounter].attachments.length>0){ //assign attachmentPresent array to task attachments - task.attachments = await validatedAttachments(task.attachments) + updateObject['$set']['tasks'][taskCounter].attachments = await validatedAttachments((updateObject['$set']['tasks'][taskCounter].attachments)) } - //push task to new array of tasks key - newTaskWithValidatedAttachments.push(task) } - //assign all valid task to task for update - updateObject['$set']['tasks'] = newTaskWithValidatedAttachments } + //push project id which is validated - - UpdatedProjectId.push(id) - //update project with new varibales - await db.collection('projects').updateOne({_id:id},updateObject) + if(JSON.stringify(updateObject["$set"]["tasks"]) != allTasksBeforeValidation || JSON.stringify(updateObject["$set"]["attachments"]) != allAttachmentsBeforeValidation){ + + if(JSON.stringify(updateObject["$set"]["tasks"]) == allTasksBeforeValidation){ + delete updateObject["$set"].tasks + } + if(JSON.stringify(updateObject["$set"]["attachments"]) == allAttachmentsBeforeValidation){ + delete updateObject["$set"].attachments + } + if(updateObject["$set"].hasOwnProperty("attachments") || updateObject["$set"].hasOwnProperty("tasks")){ + UpdatedProjectId.push(id) + //update project with new varibales + await db.collection('projects').updateOne({_id:id},updateObject)} + } } } console.log(UpdatedProjectId) + + + fs.writeFile( + 'updatedProjectWithAttachments.json', + + JSON.stringify({updatedProjectIds: UpdatedProjectId}), + + function (err) { + if (err) { + console.error('Crap happens'); + } + } + ); + + + async function validatedAttachments(attachments){ //varibale to store updated attachment objects let attachmentsPresent = [] - + //for loop for each attachment - for(let attachmentCounter = 0; attachmentCounter< attachments.length; attachmentCounter++){ - + for(let attachmentCounter = 0; attachmentCounter < attachments.length; attachmentCounter++){ + //check if isUploaded key is present or not and isUploaded should be false and checks type of attachment if(attachments[attachmentCounter].hasOwnProperty("isUploaded") && !attachments[attachmentCounter].isUploaded && attachments[attachmentCounter].type !== "link"){ - + //checks if document is present or not let documentExist = await getDocumentStatus(attachments[attachmentCounter].sourcePath) //if present then push object to new array and update isUploaded to true @@ -134,10 +155,10 @@ const filePathUrl = "https://samikshaprod.blob.core.windows.net/samiksha/"; attachmentsPresent.push(attachments[attachmentCounter]) } } - + return attachmentsPresent; } - + //function to check if document exists function getDocumentStatus (sourcePath) { return new Promise(async (resolve, reject) => { @@ -155,6 +176,7 @@ const filePathUrl = "https://samikshaprod.blob.core.windows.net/samiksha/"; if (err) { result.success = false; } else { + console.log(data.statusCode) if( data.statusCode === 200 ) { result.success = true; } else { @@ -175,18 +197,7 @@ const filePathUrl = "https://samikshaprod.blob.core.windows.net/samiksha/"; } }) } - - fs.writeFile( - 'updatedProjectWithAttachments.json', - - JSON.stringify({updatedProjectIds: UpdatedProjectId}), - - function (err) { - if (err) { - console.error('Crap happens'); - } - } - ); + console.log("Updated Projects", UpdatedProjectId.length) console.log("finished project attachment deletion based on isUploaded:= false, completed...") diff --git a/migrations/updateProjectDocuments/updatedProjectWithAttachments.json b/migrations/updateProjectDocuments/updatedProjectWithAttachments.json new file mode 100644 index 00000000..be932289 --- /dev/null +++ b/migrations/updateProjectDocuments/updatedProjectWithAttachments.json @@ -0,0 +1 @@ +{"updatedProjectIds":[]} \ No newline at end of file From 01260a7c8c79e70671078f468fd2a433e2f4b479 Mon Sep 17 00:00:00 2001 From: ankitshahu Date: Fri, 3 Feb 2023 21:42:11 +0530 Subject: [PATCH 83/92] Updated Code --- .../updateProjectDocuments/updatedProjectWithAttachments.json | 1 - 1 file changed, 1 deletion(-) delete mode 100644 migrations/updateProjectDocuments/updatedProjectWithAttachments.json diff --git a/migrations/updateProjectDocuments/updatedProjectWithAttachments.json b/migrations/updateProjectDocuments/updatedProjectWithAttachments.json deleted file mode 100644 index be932289..00000000 --- a/migrations/updateProjectDocuments/updatedProjectWithAttachments.json +++ /dev/null @@ -1 +0,0 @@ -{"updatedProjectIds":[]} \ No newline at end of file From f4d10e9357de0d57def02eee66ff600fe00c5d92 Mon Sep 17 00:00:00 2001 From: ankitshahu Date: Mon, 6 Feb 2023 11:24:50 +0530 Subject: [PATCH 84/92] updated script --- .../migratePrivateProjectToPublicProgram.js | 6 +++--- .../removeAttachmentsNotUploadedInCloud.js | 1 - migrations/updateProjectDocuments/updatedProjectIdsAll.json | 1 + .../updatedProjectWithAttachments.json | 1 + 4 files changed, 5 insertions(+), 4 deletions(-) create mode 100644 migrations/updateProjectDocuments/updatedProjectIdsAll.json create mode 100644 migrations/updateProjectDocuments/updatedProjectWithAttachments.json diff --git a/migrations/updateProjectDocuments/migratePrivateProjectToPublicProgram.js b/migrations/updateProjectDocuments/migratePrivateProjectToPublicProgram.js index 632be931..1a5866df 100644 --- a/migrations/updateProjectDocuments/migratePrivateProjectToPublicProgram.js +++ b/migrations/updateProjectDocuments/migratePrivateProjectToPublicProgram.js @@ -53,11 +53,11 @@ // get project documents from projectss collection in Array let projectDocuments = await db.collection('projects').find({ _id: { $in :projectIds } - }).project({}).toArray(); + }).project({_id:1,userProfile:1}).toArray(); //iterate project documents one by one for(let counter = 0; counter < projectDocuments.length; counter++) { - + if(projectDocuments[counter].hasOwnProperty("solutionId") && projectDocuments[counter].isAPrivateProgram){ // find solution document form solution collection let solutionDocument = await db.collection('solutions').find({ @@ -93,7 +93,7 @@ description : parentSolutionDocument[0].programDescription, isAPrivateProgram : parentSolutionDocument[0].isAPrivateProgram } - if(projectDocument[counter].hasOwnProperty("userProfile")) + if(projectDocuments[counter].hasOwnProperty("userProfile")) { let userLocations = projectDocuments[counter].userProfile.userLocations let userRoleInfomration = {} diff --git a/migrations/updateProjectDocuments/removeAttachmentsNotUploadedInCloud.js b/migrations/updateProjectDocuments/removeAttachmentsNotUploadedInCloud.js index a29b551a..b0c29f74 100644 --- a/migrations/updateProjectDocuments/removeAttachmentsNotUploadedInCloud.js +++ b/migrations/updateProjectDocuments/removeAttachmentsNotUploadedInCloud.js @@ -176,7 +176,6 @@ const filePathUrl = "https://samikshaprod.blob.core.windows.net/samiksha/"; if (err) { result.success = false; } else { - console.log(data.statusCode) if( data.statusCode === 200 ) { result.success = true; } else { diff --git a/migrations/updateProjectDocuments/updatedProjectIdsAll.json b/migrations/updateProjectDocuments/updatedProjectIdsAll.json new file mode 100644 index 00000000..cd5e7369 --- /dev/null +++ b/migrations/updateProjectDocuments/updatedProjectIdsAll.json @@ -0,0 +1 @@ +{"updatedProjectIds":[],"deletedProgramIds":[],"deletedSolutionIds":[]} \ No newline at end of file diff --git a/migrations/updateProjectDocuments/updatedProjectWithAttachments.json b/migrations/updateProjectDocuments/updatedProjectWithAttachments.json new file mode 100644 index 00000000..be932289 --- /dev/null +++ b/migrations/updateProjectDocuments/updatedProjectWithAttachments.json @@ -0,0 +1 @@ +{"updatedProjectIds":[]} \ No newline at end of file From 36d84c6a3992f3b058f5882a6dbd6d702ecdb90f Mon Sep 17 00:00:00 2001 From: ankitshahu Date: Mon, 6 Feb 2023 11:25:15 +0530 Subject: [PATCH 85/92] Updated script --- migrations/updateProjectDocuments/updatedProjectIdsAll.json | 1 - .../updateProjectDocuments/updatedProjectWithAttachments.json | 1 - 2 files changed, 2 deletions(-) delete mode 100644 migrations/updateProjectDocuments/updatedProjectIdsAll.json delete mode 100644 migrations/updateProjectDocuments/updatedProjectWithAttachments.json diff --git a/migrations/updateProjectDocuments/updatedProjectIdsAll.json b/migrations/updateProjectDocuments/updatedProjectIdsAll.json deleted file mode 100644 index cd5e7369..00000000 --- a/migrations/updateProjectDocuments/updatedProjectIdsAll.json +++ /dev/null @@ -1 +0,0 @@ -{"updatedProjectIds":[],"deletedProgramIds":[],"deletedSolutionIds":[]} \ No newline at end of file diff --git a/migrations/updateProjectDocuments/updatedProjectWithAttachments.json b/migrations/updateProjectDocuments/updatedProjectWithAttachments.json deleted file mode 100644 index be932289..00000000 --- a/migrations/updateProjectDocuments/updatedProjectWithAttachments.json +++ /dev/null @@ -1 +0,0 @@ -{"updatedProjectIds":[]} \ No newline at end of file From 4766648fe3368572fc99677d27af03d7e391302e Mon Sep 17 00:00:00 2001 From: VISHNUDAS-tunerlabse Date: Sat, 18 Feb 2023 09:03:25 +0530 Subject: [PATCH 86/92] hasAcceptedTAndC check added to importFromLibrary API --- module/userProjects/helper.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/module/userProjects/helper.js b/module/userProjects/helper.js index fc46cf9b..2283a2f1 100644 --- a/module/userProjects/helper.js +++ b/module/userProjects/helper.js @@ -2307,6 +2307,10 @@ module.exports = class UserProjectsHelper { libraryProjects.data.endDate = requestedData.endDate; } + if (requestedData.hasAcceptedTAndC) { + libraryProjects.data.hasAcceptedTAndC = true; + } + libraryProjects.data.projectTemplateId = libraryProjects.data._id; libraryProjects.data.projectTemplateExternalId = libraryProjects.data.externalId; From b77f5de68ca52c356e76773c5df1eb84c865f71f Mon Sep 17 00:00:00 2001 From: VISHNUDAS-tunerlabse Date: Thu, 23 Feb 2023 18:13:23 +0530 Subject: [PATCH 87/92] env check issue fix --- envVariables.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/envVariables.js b/envVariables.js index 307f75b3..5228d7ef 100644 --- a/envVariables.js +++ b/envVariables.js @@ -143,10 +143,7 @@ module.exports = function() { } async function getKid(){ - if ( enviromentVariables["PROJECT_CERTIFICATE_ON_OFF"] && - enviromentVariables["PROJECT_CERTIFICATE_ON_OFF"].default && - enviromentVariables["PROJECT_CERTIFICATE_ON_OFF"].default === "ON" - ) { + if ( process.env.PROJECT_CERTIFICATE_ON_OFF === "ON" ) { // get certificate issuer kid from sunbird-RC let kidData = await certificateService.getCertificateIssuerKid(); if( !kidData.success ) { From 7d71ef1a482ed406a83141f0d5baf761298ab404 Mon Sep 17 00:00:00 2001 From: ankitshahu Date: Tue, 19 Dec 2023 15:59:28 +0530 Subject: [PATCH 88/92] added long term fix for release-5.1.0 --- generics/constants/api-responses.js | 3 ++- module/userProjects/helper.js | 14 ++++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/generics/constants/api-responses.js b/generics/constants/api-responses.js index 7404a87a..568265f4 100644 --- a/generics/constants/api-responses.js +++ b/generics/constants/api-responses.js @@ -134,5 +134,6 @@ module.exports = { "CERTIFICATE_GENERATION_FAILED" : "Certificate generation failed", "NOT_ELIGIBLE_FOR_CERTIFICATE" : "Project is not eligible for certificate", "ISSUER_KID_NOT_FOUND" : "Failed to fetch certificate issuer kid", - "PROJECT_SUBMITTED_FOR_REISSUE" : "Submitted for project certificate reIssue" + "PROJECT_SUBMITTED_FOR_REISSUE" : "Submitted for project certificate reIssue", + "FAILED_TO_START_RESOURCE": "There was an error in starting/joining. Please try again after some time." }; diff --git a/module/userProjects/helper.js b/module/userProjects/helper.js index 2283a2f1..3e0f7013 100644 --- a/module/userProjects/helper.js +++ b/module/userProjects/helper.js @@ -1273,7 +1273,12 @@ module.exports = class UserProjectsHelper { ) { projectCreation.data.userProfile = userProfile.data.response; addReportInfoToSolution = true; - } + } else { + throw { + message: CONSTANTS.apiResponses.FAILED_TO_START_RESOURCE, + status: HTTP_STATUS_CODE["failed_dependency"].status, + }; + } } } else { @@ -1286,7 +1291,12 @@ module.exports = class UserProjectsHelper { ) { projectCreation.data.userProfile = userProfileData.data.response; addReportInfoToSolution = true; - } + } else { + throw { + message: CONSTANTS.apiResponses.FAILED_TO_START_RESOURCE, + status: HTTP_STATUS_CODE["failed_dependency"].status, + }; + } } projectCreation.data.userRoleInformation = userRoleInformation; From e2e8e8e43136cc8589120347b3fb96798a38526c Mon Sep 17 00:00:00 2001 From: praveenKDass Date: Tue, 9 Jul 2024 16:09:58 +0530 Subject: [PATCH 89/92] Fixing program Name listing in Project inapp report --- module/reports/helper.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/module/reports/helper.js b/module/reports/helper.js index a07f2ec5..c1d4c6b3 100644 --- a/module/reports/helper.js +++ b/module/reports/helper.js @@ -361,12 +361,12 @@ module.exports = class ReportsHelper { } if (userRole != "") { + let regex = userRole.split(","); + regex.push(""); query.userRole = { - $in : [ - "", - ...userRole.split(",") - ] - } + $regex: regex.join("|"), + $options: "i", + }; } let searchQuery = []; From 687c5ff6b4dd4f49dae252bdd7549b7169951a9f Mon Sep 17 00:00:00 2001 From: praveenKDass Date: Thu, 12 Sep 2024 13:12:15 +0530 Subject: [PATCH 90/92] bug fixes:certificate lastName issue --- module/userProjects/helper.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/module/userProjects/helper.js b/module/userProjects/helper.js index 3e0f7013..9666002c 100644 --- a/module/userProjects/helper.js +++ b/module/userProjects/helper.js @@ -2533,12 +2533,17 @@ module.exports = class UserProjectsHelper { } certificateTemplateDetails[0].issuer.kid = CERTIFICATE_ISSUER_KID; } - + let certificateUserName + if(data.userProfile.lastName && data.userProfile.lastName.length > 0){ + certificateUserName = `${data.userProfile.firstName} ${data.userProfile.lastName}` + }else { + certificateUserName = `${data.userProfile.firstName}` + } //create certificate request body let certificateData = { recipient : { id : data.userId, - name : `${data.userProfile.firstName} ${data.userProfile.lastName}`, + name : certificateUserName, type : data.userProfile.profileUserType.type }, templateUrl : data.certificate.templateUrl, From fb69c7e50ed3d11820d322c6cf8b463d4ef1061b Mon Sep 17 00:00:00 2001 From: praveenKDass Date: Thu, 12 Sep 2024 20:39:42 +0530 Subject: [PATCH 91/92] minorChange:added indentation --- module/userProjects/helper.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/module/userProjects/helper.js b/module/userProjects/helper.js index 9666002c..67d487fc 100644 --- a/module/userProjects/helper.js +++ b/module/userProjects/helper.js @@ -2533,12 +2533,12 @@ module.exports = class UserProjectsHelper { } certificateTemplateDetails[0].issuer.kid = CERTIFICATE_ISSUER_KID; } - let certificateUserName - if(data.userProfile.lastName && data.userProfile.lastName.length > 0){ - certificateUserName = `${data.userProfile.firstName} ${data.userProfile.lastName}` - }else { - certificateUserName = `${data.userProfile.firstName}` - } + let certificateUserName; + if (data.userProfile.lastName && data.userProfile.lastName.length > 0) { + certificateUserName = `${data.userProfile.firstName} ${data.userProfile.lastName}`; + } else { + certificateUserName = `${data.userProfile.firstName}`; + } //create certificate request body let certificateData = { recipient : { From a33d03d112e80eff345c2cf5e22573f4b7e55075 Mon Sep 17 00:00:00 2001 From: borkarsaish65 Date: Thu, 19 Dec 2024 11:39:03 +0530 Subject: [PATCH 92/92] savepoint --- models/programs.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/models/programs.js b/models/programs.js index cb411bb3..4aaa6262 100644 --- a/models/programs.js +++ b/models/programs.js @@ -17,6 +17,14 @@ module.exports = { type : String, index : true }, + startDate:{ + type: Date, + index: true + }, + endDate: { + type : Date, + index : true + }, resourceType: [String], language: [String], keywords: [String],