Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/.env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -182,3 +182,6 @@ SCHEDULER_SERVICE_BASE_URL= '/scheduler/'

#Refresh interval for materialized views
REFRESH_VIEW_INTERVAL= 540000

#Generic Email template for new users
GENERIC_INVITATION_EMAIL_TEMPLATE_CODE=generic_invite
2 changes: 2 additions & 0 deletions src/constants/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ module.exports = {
INACTIVE_STATUS: 'INACTIVE',
MENTOR_ROLE: 'mentor',
MENTEE_ROLE: 'mentee',
SESSION_MANAGER_ROLE: 'session_manager',
redisUserPrefix: 'user_',
redisOrgPrefix: 'org_',
location: 'location',
Expand All @@ -120,4 +121,5 @@ module.exports = {
materializedViewsPrefix: 'm_',
DELETED_STATUS: 'DELETED',
DEFAULT_ORG_VISIBILITY: 'PUBLIC',
ROLE_TYPE_NON_SYSTEM: 0,
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
'use strict'
const moment = require('moment')

/** @type {import('sequelize-cli').Migration} */
module.exports = {
up: async (queryInterface, Sequelize) => {
const defaultOrgId = queryInterface.sequelize.options.defaultOrgId
if (!defaultOrgId) {
throw new Error('Default org ID is undefined. Please make sure it is set in sequelize options.')
}
let notificationTemplateData = [
{
code: 'generic_invite',
subject: 'Welcome Aboard as a {roles}',
body: '<p>Dear {name},</p> We are delighted to inform you that you have been successfully onboarded as a {roles} for {orgName}. You can now explore {appName}. <br>We request you to register on our Mentoring Platform (if not already), to start your journey with us as a organization admin. <br><br> Click to register: {portalURL}',
status: 'ACTIVE',
type: 'email',
created_at: moment().format(),
updated_at: moment().format(),
email_header: 'email_header',
email_footer: 'email_footer',
organization_id: defaultOrgId,
},
]

await queryInterface.bulkInsert('notification_templates', notificationTemplateData, {})
},

down: async (queryInterface, Sequelize) => {
await queryInterface.bulkDelete('notification_templates', { code: 'generic_invite' })
},
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
'use strict'

/** @type {import('sequelize-cli').Migration} */
module.exports = {
up: async (queryInterface, Sequelize) => {
const defaultOrgId = queryInterface.sequelize.options.defaultOrgId
if (!defaultOrgId) {
throw new Error('Default org ID is undefined. Please make sure it is set in sequelize options.')
}
const registrationTemplate = await queryInterface.sequelize.query(
`SELECT * FROM notification_templates WHERE code = :code AND organization_id = :organization_id LIMIT 1`,
{
replacements: {
code: process.env.REGISTRATION_EMAIL_TEMPLATE_CODE,
organization_id: defaultOrgId,
},
type: Sequelize.QueryTypes.SELECT,
}
)

if (registrationTemplate) {
const update = await queryInterface.sequelize.query(
`UPDATE notification_templates SET body = :body WHERE code = :code AND organization_id = :organization_id`,
{
replacements: {
code: process.env.REGISTRATION_EMAIL_TEMPLATE_CODE,
organization_id: defaultOrgId,
body: '<p>Dear {name},</p> Welcome to {appName} community! . We are excited for you to start your journey as a {roles}. <br><br> Login to MentorED to start your journey <br> Click to register: {portalURL}',
},
type: Sequelize.QueryTypes.UPDATE,
}
)
console.log(update, 'update')
} else {
console.log('Registration template not found')
}
},

down: async (queryInterface, Sequelize) => {
//not required
},
}
2 changes: 1 addition & 1 deletion src/database/queries/orgUserInvite.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use strict'
const organizationUserInvite = require('../models/index').OrganizationUserInvite
const { UniqueConstraintError, ValidationError } = require('sequelize')
const { ValidationError } = require('sequelize')

exports.create = async (data) => {
try {
Expand Down
3 changes: 2 additions & 1 deletion src/database/queries/userCredential.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
'use strict'
const UserCredential = require('@database/models/index').UserCredential
const { UniqueConstraintError, ValidationError } = require('sequelize')

exports.create = async (data) => {
try {
const res = await UserCredential.create(data)
return res.get({ plain: true })
} catch (error) {
if (error instanceof UniqueConstraintError) {
return 'USER_ALREADY_EXISTS'
return 'User already exist'
} else if (error instanceof ValidationError) {
let message
error.errors.forEach((err) => {
Expand Down
4 changes: 4 additions & 0 deletions src/envVariables.js
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,10 @@ let enviromentVariables = {
optional: false,
default: 'aes-256-cbc',
},
GENERIC_INVITATION_EMAIL_TEMPLATE_CODE: {
message: 'Required generic invitation email template code',
optional: false,
},
}

let success = true
Expand Down
8 changes: 8 additions & 0 deletions src/generics/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,13 @@ const generateWhereClause = (tableName) => {
return whereClause
}

const getRoleTitlesFromId = (roleIds = [], roleList = []) => {
return roleIds.map((roleId) => {
const role = roleList.find((r) => r.id === roleId)
return role ? role.title : null
})
}

module.exports = {
generateToken,
hashPassword,
Expand Down Expand Up @@ -452,4 +459,5 @@ module.exports = {
isValidEmail,
isValidName,
generateWhereClause,
getRoleTitlesFromId,
}
1 change: 1 addition & 0 deletions src/sample.csv
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
name,email,roles
Sarah,sarah@tunerlabs.com,mentee
John,john@tunerlabs.com,mentor
Pradeep,pradeep@tunerlabs.com,"mentor,session_manager"
50 changes: 35 additions & 15 deletions src/services/account.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ module.exports = class AccountHelper {
if (invitedUserMatch) {
bodyData.organization_id = invitedUserMatch.organization_id
roles = invitedUserMatch.roles
role = await roleQueries.findOne(
role = await roleQueries.findAll(
{ id: invitedUserMatch.roles },
{
attributes: {
Expand All @@ -113,28 +113,29 @@ module.exports = class AccountHelper {
}
)

if (!role) {
if (!role.length > 0) {
return common.failureResponse({
message: 'ROLE_NOT_FOUND',
statusCode: httpStatusCode.not_acceptable,
responseCode: 'CLIENT_ERROR',
})
}

if (role.title === common.ORG_ADMIN_ROLE) {
isOrgAdmin = true

const defaultRole = await roleQueries.findOne(
{ title: process.env.DEFAULT_ROLE },
{
attributes: {
exclude: ['created_at', 'updated_at', 'deleted_at'],
},
}
)
role.forEach(async (eachRole) => {
if (eachRole.title === common.ORG_ADMIN_ROLE) {
const defaultRole = await roleQueries.findOne(
{ title: process.env.DEFAULT_ROLE },
{
attributes: {
exclude: ['created_at', 'updated_at', 'deleted_at'],
},
}
)

roles.push(defaultRole.id)
}
roles.push(defaultRole.id)
isOrgAdmin = true
}
})
bodyData.roles = roles
} else {
//find organization from email domain
Expand Down Expand Up @@ -234,6 +235,23 @@ module.exports = class AccountHelper {

user.user_roles = roleData

// format the roles for email template
let roleArray = []
if (roleData.length > 0) {
const mentorRoleExists = roleData.some((role) => role.title === common.MENTOR_ROLE)
if (mentorRoleExists) {
const updatedRoleList = roleData.filter((role) => role.title !== common.MENTEE_ROLE)
roleArray = _.map(updatedRoleList, 'title')
}
}

let roleToString =
roleArray.length > 0
? roleArray
.map((role) => role.replace(/_/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase()))
.join(' and ')
: ''

const accessToken = utilsHelper.generateToken(
tokenDetail,
process.env.ACCESS_TOKEN_SECRET,
Expand Down Expand Up @@ -290,6 +308,8 @@ module.exports = class AccountHelper {
body: utilsHelper.composeEmailBody(templateData.body, {
name: bodyData.name,
appName: process.env.APP_NAME,
roles: roleToString || '',
portalURL: process.env.PORTAL_URL,
}),
},
}
Expand Down
18 changes: 11 additions & 7 deletions src/services/org-admin.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ module.exports = class OrgAdminHelper {
}

const result = await fileUploadQueries.create(creationData)

if (!result?.id) {
return common.successResponse({
responseCode: 'CLIENT_ERROR',
Expand Down Expand Up @@ -421,12 +422,6 @@ function updateRoleForApprovedRequest(requestDetails, user) {
{ attributes: ['title', 'id', 'user_type', 'status'] }
)

const systemRoleIds = userRoles
.filter((role) => role.user_type === common.ROLE_TYPE_SYSTEM)
.map((role) => role.id)

let rolesToUpdate = [...systemRoleIds]

const newRole = await roleQueries.findOne(
{ id: requestDetails.role, status: common.ACTIVE_STATUS },
{ attributes: ['title', 'id', 'user_type', 'status'] }
Expand All @@ -440,7 +435,16 @@ function updateRoleForApprovedRequest(requestDetails, user) {
},
})

rolesToUpdate.push(requestDetails.role)
let rolesToUpdate = [...requestDetails.role]
let currentUserRoleIds = _.map(userRoles, 'id')

//remove mentee role from roles array
const menteeRoleId = userRoles.find((role) => role.title === common.MENTEE_ROLE)?.id
if (menteeRoleId && currentUserRoleIds.includes(menteeRoleId)) {
_.pull(currentUserRoleIds, menteeRoleId)
}
rolesToUpdate.push(...currentUserRoleIds)

const roles = _.uniq(rolesToUpdate)

await userQueries.updateUser(
Expand Down
Loading