Skip to content
4 changes: 3 additions & 1 deletion config/default.js
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ module.exports = {
offered: 'withdrawn'
},
// the sender email
NOTIFICATION_SENDER_EMAIL: process.env.NOTIFICATION_SENDER_EMAIL,
NOTIFICATION_SENDER_EMAIL: process.env.NOTIFICATION_SENDER_EMAIL || 'noreply@topcoder.com',
// the email notification sendgrid template id
NOTIFICATION_SENDGRID_TEMPLATE_ID: process.env.NOTIFICATION_SENDGRID_TEMPLATE_ID,
// frequency of cron checking for available candidates for review
Expand All @@ -275,6 +275,8 @@ module.exports = {
INTERVIEW_COMPLETED_PAST_TIME: process.env.INTERVIEW_COMPLETED_PAST_TIME || 'PT4H',
// The time before resource booking expiry when we should start sending notifications
RESOURCE_BOOKING_EXPIRY_TIME: process.env.RESOURCE_BOOKING_EXPIRY_TIME || 'P21D',
// The match window for fetching post interview actions
POST_INTERVIEW_ACTION_MATCH_WINDOW: process.env.POST_INTERVIEW_ACTION_MATCH_WINDOW || 'P1D',
// The Stripe
STRIPE_SECRET_KEY: process.env.STRIPE_SECRET_KEY,
CURRENCY: process.env.CURRENCY || 'usd',
Expand Down
6 changes: 5 additions & 1 deletion scripts/demo-email-notifications/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,11 @@ async function resetNotificationRecords () {
const jobCandidate = await JobCandidate.findById('881a19de-2b0c-4bb9-b36a-4cb5e223bdb5')
await jobCandidate.update({ status: 'interview' })
const c2Interview = await Interview.findById('077aa2ca-5b60-4ad9-a965-1b37e08a5046')
await c2Interview.update({ startTimestamp: completedStartTimestamp, duration, endTimestamp, guestNames: ['guest1', 'guest2'], hostName: 'hostName' })
await c2Interview.update({ startTimestamp: moment().subtract(moment.duration(config.POST_INTERVIEW_ACTION_MATCH_WINDOW)).subtract(30, 'm').toDate(), duration, endTimestamp, guestNames: ['guest1', 'guest2'], hostName: 'hostName' })
const jobCandidateWithinOneDay = await JobCandidate.findById('827ee401-df04-42e1-abbe-7b97ce7937ff')
await jobCandidateWithinOneDay.update({ status: 'interview' })
const interviewWithinOneDay = await Interview.findById('3144fa65-ea1a-4bec-81b0-7cb1c8845826')
await interviewWithinOneDay.update({ startTimestamp: completedStartTimestamp, duration, endTimestamp, guestNames: ['guest1', 'guest2'], hostName: 'hostName' })

// reset upcoming resource booking expiration records
localLogger.info('reset upcoming resource booking expiration records')
Expand Down
9 changes: 6 additions & 3 deletions src/common/helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -1038,7 +1038,7 @@ async function getProjects (currentUser, criteria = {}) {
message: `response body: ${JSON.stringify(res.body)}`
})
const result = _.map(res.body, (item) => {
return _.pick(item, ['id', 'name', 'invites', 'members'])
return _.extend(_.pick(item, ['id', 'invites', 'members']), { name: _.unescape(item.name) })
})
return {
total: Number(_.get(res.headers, 'x-total')),
Expand Down Expand Up @@ -1195,7 +1195,7 @@ async function getProjectById (currentUser, id) {
context: 'getProjectById',
message: `response body: ${JSON.stringify(res.body)}`
})
return _.pick(res.body, ['id', 'name', 'invites', 'members'])
return _.extend(_.pick(res.body, ['id', 'invites', 'members']), { name: _.unescape(res.body.name) })
} catch (err) {
if (err.status === HttpStatus.FORBIDDEN) {
throw new errors.ForbiddenError(
Expand Down Expand Up @@ -1925,7 +1925,8 @@ async function createProject (currentUser, data) {
context: 'createProject',
message: `response body: ${JSON.stringify(res)}`
})
return _.get(res, 'body')
const result = _.get(res, 'body')
return _.extend(result, { name: _.unescape(_.get(result, 'name')) })
}

/**
Expand Down Expand Up @@ -2062,6 +2063,8 @@ function formatDateTimeEDT (date) {
function formatDateEDT (date) {
if (date) {
return moment(date).tz('America/New_York').format('MMM D, YYYY')
} else {
return 'TBD'
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/eventHandlers/TeamEventHandler.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ async function sendNotificationEmail (payload) {
logger.debug({
component: 'TeamEventHandler',
context: 'sendNotificationEmail',
message: `project id: ${payload.project.id} created with jobs: ${_.join(_.map(payload.jobs, 'id'), ',')}`
message: `project id: ${payload.project.id}, subject: ${data.subject}, created with jobs: ${_.join(_.map(payload.jobs, 'id'), ',')}`
})
}

Expand Down
22 changes: 15 additions & 7 deletions src/services/NotificationsSchedulerService.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ async function getDataForInterview (interview, jobCandidate, job) {
* Sends notifications to all the teams which have candidates available for review
*/
async function sendCandidatesAvailableNotifications () {
localLogger.debug('[sendCandidatesAvailableNotifications]: Looking for due records...')
const jobsDao = await Job.findAll({
include: [{
model: JobCandidate,
Expand Down Expand Up @@ -190,6 +191,7 @@ async function sendCandidatesAvailableNotifications () {
* Sends reminders to the hosts and guests about their upcoming interview(s)
*/
async function sendInterviewComingUpNotifications () {
localLogger.debug('[sendInterviewComingUpNotifications]: Looking for due records...')
const currentTime = moment.utc()
const timestampFilter = {
[Op.or]: []
Expand Down Expand Up @@ -239,7 +241,7 @@ async function sendInterviewComingUpNotifications () {
if (!_.isEmpty(interview.hostEmail)) {
sendNotification({}, {
template: 'taas.notification.interview-coming-up-host',
recipients: [interview.hostEmail],
recipients: [{ email: interview.hostEmail }],
data: {
...data,
notificationType: {
Expand All @@ -258,7 +260,7 @@ async function sendInterviewComingUpNotifications () {
// send guest emails
sendNotification({}, {
template: 'taas.notification.interview-coming-up-guest',
recipients: interview.guestEmails,
recipients: interview.guestEmails.map((email) => ({ email })),
data: {
...data,
notificationType: {
Expand All @@ -281,6 +283,7 @@ async function sendInterviewComingUpNotifications () {
* Sends reminder to the interview host after it ends to change the interview status
*/
async function sendInterviewCompletedNotifications () {
localLogger.debug('[sendInterviewCompletedNotifications]: Looking for due records...')
const window = moment.duration(config.INTERVIEW_COMPLETED_MATCH_WINDOW)
const rangeStart = moment.utc().subtract(moment.duration(config.INTERVIEW_COMPLETED_PAST_TIME))
const rangeEnd = rangeStart.clone().add(window)
Expand Down Expand Up @@ -323,7 +326,7 @@ async function sendInterviewCompletedNotifications () {

sendNotification({}, {
template: 'taas.notification.interview-awaits-resolution',
recipients: [interview.hostEmail],
recipients: [{ email: interview.hostEmail }],
data: {
...data,
notificationType: {
Expand All @@ -344,6 +347,7 @@ async function sendInterviewCompletedNotifications () {
* to update the job candidate status
*/
async function sendPostInterviewActionNotifications () {
localLogger.debug('[sendPostInterviewActionNotifications]: Looking for due records...')
const completedJobCandidates = await JobCandidate.findAll({
where: {
status: constants.JobCandidateStatus.INTERVIEW
Expand All @@ -353,7 +357,10 @@ async function sendPostInterviewActionNotifications () {
as: 'interviews',
required: true,
where: {
status: constants.Interviews.Status.Completed
status: constants.Interviews.Status.Completed,
startTimestamp: {
[Op.lte]: moment.utc().subtract(moment.duration(config.POST_INTERVIEW_ACTION_MATCH_WINDOW))
}
}
}]
})
Expand Down Expand Up @@ -436,6 +443,7 @@ async function sendPostInterviewActionNotifications () {
* Sends reminders to all members of teams which have atleast one upcoming resource booking expiration
*/
async function sendResourceBookingExpirationNotifications () {
localLogger.debug('[sendResourceBookingExpirationNotifications]: Looking for due records...')
const currentTime = moment.utc()
const maxEndDate = currentTime.clone().add(moment.duration(config.RESOURCE_BOOKING_EXPIRY_TIME))

Expand Down Expand Up @@ -543,7 +551,7 @@ async function sendResourceBookingExpirationNotifications () {
async function sendNotification (currentUser, data, webNotifications = []) {
const template = emailTemplates[data.template]
const dataCC = data.cc || []
const templateCC = template.cc || []
const templateCC = (template.cc || []).map(email => ({ email }))
const dataRecipients = data.recipients || []
const templateRecipients = (template.recipients || []).map(email => ({ email }))
const subjectBody = {
Expand All @@ -557,14 +565,14 @@ async function sendNotification (currentUser, data, webNotifications = []) {
)
}

const recipients = _.map(_.uniq([...dataRecipients, ...templateRecipients]), function (r) { return { email: r } })
const recipients = _.uniq([...dataRecipients, ...templateRecipients])
const emailData = {
serviceId: 'email',
type: data.template,
details: {
from: data.from || template.from,
recipients,
cc: _.map(_.uniq([...dataCC, ...templateCC]), function (r) { return { email: r } }),
cc: _.uniq([...dataCC, ...templateCC]),
data: { ...data.data, ...subjectBody },
sendgridTemplateId: template.sendgridTemplateId,
version: 'v3'
Expand Down