Skip to content

Commit 2d8680b

Browse files
committed
Challenge: 430559a5-abf5-4820-ade1-2c8a38b951fd Implemented email notifications
1 parent 07082f4 commit 2d8680b

File tree

12 files changed

+1246
-5
lines changed

12 files changed

+1246
-5
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,3 +124,6 @@ api.env
124124

125125
# macOS files
126126
.DS_Store
127+
128+
# rendered html for email template
129+
scripts/notification-renderer/rendered.html

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,7 @@ To be able to change and test `taas-es-processor` locally you can follow the nex
221221
| `npm run delete-index` | Delete Elasticsearch indexes. Use `-- --force` flag to skip confirmation |
222222
| `npm run data:import <filePath>` | Imports data into ES and db from filePath (`./data/demo-data.json` is used as default). Use `-- --force` flag to skip confirmation |
223223
| `npm run data:export <filePath>` | Exports data from ES and db into filePath (`./data/demo-data.json` is used as default). Use `-- --force` flag to skip confirmation |
224+
| `npm run renderTemplate <notificationId>` | Generates `scripts/notification-renderer/rendered.html` which has the rendered email template for the given `notificationId` where `notificationId` is one of the keys in `data/notifications.json` ex: `npm run renderTemplate upcomingResourceBookingExpiration` |
224225
| `npm run index:all` | Indexes all data from db into ES. Use `-- --force` flag to skip confirmation |
225226
| `npm run index:jobs <jobId>` | Indexes job data from db into ES, if jobId is not given all data is indexed. Use `-- --force` flag to skip confirmation |
226227
| `npm run index:job-candidates <jobCandidateId>` | Indexes job candidate data from db into ES, if jobCandidateId is not given all data is indexed. Use `-- --force` flag to skip confirmation |

app-constants.js

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,14 @@ const PaymentSchedulerStatus = {
152152
CLOSE_CHALLENGE: 'close-challenge'
153153
}
154154

155+
const JobStatus = {
156+
OPEN: 'open'
157+
}
158+
159+
const JobCandidateStatus = {
160+
INTERVIEW: 'interview'
161+
}
162+
155163
module.exports = {
156164
UserRoles,
157165
FullManagePermissionRoles,
@@ -164,5 +172,7 @@ module.exports = {
164172
PaymentSchedulerStatus,
165173
PaymentProcessingSwitch,
166174
PaymentStatusRules,
167-
ActiveWorkPeriodPaymentStatuses
175+
ActiveWorkPeriodPaymentStatuses,
176+
JobStatus,
177+
JobCandidateStatus
168178
}

app.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ const logger = require('./src/common/logger')
1414
const eventHandlers = require('./src/eventHandlers')
1515
const interviewService = require('./src/services/InterviewService')
1616
const { processScheduler } = require('./src/services/PaymentSchedulerService')
17+
const emailNotificationService = require('./src/services/EmailNotificationService')
1718

1819
// setup express app
1920
const app = express()
@@ -101,6 +102,12 @@ const server = app.listen(app.get('port'), () => {
101102

102103
// schedule payment processing
103104
schedule.scheduleJob(config.PAYMENT_PROCESSING.CRON, processScheduler)
105+
106+
schedule.scheduleJob(config.CRON_CANDIDATE_REVIEW, emailNotificationService.sendCandidatesAvailableEmails)
107+
schedule.scheduleJob(config.CRON_INTERVIEW_COMING_UP, emailNotificationService.sendInterviewComingUpEmails)
108+
schedule.scheduleJob(config.CRON_INTERVIEW_COMPLETED, emailNotificationService.sendInterviewCompletedEmails)
109+
schedule.scheduleJob(config.CRON_POST_INTERVIEW, emailNotificationService.sendPostInterviewActionEmails)
110+
schedule.scheduleJob(config.CRON_UPCOMING_RESOURCE_BOOKING, emailNotificationService.sendResourceBookingExpirationEmails)
104111
})
105112

106113
if (process.env.NODE_ENV === 'test') {

config/default.js

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,5 +226,25 @@ module.exports = {
226226
interview: 'withdrawn',
227227
selected: 'withdrawn',
228228
offered: 'withdrawn'
229-
}
229+
},
230+
// the sender email
231+
NOTIFICATION_SENDER_EMAIL: process.env.NOTIFICATION_SENDER_EMAIL,
232+
// the email notification sendgrid template id
233+
NOTIFICATION_SENDGRID_TEMPLATE_ID: process.env.NOTIFICATION_SENDGRID_TEMPLATE_ID,
234+
// hours after interview completed when we should post the notification
235+
INTERVIEW_COMPLETED_NOTIFICATION_HOURS: process.env.INTERVIEW_COMPLETED_NOTIFICATION_HOURS || 4,
236+
// no of weeks before expiry when we should post the notification
237+
RESOURCE_BOOKING_EXPIRY_NOTIFICATION_WEEKS: process.env.RESOURCE_BOOKING_EXPIRY_NOTIFICATION_WEEKS || 3,
238+
// frequency of cron checking for available candidates for review
239+
CRON_CANDIDATE_REVIEW: process.env.CRON_CANDIDATE_REVIEW || '00 00 13 * * 0-6',
240+
// frequency of cron checking for coming up interviews
241+
// when changing this to frequency other than 5 mins, please change the minutesRange in sendInterviewComingUpEmails correspondingly
242+
CRON_INTERVIEW_COMING_UP: process.env.CRON_INTERVIEW_COMING_UP || '*/5 * * * *',
243+
// frequency of cron checking for interview completed
244+
// when changing this to frequency other than 5 mins, please change the minutesRange in sendInterviewCompletedEmails correspondingly
245+
CRON_INTERVIEW_COMPLETED: process.env.CRON_INTERVIEW_COMPLETED || '*/5 * * * *',
246+
// frequency of cron checking for post interview actions
247+
CRON_POST_INTERVIEW: process.env.CRON_POST_INTERVIEW || '00 00 13 * * 0-6',
248+
// frequency of cron checking for upcoming resource bookings
249+
CRON_UPCOMING_RESOURCE_BOOKING: process.env.CRON_UPCOMING_RESOURCE_BOOKING || '00 00 13 * * 1'
230250
}

