From 453ec9bf2b95fac6a22ffb1c9417178260f0eddc Mon Sep 17 00:00:00 2001 From: yoution Date: Thu, 19 Aug 2021 18:27:56 +0800 Subject: [PATCH 1/9] fix: issue #509 --- src/common/helper.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/common/helper.js b/src/common/helper.js index 05ed2c5c..cb29890a 100644 --- a/src/common/helper.js +++ b/src/common/helper.js @@ -2062,6 +2062,8 @@ function formatDateTimeEDT (date) { function formatDateEDT (date) { if (date) { return moment(date).tz('America/New_York').format('MMM D, YYYY') + } else { + return 'TBD' } } From 058d6d05443ee0066d6c50af3698af26ba24d624 Mon Sep 17 00:00:00 2001 From: Maksym Mykhailenko Date: Fri, 20 Aug 2021 10:22:06 +0300 Subject: [PATCH 2/9] fix recipients for project member notifications --- src/services/NotificationsSchedulerService.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/services/NotificationsSchedulerService.js b/src/services/NotificationsSchedulerService.js index d90c77b6..51c6d527 100644 --- a/src/services/NotificationsSchedulerService.js +++ b/src/services/NotificationsSchedulerService.js @@ -239,7 +239,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: { @@ -258,7 +258,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: { @@ -323,7 +323,7 @@ async function sendInterviewCompletedNotifications () { sendNotification({}, { template: 'taas.notification.interview-awaits-resolution', - recipients: [interview.hostEmail], + recipients: [{ email: interview.hostEmail }], data: { ...data, notificationType: { @@ -543,7 +543,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 = { @@ -557,14 +557,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' From 68feb1dd6715a80679f3475a8ffd1be82e3e52dc Mon Sep 17 00:00:00 2001 From: Maksym Mykhailenko Date: Fri, 20 Aug 2021 13:02:48 +0300 Subject: [PATCH 3/9] set default email sender for easier local dev --- config/default.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/default.js b/config/default.js index 6a8cfef3..80c1d3d8 100644 --- a/config/default.js +++ b/config/default.js @@ -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 From 9cefc08cdc333d5fe5af45b6796750c01c356600 Mon Sep 17 00:00:00 2001 From: Maksym Mykhailenko Date: Fri, 20 Aug 2021 14:29:35 +0300 Subject: [PATCH 4/9] added debug logs to see when notifications are triggered by scheduler --- src/services/NotificationsSchedulerService.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/services/NotificationsSchedulerService.js b/src/services/NotificationsSchedulerService.js index 51c6d527..e754e66f 100644 --- a/src/services/NotificationsSchedulerService.js +++ b/src/services/NotificationsSchedulerService.js @@ -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, @@ -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]: [] @@ -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) @@ -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 @@ -436,6 +440,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)) From be07bc69092a7338004cf5dbda5cb2f0fd54ce44 Mon Sep 17 00:00:00 2001 From: xxcxy Date: Fri, 20 Aug 2021 19:50:08 +0800 Subject: [PATCH 5/9] update the logic of Post Interview Action Reminder --- config/default.js | 2 ++ scripts/demo-email-notifications/index.js | 6 +++++- src/services/NotificationsSchedulerService.js | 5 ++++- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/config/default.js b/config/default.js index 6a8cfef3..3433b840 100644 --- a/config/default.js +++ b/config/default.js @@ -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', diff --git a/scripts/demo-email-notifications/index.js b/scripts/demo-email-notifications/index.js index 9a792b26..5cc5d7a8 100644 --- a/scripts/demo-email-notifications/index.js +++ b/scripts/demo-email-notifications/index.js @@ -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', jobId: 'ff76b81d-f49b-4019-b50e-c7932a818f19' }) + const interviewWithinOneDay = await Interview.findById('b1f7ba76-640f-47e2-9463-59e51b51ec60') + 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') diff --git a/src/services/NotificationsSchedulerService.js b/src/services/NotificationsSchedulerService.js index 51c6d527..2e99efec 100644 --- a/src/services/NotificationsSchedulerService.js +++ b/src/services/NotificationsSchedulerService.js @@ -353,7 +353,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)) + } } }] }) From 8aec9d3e31834860a9b17041440f5f6ce6bd1ee1 Mon Sep 17 00:00:00 2001 From: xxcxy Date: Fri, 20 Aug 2021 20:35:41 +0800 Subject: [PATCH 6/9] fix Issue in the email title when the team name has symbols --- src/common/helper.js | 4 ++-- src/eventHandlers/TeamEventHandler.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/common/helper.js b/src/common/helper.js index cb29890a..16292383 100644 --- a/src/common/helper.js +++ b/src/common/helper.js @@ -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')), @@ -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( diff --git a/src/eventHandlers/TeamEventHandler.js b/src/eventHandlers/TeamEventHandler.js index aeec9e25..985ffc61 100644 --- a/src/eventHandlers/TeamEventHandler.js +++ b/src/eventHandlers/TeamEventHandler.js @@ -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'), ',')}` }) } From 47d8c2f395607f6f1ce6896bd474ec3fe4fc4d59 Mon Sep 17 00:00:00 2001 From: xxcxy Date: Fri, 20 Aug 2021 21:00:36 +0800 Subject: [PATCH 7/9] fix test data --- scripts/demo-email-notifications/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/demo-email-notifications/index.js b/scripts/demo-email-notifications/index.js index 5cc5d7a8..04eb705b 100644 --- a/scripts/demo-email-notifications/index.js +++ b/scripts/demo-email-notifications/index.js @@ -45,7 +45,7 @@ async function resetNotificationRecords () { 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', jobId: 'ff76b81d-f49b-4019-b50e-c7932a818f19' }) - const interviewWithinOneDay = await Interview.findById('b1f7ba76-640f-47e2-9463-59e51b51ec60') + 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 From 2f6b92ea3b73fa0634fb042901488fdc90ce6901 Mon Sep 17 00:00:00 2001 From: xxcxy Date: Fri, 20 Aug 2021 21:51:42 +0800 Subject: [PATCH 8/9] fix test data --- scripts/demo-email-notifications/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/demo-email-notifications/index.js b/scripts/demo-email-notifications/index.js index 04eb705b..7e5b19c0 100644 --- a/scripts/demo-email-notifications/index.js +++ b/scripts/demo-email-notifications/index.js @@ -44,7 +44,7 @@ async function resetNotificationRecords () { const c2Interview = await Interview.findById('077aa2ca-5b60-4ad9-a965-1b37e08a5046') 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', jobId: 'ff76b81d-f49b-4019-b50e-c7932a818f19' }) + 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' }) From c0f968976a6647574cb9bd3a2f6f136910084c25 Mon Sep 17 00:00:00 2001 From: xxcxy Date: Fri, 20 Aug 2021 22:13:58 +0800 Subject: [PATCH 9/9] fix create project --- src/common/helper.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/common/helper.js b/src/common/helper.js index 16292383..4d2f1dab 100644 --- a/src/common/helper.js +++ b/src/common/helper.js @@ -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')) }) } /**