From eb6909a8bcfc0077f5121c3cc580a2a9141fadd1 Mon Sep 17 00:00:00 2001 From: Akash Shah Date: Tue, 15 Nov 2022 20:28:17 +0530 Subject: [PATCH 1/3] Changes to prevent userProfile and userRoleInformation mismatch in projects. --- .../updateUserProfileInProjects.js | 331 ++++++++++++++++++ module/userProjects/helper.js | 199 +++++++++++ 2 files changed, 530 insertions(+) create mode 100644 migrations/userProfileAndRoleMismatchInProjects/updateUserProfileInProjects.js diff --git a/migrations/userProfileAndRoleMismatchInProjects/updateUserProfileInProjects.js b/migrations/userProfileAndRoleMismatchInProjects/updateUserProfileInProjects.js new file mode 100644 index 00000000..6f172436 --- /dev/null +++ b/migrations/userProfileAndRoleMismatchInProjects/updateUserProfileInProjects.js @@ -0,0 +1,331 @@ +/** + * 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"; + +(async () => { + + let connection = await MongoClient.connect(url, { useNewUrlParser: true }); + let db = connection.db(dbName); + try { + + let updatedProjectIds = []; + + //get all projects + let projectDocument = await db.collection('projects').find({ + userRoleInformation: {$exists : true}, + userProfile: {$exists : true}, + }).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; + } + ); + + let projectDocuments = await db.collection('projects').find({ + _id: { $in : projectIds } + }).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); + } + }) + } + + 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 diff --git a/module/userProjects/helper.js b/module/userProjects/helper.js index 1bc5651f..07018ad4 100644 --- a/module/userProjects/helper.js +++ b/module/userProjects/helper.js @@ -1101,6 +1101,7 @@ module.exports = class UserProjectsHelper { } solutionDetails = solutionDetails.data[0]; + } let projectCreation = @@ -1266,6 +1267,18 @@ module.exports = class UserProjectsHelper { } projectCreation.data.userRoleInformation = userRoleInformation; + + //compare & update userProfile with userRoleInformation + if ( projectCreation.data.userProfile && userRoleInformation ) { + let updatedUserProfile = await _updateUserProfileBasedOnUserRoleInfo( + projectCreation.data.userProfile, + userRoleInformation + ); + + if (updatedUserProfile && updatedUserProfile.success == true && updatedUserProfile.profileMismatchFound == true) { + projectCreation.data.userProfile = updatedUserProfile.data; + } + } let project = await projectQueries.createProject(projectCreation.data); @@ -3050,7 +3063,193 @@ function _projectData(data) { }) } +/** + * Validate & Update UserProfile in Projects. + * @method + * @name _updateUserProfileBasedOnUserRoleInfo + * @param {Object} userProfile - userProfile data. + * @param {Object} userRoleInformation - userRoleInformation data. + * @returns {Object} updated UserProfile information. +*/ + +function _updateUserProfileBasedOnUserRoleInfo(userProfile, userRoleInformation) { + return new Promise(async (resolve, reject) => { + try { + + + let updateUserProfileRoleInformation = false; // Flag to see if roleInformation i.e. userProfile.profileUserTypes has to be updated based on userRoleInfromation.roles + + if(userRoleInformation.role) { // Check if userRoleInformation has role value. + let rolesInUserRoleInformation = 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(!observation.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(!observation.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" + }) + } + } + } + } + + if(updateUserProfileRoleInformation) { // If profileUserTypes in userProfile was wrong and is updated as per userRoleInformation + userProfile.userRoleMismatchFoundAndUpdated = true; + } + + // Create location only object from userRoleInformation + let userRoleInformationLocationObject = _.omit(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 (UTILS.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 userProfileService.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 userProfileService.locationSearch(codeQuery); + if ( entityData.success ) { + userLocations = userLocations.concat(entityData.data); + } + } + + if ( userLocations.length > 0 ) { + userProfile["userLocations"] = userLocations; + userProfile.userLocationsMismatchFoundAndUpdated = true; // If userLocations in userProfile was wrong and is updated as per userRoleInformation + } + } + + return resolve({ + success: true, + profileMismatchFound : (updateUserProfileLocationInformation || updateUserProfileRoleInformation) ? true : false, + data: updatedUserProfile + }); + + } catch (error) { + return resolve({ + status: error.status || httpStatusCode.internal_server_error.status, + message: error.message || httpStatusCode.internal_server_error.message, + data : false + }); + } + }) +} \ No newline at end of file From 734b80d3b4ad1cdb80c0d0b37bc00693ae871b13 Mon Sep 17 00:00:00 2001 From: Akash Shah Date: Tue, 15 Nov 2022 21:13:45 +0530 Subject: [PATCH 2/3] Variable fix. --- 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 07018ad4..011ffb65 100644 --- a/module/userProjects/helper.js +++ b/module/userProjects/helper.js @@ -3241,7 +3241,7 @@ function _updateUserProfileBasedOnUserRoleInfo(userProfile, userRoleInformation) return resolve({ success: true, profileMismatchFound : (updateUserProfileLocationInformation || updateUserProfileRoleInformation) ? true : false, - data: updatedUserProfile + data: userProfile }); } catch (error) { From 3c730b48b81c0162669e35f2eba8a80ecca49c76 Mon Sep 17 00:00:00 2001 From: Akash Shah Date: Tue, 15 Nov 2022 21:25:43 +0530 Subject: [PATCH 3/3] Code fix. --- module/userProjects/helper.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/module/userProjects/helper.js b/module/userProjects/helper.js index 011ffb65..4f7cdf31 100644 --- a/module/userProjects/helper.js +++ b/module/userProjects/helper.js @@ -142,6 +142,9 @@ module.exports = class UserProjectsHelper { const projectsModel = Object.keys(schemas["projects"].schema); + if(data.userRoleInformation) delete data.userRoleInformation; + if(data.userProfile) delete data.userProfile; + let updateProject = {}; let projectData = await _projectData(data); if (projectData && projectData.success == true) { @@ -1269,7 +1272,7 @@ module.exports = class UserProjectsHelper { projectCreation.data.userRoleInformation = userRoleInformation; //compare & update userProfile with userRoleInformation - if ( projectCreation.data.userProfile && userRoleInformation ) { + if ( projectCreation.data.userProfile && userRoleInformation && Object.keys(userRoleInformation).length > 0 && Object.keys(projectCreation.data.userProfile).length > 0 ) { let updatedUserProfile = await _updateUserProfileBasedOnUserRoleInfo( projectCreation.data.userProfile, userRoleInformation