config/email_template.config.js

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,5 +100,47 @@ module.exports = {
100100
cc: config.INTERVIEW_INVITATION_CC_LIST,
101101
recipients: config.INTERVIEW_INVITATION_RECIPIENTS_LIST,
102102
sendgridTemplateId: config.INTERVIEW_INVITATION_SENDGRID_TEMPLATE_ID
103+
},
104+
'candidate-review': {
105+
subject: 'Topcoder - {{teamName}} has job candidates available for review',
106+
body: '',
107+
recipients: [],
108+
from: config.NOTIFICATION_SENDER_EMAIL,
109+
sendgrid_template_id: config.NOTIFICATION_SENDGRID_TEMPLATE_ID
110+
},
111+
'interview-coming-up-host': {
112+
subject: 'Topcoder - Interview Coming Up: {{jobTitle}} with {{guestFullName}}',
113+
body: '',
114+
recipients: [],
115+
from: config.NOTIFICATION_SENDER_EMAIL,
116+
sendgrid_template_id: config.NOTIFICATION_SENDGRID_TEMPLATE_ID
117+
},
118+
'interview-coming-up-guest': {
119+
subject: 'Topcoder - Interview Coming Up: {{jobTitle}} with {{hostFullName}}',
120+
body: '',
121+
recipients: [],
122+
from: config.NOTIFICATION_SENDER_EMAIL,
123+
sendgrid_template_id: config.NOTIFICATION_SENDGRID_TEMPLATE_ID
124+
},
125+
'interview-completed': {
126+
subject: 'Topcoder - Interview Awaits Resolution: {{jobTitle}} for {{guestFullName}}',
127+
body: '',
128+
recipients: [],
129+
from: config.NOTIFICATION_SENDER_EMAIL,
130+
sendgrid_template_id: config.NOTIFICATION_SENDGRID_TEMPLATE_ID
131+
},
132+
'post-interview-action': {
133+
subject: 'Topcoder - Candidate Action Required in {{teamName}} for {{numCandidates}} candidates',
134+
body: '',
135+
recipients: [],
136+
from: config.NOTIFICATION_SENDER_EMAIL,
137+
sendgrid_template_id: config.NOTIFICATION_SENDGRID_TEMPLATE_ID
138+
},
139+
'resource-booking-expiration': {
140+
subject: 'Topcoder - Resource Booking Expiring in {{teamName}} for {{numResourceBookings}} resource bookings',
141+
body: '',
142+
recipients: [],
143+
from: config.NOTIFICATION_SENDER_EMAIL,
144+
sendgrid_template_id: config.NOTIFICATION_SENDGRID_TEMPLATE_ID
103145
}
104146
}

