Skip to content

[DEV] Send Email Notifications using Scheduler #449

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Aug 5, 2021
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
12 changes: 11 additions & 1 deletion app-constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,14 @@ const PaymentSchedulerStatus = {
CLOSE_CHALLENGE: 'close-challenge'
}

const JobStatus = {
OPEN: 'open'
}

const JobCandidateStatus = {
INTERVIEW: 'interview'
}

module.exports = {
UserRoles,
FullManagePermissionRoles,
Expand All @@ -164,5 +172,7 @@ module.exports = {
PaymentSchedulerStatus,
PaymentProcessingSwitch,
PaymentStatusRules,
ActiveWorkPeriodPaymentStatuses
ActiveWorkPeriodPaymentStatuses,
JobStatus,
JobCandidateStatus
}
7 changes: 7 additions & 0 deletions app.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const eventHandlers = require('./src/eventHandlers')
const interviewService = require('./src/services/InterviewService')
const { processScheduler } = require('./src/services/PaymentSchedulerService')
const { sendSurveys } = require('./src/services/SurveyService')
const emailNotificationService = require('./src/services/EmailNotificationService')

// setup express app
const app = express()
Expand Down Expand Up @@ -103,6 +104,12 @@ const server = app.listen(app.get('port'), () => {
schedule.scheduleJob(config.WEEKLY_SURVEY.CRON, sendSurveys)
// schedule payment processing
schedule.scheduleJob(config.PAYMENT_PROCESSING.CRON, processScheduler)

schedule.scheduleJob(config.CRON_CANDIDATE_REVIEW, emailNotificationService.sendCandidatesAvailableEmails)
schedule.scheduleJob(config.CRON_INTERVIEW_COMING_UP, emailNotificationService.sendInterviewComingUpEmails)
schedule.scheduleJob(config.CRON_INTERVIEW_COMPLETED, emailNotificationService.sendInterviewCompletedEmails)
schedule.scheduleJob(config.CRON_POST_INTERVIEW, emailNotificationService.sendPostInterviewActionEmails)
schedule.scheduleJob(config.CRON_UPCOMING_RESOURCE_BOOKING, emailNotificationService.sendResourceBookingExpirationEmails)
})

if (process.env.NODE_ENV === 'test') {
Expand Down
24 changes: 23 additions & 1 deletion config/default.js
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,8 @@ module.exports = {

// the Kafka message topic for sending email
EMAIL_TOPIC: process.env.EMAIL_TOPIC || 'external.action.email',
// the Kafka message topic for creating notifications
NOTIFICATIONS_CREATE_TOPIC: process.env.NOTIFICATIONS_CREATE_TOPIC || 'notifications.action.create',
// the emails address for receiving the issue report
// REPORT_ISSUE_EMAILS may contain comma-separated list of email which is converted to array
REPORT_ISSUE_EMAILS: (process.env.REPORT_ISSUE_EMAILS || '').split(','),
Expand Down Expand Up @@ -237,5 +239,25 @@ module.exports = {
interview: 'withdrawn',
selected: 'withdrawn',
offered: 'withdrawn'
}
},
// the sender email
NOTIFICATION_SENDER_EMAIL: process.env.NOTIFICATION_SENDER_EMAIL,
// the email notification sendgrid template id
NOTIFICATION_SENDGRID_TEMPLATE_ID: process.env.NOTIFICATION_SENDGRID_TEMPLATE_ID,
// hours after interview completed when we should post the notification
INTERVIEW_COMPLETED_NOTIFICATION_HOURS: process.env.INTERVIEW_COMPLETED_NOTIFICATION_HOURS || 4,
// no of weeks before expiry when we should post the notification
RESOURCE_BOOKING_EXPIRY_NOTIFICATION_WEEKS: process.env.RESOURCE_BOOKING_EXPIRY_NOTIFICATION_WEEKS || 3,
// frequency of cron checking for available candidates for review
CRON_CANDIDATE_REVIEW: process.env.CRON_CANDIDATE_REVIEW || '00 00 13 * * 0-6',
// frequency of cron checking for coming up interviews
// when changing this to frequency other than 5 mins, please change the minutesRange in sendInterviewComingUpEmails correspondingly
CRON_INTERVIEW_COMING_UP: process.env.CRON_INTERVIEW_COMING_UP || '*/5 * * * *',
// frequency of cron checking for interview completed
// when changing this to frequency other than 5 mins, please change the minutesRange in sendInterviewCompletedEmails correspondingly
CRON_INTERVIEW_COMPLETED: process.env.CRON_INTERVIEW_COMPLETED || '*/5 * * * *',
// frequency of cron checking for post interview actions
CRON_POST_INTERVIEW: process.env.CRON_POST_INTERVIEW || '00 00 13 * * 0-6',
// frequency of cron checking for upcoming resource bookings
CRON_UPCOMING_RESOURCE_BOOKING: process.env.CRON_UPCOMING_RESOURCE_BOOKING || '00 00 13 * * 1'
}
229 changes: 141 additions & 88 deletions config/email_template.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,99 +6,152 @@
const config = require('config')

module.exports = {
/* Report a general issue for a team.
*
* - projectId: the project ID. Example: 123412
* - projectName: the project name. Example: "TaaS API Misc Updates"
* - reportText: the body of reported issue. Example: "I have issue with ... \n ... Thank you in advance!"
/**
* List all the kind of emails which could be sent by the endpoint `POST /taas-teams/email` inside `teamTemplates`.
*/
'team-issue-report': {
subject: 'Issue Reported on TaaS Team {{projectName}} ({{projectId}}).',
body: 'Project Name: {{projectName}}' + '\n' +
'Project ID: {{projectId}}' + '\n' +
`Project URL: ${config.TAAS_APP_URL}/{{projectId}}` + '\n' +
'\n' +
'{{reportText}}',
recipients: config.REPORT_ISSUE_EMAILS,
sendgridTemplateId: config.REPORT_ISSUE_SENDGRID_TEMPLATE_ID
},
teamTemplates: {
/* Report a general issue for a team.
*
* - projectId: the project ID. Example: 123412
* - projectName: the project name. Example: "TaaS API Misc Updates"
* - reportText: the body of reported issue. Example: "I have issue with ... \n ... Thank you in advance!"
*/
'team-issue-report': {
subject: 'Issue Reported on TaaS Team {{projectName}} ({{projectId}}).',
body: 'Project Name: {{projectName}}' + '\n' +
'Project ID: {{projectId}}' + '\n' +
`Project URL: ${config.TAAS_APP_URL}/{{projectId}}` + '\n' +
'\n' +
'{{reportText}}',
recipients: config.REPORT_ISSUE_EMAILS,
sendgridTemplateId: config.REPORT_ISSUE_SENDGRID_TEMPLATE_ID
},

/* Report issue for a particular member
*
* - userHandle: the user handle. Example: "bili_2021"
* - projectId: the project ID. Example: 123412
* - projectName: the project name. Example: "TaaS API Misc Updates"
* - reportText: the body of reported issue. Example: "I have issue with ... \n ... Thank you in advance!"
*/
'member-issue-report': {
subject: 'Issue Reported for member {{userHandle}} on TaaS Team {{projectName}} ({{projectId}}).',
body: 'User Handle: {{userHandle}}' + '\n' +
'Project Name: {{projectName}}' + '\n' +
'Project ID: {{projectId}}' + '\n' +
`Project URL: ${config.TAAS_APP_URL}/{{projectId}}` + '\n' +
'\n' +
'{{reportText}}',
recipients: config.REPORT_ISSUE_EMAILS,
sendgridTemplateId: config.REPORT_ISSUE_SENDGRID_TEMPLATE_ID
},
/* Report issue for a particular member
*
* - userHandle: the user handle. Example: "bili_2021"
* - projectId: the project ID. Example: 123412
* - projectName: the project name. Example: "TaaS API Misc Updates"
* - reportText: the body of reported issue. Example: "I have issue with ... \n ... Thank you in advance!"
*/
'member-issue-report': {
subject: 'Issue Reported for member {{userHandle}} on TaaS Team {{projectName}} ({{projectId}}).',
body: 'User Handle: {{userHandle}}' + '\n' +
'Project Name: {{projectName}}' + '\n' +
'Project ID: {{projectId}}' + '\n' +
`Project URL: ${config.TAAS_APP_URL}/{{projectId}}` + '\n' +
'\n' +
'{{reportText}}',
recipients: config.REPORT_ISSUE_EMAILS,
sendgridTemplateId: config.REPORT_ISSUE_SENDGRID_TEMPLATE_ID
},

/* Request extension for a particular member
*
* - userHandle: the user handle. Example: "bili_2021"
* - projectId: the project ID. Example: 123412
* - projectName: the project name. Example: "TaaS API Misc Updates"
* - text: comment for the request. Example: "I would like to keep working with this member for 2 months..."
*/
'extension-request': {
subject: 'Extension Requested for member {{userHandle}} on TaaS Team {{projectName}} ({{projectId}}).',
body: 'User Handle: {{userHandle}}' + '\n' +
'Project Name: {{projectName}}' + '\n' +
'Project ID: {{projectId}}' + '\n' +
`Project URL: ${config.TAAS_APP_URL}/{{projectId}}` + '\n' +
'\n' +
'{{text}}',
recipients: config.REPORT_ISSUE_EMAILS,
sendgridTemplateId: config.REQUEST_EXTENSION_SENDGRID_TEMPLATE_ID
/* Request extension for a particular member
*
* - userHandle: the user handle. Example: "bili_2021"
* - projectId: the project ID. Example: 123412
* - projectName: the project name. Example: "TaaS API Misc Updates"
* - text: comment for the request. Example: "I would like to keep working with this member for 2 months..."
*/
'extension-request': {
subject: 'Extension Requested for member {{userHandle}} on TaaS Team {{projectName}} ({{projectId}}).',
body: 'User Handle: {{userHandle}}' + '\n' +
'Project Name: {{projectName}}' + '\n' +
'Project ID: {{projectId}}' + '\n' +
`Project URL: ${config.TAAS_APP_URL}/{{projectId}}` + '\n' +
'\n' +
'{{text}}',
recipients: config.REPORT_ISSUE_EMAILS,
sendgridTemplateId: config.REQUEST_EXTENSION_SENDGRID_TEMPLATE_ID
},

/* Request interview for a job candidate
*
* - interviewType: the x.ai interview type. Example: "interview-30"
* - interviewRound: the round of the interview. Example: 2
* - interviewDuration: duration of the interview, in minutes. Example: 30
* - interviewerList: The list of interviewer email addresses. Example: "first@attendee.com, second@attendee.com"
* - candidateId: the id of the jobCandidate. Example: "cc562545-7b75-48bf-87e7-50b3c57e41b1"
* - candidateName: Full name of candidate. Example: "John Doe"
* - jobName: The title of the job. Example: "TaaS API Misc Updates"
*
* Template (defined in SendGrid):
* Subject: '{{interviewType}} tech interview with {{candidateName}} for {{jobName}} is requested by the Customer'
* Body:
* 'Hello!
* <br /><br />
* Congratulations, you have been selected to participate in a Topcoder Gig Work Interview!
* <br /><br />
* Please monitor your email for a response to this where you can coordinate your availability.
* <br /><br />
* Interviewee: {{candidateName}}<br />
* Interviewer(s): {{interviewerList}}<br />
* Interview Length: {{interviewDuration}} minutes
* <br /><br />
* /{{interviewType}}
* <br /><br />
* Topcoder Info:<br />
* Note: "id: {{candidateId}}, round: {{interviewRound}}"'
*
* Note, that the template should be defined in SendGrid.
* The subject & body above (identical to actual SendGrid template) is for reference purposes.
* We won't pass subject & body but only substitutions (replacements in template subject/body).
*/
'interview-invitation': {
subject: '',
body: '',
from: config.INTERVIEW_INVITATION_SENDER_EMAIL,
cc: config.INTERVIEW_INVITATION_CC_LIST,
recipients: config.INTERVIEW_INVITATION_RECIPIENTS_LIST,
sendgridTemplateId: config.INTERVIEW_INVITATION_SENDGRID_TEMPLATE_ID
}
},

/* Request interview for a job candidate
*
* - interviewType: the x.ai interview type. Example: "interview-30"
* - interviewRound: the round of the interview. Example: 2
* - interviewDuration: duration of the interview, in minutes. Example: 30
* - interviewerList: The list of interviewer email addresses. Example: "first@attendee.com, second@attendee.com"
* - candidateId: the id of the jobCandidate. Example: "cc562545-7b75-48bf-87e7-50b3c57e41b1"
* - candidateName: Full name of candidate. Example: "John Doe"
* - jobName: The title of the job. Example: "TaaS API Misc Updates"
*
* Template (defined in SendGrid):
* Subject: '{{interviewType}} tech interview with {{candidateName}} for {{jobName}} is requested by the Customer'
* Body:
* 'Hello!
* <br /><br />
* Congratulations, you have been selected to participate in a Topcoder Gig Work Interview!
* <br /><br />
* Please monitor your email for a response to this where you can coordinate your availability.
* <br /><br />
* Interviewee: {{candidateName}}<br />
* Interviewer(s): {{interviewerList}}<br />
* Interview Length: {{interviewDuration}} minutes
* <br /><br />
* /{{interviewType}}
* <br /><br />
* Topcoder Info:<br />
* Note: "id: {{candidateId}}, round: {{interviewRound}}"'
*
* Note, that the template should be defined in SendGrid.
* The subject & body above (identical to actual SendGrid template) is for reference purposes.
* We won't pass subject & body but only substitutions (replacements in template subject/body).
/**
* List all kind of emails which could be send as Email Notifications by scheduler, API endpoints or anything else.
*/
'interview-invitation': {
subject: '',
body: '',
from: config.INTERVIEW_INVITATION_SENDER_EMAIL,
cc: config.INTERVIEW_INVITATION_CC_LIST,
recipients: config.INTERVIEW_INVITATION_RECIPIENTS_LIST,
sendgridTemplateId: config.INTERVIEW_INVITATION_SENDGRID_TEMPLATE_ID
notificationEmailTemplates: {
'taas.notification.candidates-available-for-review': {
subject: 'Topcoder - {{teamName}} has job candidates available for review',
body: '',
recipients: [],
from: config.NOTIFICATION_SENDER_EMAIL,
sendgridTemplateId: config.NOTIFICATION_SENDGRID_TEMPLATE_ID
},
'taas.notification.interview-coming-up-host': {
subject: 'Topcoder - Interview Coming Up: {{jobTitle}} with {{guestFullName}}',
body: '',
recipients: [],
from: config.NOTIFICATION_SENDER_EMAIL,
sendgridTemplateId: config.NOTIFICATION_SENDGRID_TEMPLATE_ID
},
'taas.notification.interview-coming-up-guest': {
subject: 'Topcoder - Interview Coming Up: {{jobTitle}} with {{hostFullName}}',
body: '',
recipients: [],
from: config.NOTIFICATION_SENDER_EMAIL,
sendgridTemplateId: config.NOTIFICATION_SENDGRID_TEMPLATE_ID
},
'taas.notification.interview-awaits-resolution': {
subject: 'Topcoder - Interview Awaits Resolution: {{jobTitle}} for {{guestFullName}}',
body: '',
recipients: [],
from: config.NOTIFICATION_SENDER_EMAIL,
sendgridTemplateId: config.NOTIFICATION_SENDGRID_TEMPLATE_ID
},
'taas.notification.post-interview-action-required': {
subject: 'Topcoder - Candidate Action Required in {{teamName}} for {{numCandidates}} candidates',
body: '',
recipients: [],
from: config.NOTIFICATION_SENDER_EMAIL,
sendgridTemplateId: config.NOTIFICATION_SENDGRID_TEMPLATE_ID
},
'taas.notification.resource-booking-expiration': {
subject: 'Topcoder - Resource Booking Expiring in {{teamName}} for {{numResourceBookings}} resource bookings',
body: '',
recipients: [],
from: config.NOTIFICATION_SENDER_EMAIL,
sendgridTemplateId: config.NOTIFICATION_SENDGRID_TEMPLATE_ID
}
}
}
Loading