Skip to content
121 changes: 58 additions & 63 deletions src/helpers/userInvite.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ const { Op } = require('sequelize')
const emailEncryption = require('@utils/emailEncryption')
const userOrganizationQueries = require('@database/queries/userOrganization')
const userOrganizationRoleQueries = require('@database/queries/userOrganizationRole')
const { eventBodyDTO, keysFilter } = require('@dtos/userDTO')
const { eventBodyDTO, keysFilter, transformUser } = require('@dtos/userDTO')
const { broadcastEvent } = require('@helpers/eventBroadcasterMain')
const { generateUniqueUsername, generateUniqueCodeString } = require('@utils/usernameGenerator.js')
const userRolesQueries = require('@database/queries/userOrganizationRole')
Expand All @@ -39,7 +39,15 @@ let externalEntityNameIdMap = {}
let emailAndPhoneMissing = false
let updatedUserIds = []
let loginUrl = ''

const outPutFileKeys = ['name', 'email', 'phone_code', 'phone', 'username', 'password', 'roles', 'statusOrUserId'] // final output file headers
const filterOutput = (input) => {
return outPutFileKeys.reduce((acc, key) => {
if (input.hasOwnProperty(key)) {
acc[key] = input[key]
}
return acc
}, {})
}
module.exports = class UserInviteHelper {
static async uploadInvites(data) {
return new Promise(async (resolve, reject) => {
Expand Down Expand Up @@ -559,8 +567,9 @@ module.exports = class UserInviteHelper {
if (invitee.phone && !invitee.phone_code) {
invalidFields.push('phone_code')
}
// check if the phone is duplicate

if (invitee.phone && invitee.phone_code) {
// check if the phone is duplicate
if (duplicateChecker.includes(`${invitee.phone_code}${invitee.phone}`)) {
duplicateValues.push('phone')
}
Expand Down Expand Up @@ -644,7 +653,7 @@ module.exports = class UserInviteHelper {
invitee.roles = invitee.roles.length > 0 ? invitee.roles.join(',') : ''
delete invitee.meta
invitee.statusOrUserId = errorMessageArray.join('. ')
input.push(invitee)
input.push(filterOutput(invitee))
continue
}

Expand Down Expand Up @@ -673,7 +682,7 @@ module.exports = class UserInviteHelper {
: 'User already exist or invited'
invitee.roles = invitee.roles.length > 0 ? invitee.roles.join(',') : ''
delete invitee.meta
input.push(invitee)
input.push(filterOutput(invitee))
continue
}

Expand Down Expand Up @@ -703,6 +712,7 @@ module.exports = class UserInviteHelper {
)
if (currentOrgs.length <= 0) {
invitee.statusOrUserId = `User not found in tenant : ${user.tenant_code} `
input.push(filterOutput(invitee))
continue
}
await userOrganizationQueries.changeUserOrganization({
Expand Down Expand Up @@ -822,49 +832,43 @@ module.exports = class UserInviteHelper {
userUpdateData.meta ||
userUpdateData.password_update
) {
// const userCred = await UserCredentialQueries.findOne({
// email: encryptedEmail,
// })

const [count, userUpdate] = await userQueries.updateUser(
{ id: existingUser.id },
userUpdateData
)
updatedUserIds.push(existingUser.id)

// find the keys which are modified
let modifiedKeys = Object.keys(userUpdate[0].dataValues).filter((key) => {
const current = userUpdate[0].dataValues[key]
const previous = userUpdate[0]._previousDataValues[key]
return current !== previous && !_.isEqual(current, previous)
})
// remove critical and obvious datas from the keys to be sent in event
modifiedKeys = keysFilter(modifiedKeys)

let oldValues = {},
// format old values for event and remove transform it to match the standards
let oldValues = transformUser(userUpdate[0]?._previousDataValues) || {},
newValues = {},
userFetch = {}
userFetch =
isOrgUpdate ||
isRoleUpdated ||
modifiedKeys.length > 0 ||
additionalCsvHeaders.length > 0
? await userQueries.findAllUserWithOrganization(
{ id: existingUser.id },
{},
user.tenant_code
)
: {} // fetch the user with organization and roles if any of the critical fields are updated
userFetch = userFetch?.[0] || {}

// if org or role is updated assign the old and new values
if (isOrgUpdate || isRoleUpdated) {
oldValues.organizations = existingUser.organizations
userFetch = await userQueries.findAllUserWithOrganization(
{ id: existingUser.id },
{},
user.tenant_code
)

newValues.organizations = userFetch[0].organizations
newValues.organizations = userFetch?.organizations
}

// if any keys are modified in user table or additional csv headers are present prepare the new values
if (modifiedKeys.length > 0 || additionalCsvHeaders.length > 0) {
if (Object.keys(userFetch).length == 0) {
userFetch = await userQueries.findAllUserWithOrganization(
{ id: existingUser.id },
{},
user.tenant_code
)
}

userFetch = userCredentials.find((user) => user.id == existingUser.id)
let userMeta = { ...userFetch?.meta }
userFetch = await utils.processDbResponse(userFetch, prunedEntities)
const comparisonKeys = [...modifiedKeys, ...additionalCsvHeaders]
comparisonKeys.forEach((modifiedKey) => {
if (modifiedKey == 'meta') {
Expand All @@ -888,24 +892,29 @@ module.exports = class UserInviteHelper {
}
})

oldValues = userFetch
oldValues.email = oldValues.email
? emailEncryption.decrypt(oldValues.email)
: oldValues.email
/*
user meta with entity and _id from external micro-service is passed with entity information and value of the _ids
to prarse it to a standard format with data for emitting the event
*/
userMeta = utils.parseMetaData(userMeta, prunedEntities, externalEntityNameIdMap)
oldValues = {
...oldValues,
...userMeta,
}
oldValues.phone = oldValues.phone
? emailEncryption.decrypt(oldValues.phone)
: oldValues.phone
}

// parse meta data to fetch the entity name and value instead of _ids
const userOldMeta = oldValues?.meta
? utils.parseMetaData(oldValues.meta, prunedEntities, externalEntityNameIdMap)
: {}
// delete the meta raw field
delete oldValues.meta
// merge the parsed meta data with old values
oldValues = {
...oldValues,
...userOldMeta,
}
// decrypt email and phone for event
if (oldValues?.email) {
oldValues.email = emailEncryption.decrypt(oldValues.email)
}
if (oldValues?.phone) {
oldValues.phone = emailEncryption.decrypt(oldValues.phone)
}
if (Object.keys(oldValues).length > 0 || Object.keys(newValues).length > 0) {
const eventBody = eventBodyDTO({
entity: 'user',
Expand All @@ -926,23 +935,6 @@ module.exports = class UserInviteHelper {
broadcastEvent('userEvents', { requestBody: eventBody, isInternal: true })
}

// Update UserCredential with organization_id and potentially password
// const credentialUpdateData = { organization_id: user.organization_id }

// // Add password to update data if provided
// if (invitee.password) {
// // Assuming you have a password hashing utility
// // You might need to adjust this based on your password handling
// credentialUpdateData.password = invitee.password // Consider hashing
// }

// await UserCredentialQueries.updateUser(
// {
// email: encryptedEmail,
// },
// credentialUpdateData
// )

const currentRoles = existingUser.roles.map((role) => role.title)
let newRoles = []
if (userUpdateData?.roles) {
Expand Down Expand Up @@ -993,6 +985,8 @@ module.exports = class UserInviteHelper {
} else {
//user doesn't have access to update user data
invitee.statusOrUserId = 'Unauthorised to bulk upload user from another organisation'
invitee.roles = invitee.roles.length > 0 ? invitee.roles.join(',') : ''
input.push(filterOutput(invitee))
continue
}
}
Expand Down Expand Up @@ -1134,8 +1128,9 @@ module.exports = class UserInviteHelper {
created_by: user.id,
name: parsedData?.name,
username: parsedData?.username,
email: raw_email,
email: raw_email ? raw_email : null,
phone: parsedData?.phone ? parsedData?.phone : null,
phone_code: parsedData?.phone_code ? parsedData?.phone_code : null,
organizations,
tenant_code: user?.tenant_code,
...metaData,
Expand Down Expand Up @@ -1313,7 +1308,7 @@ module.exports = class UserInviteHelper {
invitee.statusOrUserId = `${invitee.statusOrUserId} , Login URL : ${loginUrl}`
}
delete invitee.meta
input.push(invitee)
input.push(filterOutput(invitee))
}
if (updatedUserIds.length > 0) {
// flush all user active sessions after update
Expand Down