data/notifications.json

Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
{
2+
"candidatesAvailableForReview": {
3+
"teamName": "the thing three",
4+
"teamJobs": [
5+
{
6+
"title": "Dummy title - at most 64 characters",
7+
"nResourceBookings": 0,
8+
"jobCandidates": [
9+
{
10+
"handle": "testfordevemail",
11+
"name": "John Doe",
12+
"status": "open"
13+
}
14+
],
15+
"reviewLink": "https://platform.topcoder-dev.com/taas/myteams/111/positions/36dad9f2-98ed-4d3a-9ea7-2cd3d0f8a51a/candidates/to-review"
16+
},
17+
{
18+
"title": "Dummy title - at most 64 characters",
19+
"nResourceBookings": 0,
20+
"jobCandidates": [
21+
{
22+
"handle": "pshah_manager",
23+
"name": "pshah manager",
24+
"status": "open"
25+
},
26+
{
27+
"handle": "pshah_manager",
28+
"name": "pshah manager",
29+
"status": "open"
30+
},
31+
{
32+
"handle": "pshah_manager",
33+
"name": "pshah manager",
34+
"status": "open"
35+
},
36+
{
37+
"handle": "pshah_manager",
38+
"name": "pshah manager",
39+
"status": "open"
40+
},
41+
{
42+
"handle": "pshah_manager",
43+
"name": "pshah manager",
44+
"status": "open"
45+
},
46+
{
47+
"handle": "pshah_manager",
48+
"name": "pshah manager",
49+
"status": "open"
50+
},
51+
{
52+
"handle": "testfordevemail",
53+
"name": "John Doe",
54+
"status": "open"
55+
}
56+
],
57+
"reviewLink": "https://platform.topcoder-dev.com/taas/myteams/111/positions/728ff056-63f6-4730-8a9f-3074acad8479/candidates/to-review"
58+
}
59+
],
60+
"notificationType": {
61+
"candidatesAvailableForReview": true
62+
},
63+
"description": "Candidates are available for review",
64+
"subject": "Topcoder - the thing three has job candidates available for review",
65+
"body": ""
66+
},
67+
"interviewComingUpForHost": {
68+
"jobTitle": "Dummy title - at most 64 characters",
69+
"guestFullName": "name1",
70+
"hostFullName": "host name",
71+
"candidateName": "John Doe",
72+
"handle": "testfordevemail",
73+
"attendees": [
74+
"name1",
75+
"name2",
76+
"name3"
77+
],
78+
"startTime": "Tue, 27 Jul 2021 21:21:17 GMT",
79+
"duration": 120,
80+
"interviewLink": "https://platform.topcoder-dev.com/taas/myteams/111/positions/36dad9f2-98ed-4d3a-9ea7-2cd3d0f8a51a/candidates/interviews",
81+
"notificationType": {
82+
"interviewComingUpForHost": true
83+
},
84+
"description": "Interview Coming Up",
85+
"subject": "Topcoder - Interview Coming Up: Dummy title - at most 64 characters with name1",
86+
"body": ""
87+
},
88+
89+
"interviewComingUpForGuest": {
90+
"jobTitle": "Dummy title - at most 64 characters",
91+
"guestFullName": "name1",
92+
"hostFullName": "host name",
93+
"candidateName": "John Doe",
94+
"handle": "testfordevemail",
95+
"attendees": [
96+
"name1",
97+
"name2",
98+
"name3"
99+
],
100+
"startTime": "Tue, 27 Jul 2021 21:21:17 GMT",
101+
"duration": 120,
102+
"interviewLink": "https://platform.topcoder-dev.com/taas/myteams/111/positions/36dad9f2-98ed-4d3a-9ea7-2cd3d0f8a51a/candidates/interviews",
103+
"notificationType": {
104+
"interviewComingUpForGuest": true
105+
},
106+
"description": "Interview Coming Up",
107+
"subject": "Topcoder - Interview Coming Up: Dummy title - at most 64 characters with host name",
108+
"body": ""
109+
},
110+
111+
"interviewCompleted": {
112+
"jobTitle": "Dummy title - at most 64 characters",
113+
"guestFullName": "name1",
114+
"hostFullName": null,
115+
"candidateName": "John Doe",
116+
"handle": "testfordevemail",
117+
"attendees": [
118+
"name1",
119+
"name2",
120+
"name3"
121+
],
122+
"startTime": "Tue, 27 Jul 2021 21:21:17 GMT",
123+
"duration": 120,
124+
"interviewLink": "https://platform.topcoder-dev.com/taas/myteams/111/positions/36dad9f2-98ed-4d3a-9ea7-2cd3d0f8a51a/candidates/interviews",
125+
"notificationType": {
126+
"interviewCompleted": true
127+
},
128+
"description": "Interview Completed",
129+
"subject": "Topcoder - Interview Awaits Resolution: Dummy title - at most 64 characters for name1",
130+
"body": ""
131+
},
132+
133+
"postInterviewCandidateAction": {
134+
"teamName": "the thing three",
135+
"numCandidates": 3,
136+
"teamInterviews": [
137+
{
138+
"jobTitle": "Dummy title - at most 64 characters",
139+
"guestFullName": "name1",
140+
"hostFullName": "host name",
141+
"candidateName": "John Doe",
142+
"handle": "testfordevemail",
143+
"attendees": [
144+
"name1",
145+
"name2",
146+
"name3"
147+
],
148+
"startTime": "Tue, 27 Jul 2021 21:21:17 GMT",
149+
"duration": 120,
150+
"interviewLink": "https://platform.topcoder-dev.com/taas/myteams/111/positions/36dad9f2-98ed-4d3a-9ea7-2cd3d0f8a51a/candidates/interviews"
151+
},
152+
{
153+
"jobTitle": "Dummy title - at most 64 characters",
154+
"guestFullName": "guest1",
155+
"hostFullName": null,
156+
"candidateName": "pshah manager",
157+
"handle": "pshah_manager",
158+
"attendees": [
159+
"guest1",
160+
"guest2",
161+
"guest3"
162+
],
163+
"startTime": "Tue, 27 Jul 2021 21:21:17 GMT",
164+
"duration": 30,
165+
"interviewLink": "https://platform.topcoder-dev.com/taas/myteams/111/positions/728ff056-63f6-4730-8a9f-3074acad8479/candidates/interviews"
166+
},
167+
{
168+
"jobTitle": "Dummy title - at most 64 characters",
169+
"guestFullName": "g name1",
170+
"hostFullName": null,
171+
"candidateName": "John Doe",
172+
"handle": "testfordevemail",
173+
"attendees": [
174+
"g name1",
175+
"g name2"
176+
],
177+
"startTime": "Tue, 27 Jul 2021 21:21:17 GMT",
178+
"duration": 60,
179+
"interviewLink": "https://platform.topcoder-dev.com/taas/myteams/111/positions/728ff056-63f6-4730-8a9f-3074acad8479/candidates/interviews"
180+
}
181+
],
182+
"notificationType": {
183+
"postInterviewCandidateAction": true
184+
},
185+
"description": "Post Interview Candidate Action Reminder",
186+
"subject": "Topcoder - Candidate Action Required in the thing three for 3 candidates",
187+
"body": ""
188+
},
189+
190+
"upcomingResourceBookingExpiration": {
191+
"teamName": "the thing three",
192+
"numResourceBookings": 6,
193+
"teamResourceBookings": [
194+
{
195+
"jobTitle": "Dummy title - at most 64 characters",
196+
"handle": "testcat",
197+
"endDate": "2021-04-27"
198+
},
199+
{
200+
"jobTitle": "Dummy title - at most 64 characters",
201+
"handle": "sachin-wipro",
202+
"endDate": "2020-10-27"
203+
},
204+
{
205+
"jobTitle": "Dummy title - at most 64 characters",
206+
"handle": "aaaa",
207+
"endDate": "2021-04-27"
208+
},
209+
{
210+
"jobTitle": "Dummy title - at most 64 characters",
211+
"handle": "pshah_manager",
212+
"endDate": "2021-01-27"
213+
},
214+
{
215+
"jobTitle": "Dummy title - at most 64 characters",
216+
"handle": "amy_admin",
217+
"endDate": "2021-06-15"
218+
},
219+
{
220+
"jobTitle": "Dummy title - at most 64 characters",
221+
"handle": "lakshmiaconnmgr",
222+
"endDate": "2021-06-15"
223+
}
224+
],
225+
"notificationType": {
226+
"upcomingResourceBookingExpiration": true
227+
},
228+
"description": "Upcoming Resource Booking Expiration",
229+
"subject": "Topcoder - Resource Booking Expiring in the thing three for 6 resource bookings",
230+
"body": ""
231+
}
232+
}

0 commit comments

Comments
 (0)