Skip to content

Commit 5d02723

Browse files
Merge pull request #439 from topcoder-platform/dev
[PROD] Next Release
2 parents c31d278 + 8e895df commit 5d02723

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+3417
-366
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ build/Release
4040
# Dependency directories
4141
node_modules/
4242
jspm_packages/
43+
scripts/withdrawn-migration/temp/
4344

4445
# Snowpack dependency directory (https://snowpack.dev/)
4546
web_modules/

README.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@
4747
ES_HOST=http://dockerhost:9200
4848
DATABASE_URL=postgres://postgres:postgres@dockerhost:5432/postgres
4949
BUSAPI_URL=http://dockerhost:8002/v5
50+
# stripe
51+
STRIPE_SECRET_KEY=
52+
CURRENCY=usd
5053
```
5154

5255
- Values from this file would be automatically used by many `npm` commands.
@@ -215,6 +218,7 @@ To be able to change and test `taas-es-processor` locally you can follow the nex
215218
| `npm run services:up` | Start services via docker-compose for local development. |
216219
| `npm run services:down` | Stop services via docker-compose for local development. |
217220
| `npm run services:logs -- -f <service_name>` | View logs of some service inside docker-compose. |
221+
| `npm run services:rebuild -- -f <service_name>` | Rebuild service container ignoring cache (useful when pushed something to the Git repository of service) |
218222
| `npm run local:init` | Recreate Database and Elasticsearch indexes and populate demo data for local development (removes any existent data). |
219223
| `npm run local:reset` | Recreate Database and Elasticsearch indexes (removes any existent data). |
220224
| `npm run cov` | Code Coverage Report. |
@@ -337,6 +341,6 @@ When we add, update or delete models and/or endpoints we have to make sure that
337341
- Test, that when we migrate DB from the previous state using `npm run migrate`, we get exactly the same DB schema as if we create DB from scratch using command `npm run init-db force`.
338342
339343
## EMSI mapping
340-
mapping EMSI tags to topcoder skills
341-
Run `npm run emsi-mapping` to create the mapping file
344+
mapping EMSI tags to topcoder skills
345+
Run `npm run emsi-mapping` to create the mapping file
342346
It will take about 15 minutes to create the mapping file `script/emsi-mapping/emsi-skils-mapping.js`

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: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ 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 { sendSurveys } = require('./src/services/SurveyService')
18+
const emailNotificationService = require('./src/services/EmailNotificationService')
1719

1820
// setup express app
1921
const app = express()
@@ -98,9 +100,16 @@ const server = app.listen(app.get('port'), () => {
98100
eventHandlers.init()
99101
// schedule updateCompletedInterviews to run every hour
100102
schedule.scheduleJob('0 0 * * * *', interviewService.updateCompletedInterviews)
101-
103+
// schedule sendSurveys
104+
schedule.scheduleJob(config.WEEKLY_SURVEY.CRON, sendSurveys)
102105
// schedule payment processing
103106
schedule.scheduleJob(config.PAYMENT_PROCESSING.CRON, processScheduler)
107+
108+
schedule.scheduleJob(config.CRON_CANDIDATE_REVIEW, emailNotificationService.sendCandidatesAvailableEmails)
109+
schedule.scheduleJob(config.CRON_INTERVIEW_COMING_UP, emailNotificationService.sendInterviewComingUpEmails)
110+
schedule.scheduleJob(config.CRON_INTERVIEW_COMPLETED, emailNotificationService.sendInterviewCompletedEmails)
111+
schedule.scheduleJob(config.CRON_POST_INTERVIEW, emailNotificationService.sendPostInterviewActionEmails)
112+
schedule.scheduleJob(config.CRON_UPCOMING_RESOURCE_BOOKING, emailNotificationService.sendResourceBookingExpirationEmails)
104113
})
105114

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

config/default.js

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,8 @@ module.exports = {
147147

148148
// the Kafka message topic for sending email
149149
EMAIL_TOPIC: process.env.EMAIL_TOPIC || 'external.action.email',
150+
// the Kafka message topic for creating notifications
151+
NOTIFICATIONS_CREATE_TOPIC: process.env.NOTIFICATIONS_CREATE_TOPIC || 'notifications.action.create',
150152
// the emails address for receiving the issue report
151153
// REPORT_ISSUE_EMAILS may contain comma-separated list of email which is converted to array
152154
REPORT_ISSUE_EMAILS: (process.env.REPORT_ISSUE_EMAILS || '').split(','),
@@ -180,6 +182,17 @@ module.exports = {
180182
INTERNAL_MEMBER_GROUPS: process.env.INTERNAL_MEMBER_GROUPS || ['20000000', '20000001', '20000003', '20000010', '20000015'],
181183
// Topcoder skills cache time in minutes
182184
TOPCODER_SKILLS_CACHE_TIME: process.env.TOPCODER_SKILLS_CACHE_TIME || 60,
185+
// weekly survey scheduler config
186+
WEEKLY_SURVEY: {
187+
CRON: process.env.WEEKLY_SURVEY_CRON || '0 1 * * 7',
188+
BASE_URL: process.env.WEEKLY_SURVEY_BASE_URL || 'https://api.surveymonkey.net/v3/surveys',
189+
JWT_TOKEN: process.env.WEEKLY_SURVEY_JWT_TOKEN || '',
190+
SURVEY_ID: process.env.WEEKLY_SURVEY_SURVEY_ID || '',
191+
SURVEY_COLLECTOR_PREFIX: process.env.WEEKLY_SURVEY_SURVEY_COLLECTOR_PREFIX || 'Week ending',
192+
SURVEY_MASTER_COLLECTOR_ID: process.env.WEEKLY_SURVEY_SURVEY_MASTER_COLLECTOR_ID || '',
193+
SURVEY_MASTER_MESSAGE_ID: process.env.WEEKLY_SURVEY_SURVEY_MASTER_MESSAGE_ID || '',
194+
SURVEY_CONTACT_GROUP_ID: process.env.WEEKLY_SURVEY_SURVEY_CONTACT_GROUP_ID || ''
195+
},
183196
// payment scheduler config
184197
PAYMENT_PROCESSING: {
185198
// switch off actual API calls in Payment Scheduler
@@ -226,5 +239,31 @@ module.exports = {
226239
interview: 'withdrawn',
227240
selected: 'withdrawn',
228241
offered: 'withdrawn'
229-
}
242+
},
243+
// the sender email
244+
NOTIFICATION_SENDER_EMAIL: process.env.NOTIFICATION_SENDER_EMAIL,
245+
// the email notification sendgrid template id
246+
NOTIFICATION_SENDGRID_TEMPLATE_ID: process.env.NOTIFICATION_SENDGRID_TEMPLATE_ID,
247+
// frequency of cron checking for available candidates for review
248+
CRON_CANDIDATE_REVIEW: process.env.CRON_CANDIDATE_REVIEW || '00 00 13 * * 0-6',
249+
// frequency of cron checking for coming up interviews
250+
// when changing this to frequency other than 5 mins, please change the minutesRange in sendInterviewComingUpEmails correspondingly
251+
CRON_INTERVIEW_COMING_UP: process.env.CRON_INTERVIEW_COMING_UP || '*/5 * * * *',
252+
// frequency of cron checking for interview completed
253+
// when changing this to frequency other than 5 mins, please change the minutesRange in sendInterviewCompletedEmails correspondingly
254+
CRON_INTERVIEW_COMPLETED: process.env.CRON_INTERVIEW_COMPLETED || '*/5 * * * *',
255+
// frequency of cron checking for post interview actions
256+
CRON_POST_INTERVIEW: process.env.CRON_POST_INTERVIEW || '00 00 13 * * 0-6',
257+
// frequency of cron checking for upcoming resource bookings
258+
CRON_UPCOMING_RESOURCE_BOOKING: process.env.CRON_UPCOMING_RESOURCE_BOOKING || '00 00 13 * * 1',
259+
// The match window for fetching interviews which are coming up
260+
INTERVIEW_COMING_UP_MATCH_WINDOW: process.env.INTERVIEW_COMING_UP_MATCH_WINDOW || 'PT5M',
261+
// The remind time for fetching interviews which are coming up
262+
INTERVIEW_COMING_UP_REMIND_TIME: (process.env.INTERVIEW_COMING_UP_REMIND_TIME || 'PT1H,PT24H').split(','),
263+
// The match window for fetching completed interviews
264+
INTERVIEW_COMPLETED_MATCH_WINDOW: process.env.INTERVIEW_COMPLETED_MATCH_WINDOW || 'PT5M',
265+
// The interview completed past time for fetching interviews
266+
INTERVIEW_COMPLETED_PAST_TIME: process.env.INTERVIEW_COMPLETED_PAST_TIME || 'PT4H',
267+
// The time before resource booking expiry when we should start sending notifications
268+
RESOURCE_BOOKING_EXPIRY_TIME: process.env.RESOURCE_BOOKING_EXPIRY_TIME || 'P21D'
230269
}

config/email_template.config.js

Lines changed: 141 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -6,99 +6,152 @@
66
const config = require('config')
77

88
module.exports = {
9-
/* Report a general issue for a team.
10-
*
11-
* - projectId: the project ID. Example: 123412
12-
* - projectName: the project name. Example: "TaaS API Misc Updates"
13-
* - reportText: the body of reported issue. Example: "I have issue with ... \n ... Thank you in advance!"
9+
/**
10+
* List all the kind of emails which could be sent by the endpoint `POST /taas-teams/email` inside `teamTemplates`.
1411
*/
15-
'team-issue-report': {
16-
subject: 'Issue Reported on TaaS Team {{projectName}} ({{projectId}}).',
17-
body: 'Project Name: {{projectName}}' + '\n' +
18-
'Project ID: {{projectId}}' + '\n' +
19-
`Project URL: ${config.TAAS_APP_URL}/{{projectId}}` + '\n' +
20-
'\n' +
21-
'{{reportText}}',
22-
recipients: config.REPORT_ISSUE_EMAILS,
23-
sendgridTemplateId: config.REPORT_ISSUE_SENDGRID_TEMPLATE_ID
24-
},
12+
teamTemplates: {
13+
/* Report a general issue for a team.
14+
*
15+
* - projectId: the project ID. Example: 123412
16+
* - projectName: the project name. Example: "TaaS API Misc Updates"
17+
* - reportText: the body of reported issue. Example: "I have issue with ... \n ... Thank you in advance!"
18+
*/
19+
'team-issue-report': {
20+
subject: 'Issue Reported on TaaS Team {{projectName}} ({{projectId}}).',
21+
body: 'Project Name: {{projectName}}' + '\n' +
22+
'Project ID: {{projectId}}' + '\n' +
23+
`Project URL: ${config.TAAS_APP_URL}/{{projectId}}` + '\n' +
24+
'\n' +
25+
'{{reportText}}',
26+
recipients: config.REPORT_ISSUE_EMAILS,
27+
sendgridTemplateId: config.REPORT_ISSUE_SENDGRID_TEMPLATE_ID
28+
},
2529

26-
/* Report issue for a particular member
27-
*
28-
* - userHandle: the user handle. Example: "bili_2021"
29-
* - projectId: the project ID. Example: 123412
30-
* - projectName: the project name. Example: "TaaS API Misc Updates"
31-
* - reportText: the body of reported issue. Example: "I have issue with ... \n ... Thank you in advance!"
32-
*/
33-
'member-issue-report': {
34-
subject: 'Issue Reported for member {{userHandle}} on TaaS Team {{projectName}} ({{projectId}}).',
35-
body: 'User Handle: {{userHandle}}' + '\n' +
36-
'Project Name: {{projectName}}' + '\n' +
37-
'Project ID: {{projectId}}' + '\n' +
38-
`Project URL: ${config.TAAS_APP_URL}/{{projectId}}` + '\n' +
39-
'\n' +
40-
'{{reportText}}',
41-
recipients: config.REPORT_ISSUE_EMAILS,
42-
sendgridTemplateId: config.REPORT_ISSUE_SENDGRID_TEMPLATE_ID
43-
},
30+
/* Report issue for a particular member
31+
*
32+
* - userHandle: the user handle. Example: "bili_2021"
33+
* - projectId: the project ID. Example: 123412
34+
* - projectName: the project name. Example: "TaaS API Misc Updates"
35+
* - reportText: the body of reported issue. Example: "I have issue with ... \n ... Thank you in advance!"
36+
*/
37+
'member-issue-report': {
38+
subject: 'Issue Reported for member {{userHandle}} on TaaS Team {{projectName}} ({{projectId}}).',
39+
body: 'User Handle: {{userHandle}}' + '\n' +
40+
'Project Name: {{projectName}}' + '\n' +
41+
'Project ID: {{projectId}}' + '\n' +
42+
`Project URL: ${config.TAAS_APP_URL}/{{projectId}}` + '\n' +
43+
'\n' +
44+
'{{reportText}}',
45+
recipients: config.REPORT_ISSUE_EMAILS,
46+
sendgridTemplateId: config.REPORT_ISSUE_SENDGRID_TEMPLATE_ID
47+
},
4448

45-
/* Request extension for a particular member
46-
*
47-
* - userHandle: the user handle. Example: "bili_2021"
48-
* - projectId: the project ID. Example: 123412
49-
* - projectName: the project name. Example: "TaaS API Misc Updates"
50-
* - text: comment for the request. Example: "I would like to keep working with this member for 2 months..."
51-
*/
52-
'extension-request': {
53-
subject: 'Extension Requested for member {{userHandle}} on TaaS Team {{projectName}} ({{projectId}}).',
54-
body: 'User Handle: {{userHandle}}' + '\n' +
55-
'Project Name: {{projectName}}' + '\n' +
56-
'Project ID: {{projectId}}' + '\n' +
57-
`Project URL: ${config.TAAS_APP_URL}/{{projectId}}` + '\n' +
58-
'\n' +
59-
'{{text}}',
60-
recipients: config.REPORT_ISSUE_EMAILS,
61-
sendgridTemplateId: config.REQUEST_EXTENSION_SENDGRID_TEMPLATE_ID
49+
/* Request extension for a particular member
50+
*
51+
* - userHandle: the user handle. Example: "bili_2021"
52+
* - projectId: the project ID. Example: 123412
53+
* - projectName: the project name. Example: "TaaS API Misc Updates"
54+
* - text: comment for the request. Example: "I would like to keep working with this member for 2 months..."
55+
*/
56+
'extension-request': {
57+
subject: 'Extension Requested for member {{userHandle}} on TaaS Team {{projectName}} ({{projectId}}).',
58+
body: 'User Handle: {{userHandle}}' + '\n' +
59+
'Project Name: {{projectName}}' + '\n' +
60+
'Project ID: {{projectId}}' + '\n' +
61+
`Project URL: ${config.TAAS_APP_URL}/{{projectId}}` + '\n' +
62+
'\n' +
63+
'{{text}}',
64+
recipients: config.REPORT_ISSUE_EMAILS,
65+
sendgridTemplateId: config.REQUEST_EXTENSION_SENDGRID_TEMPLATE_ID
66+
},
67+
68+
/* Request interview for a job candidate
69+
*
70+
* - interviewType: the x.ai interview type. Example: "interview-30"
71+
* - interviewRound: the round of the interview. Example: 2
72+
* - interviewDuration: duration of the interview, in minutes. Example: 30
73+
* - interviewerList: The list of interviewer email addresses. Example: "first@attendee.com, second@attendee.com"
74+
* - candidateId: the id of the jobCandidate. Example: "cc562545-7b75-48bf-87e7-50b3c57e41b1"
75+
* - candidateName: Full name of candidate. Example: "John Doe"
76+
* - jobName: The title of the job. Example: "TaaS API Misc Updates"
77+
*
78+
* Template (defined in SendGrid):
79+
* Subject: '{{interviewType}} tech interview with {{candidateName}} for {{jobName}} is requested by the Customer'
80+
* Body:
81+
* 'Hello!
82+
* <br /><br />
83+
* Congratulations, you have been selected to participate in a Topcoder Gig Work Interview!
84+
* <br /><br />
85+
* Please monitor your email for a response to this where you can coordinate your availability.
86+
* <br /><br />
87+
* Interviewee: {{candidateName}}<br />
88+
* Interviewer(s): {{interviewerList}}<br />
89+
* Interview Length: {{interviewDuration}} minutes
90+
* <br /><br />
91+
* /{{interviewType}}
92+
* <br /><br />
93+
* Topcoder Info:<br />
94+
* Note: "id: {{candidateId}}, round: {{interviewRound}}"'
95+
*
96+
* Note, that the template should be defined in SendGrid.
97+
* The subject & body above (identical to actual SendGrid template) is for reference purposes.
98+
* We won't pass subject & body but only substitutions (replacements in template subject/body).
99+
*/
100+
'interview-invitation': {
101+
subject: '',
102+
body: '',
103+
from: config.INTERVIEW_INVITATION_SENDER_EMAIL,
104+
cc: config.INTERVIEW_INVITATION_CC_LIST,
105+
recipients: config.INTERVIEW_INVITATION_RECIPIENTS_LIST,
106+
sendgridTemplateId: config.INTERVIEW_INVITATION_SENDGRID_TEMPLATE_ID
107+
}
62108
},
63109

64-
/* Request interview for a job candidate
65-
*
66-
* - interviewType: the x.ai interview type. Example: "interview-30"
67-
* - interviewRound: the round of the interview. Example: 2
68-
* - interviewDuration: duration of the interview, in minutes. Example: 30
69-
* - interviewerList: The list of interviewer email addresses. Example: "first@attendee.com, second@attendee.com"
70-
* - candidateId: the id of the jobCandidate. Example: "cc562545-7b75-48bf-87e7-50b3c57e41b1"
71-
* - candidateName: Full name of candidate. Example: "John Doe"
72-
* - jobName: The title of the job. Example: "TaaS API Misc Updates"
73-
*
74-
* Template (defined in SendGrid):
75-
* Subject: '{{interviewType}} tech interview with {{candidateName}} for {{jobName}} is requested by the Customer'
76-
* Body:
77-
* 'Hello!
78-
* <br /><br />
79-
* Congratulations, you have been selected to participate in a Topcoder Gig Work Interview!
80-
* <br /><br />
81-
* Please monitor your email for a response to this where you can coordinate your availability.
82-
* <br /><br />
83-
* Interviewee: {{candidateName}}<br />
84-
* Interviewer(s): {{interviewerList}}<br />
85-
* Interview Length: {{interviewDuration}} minutes
86-
* <br /><br />
87-
* /{{interviewType}}
88-
* <br /><br />
89-
* Topcoder Info:<br />
90-
* Note: "id: {{candidateId}}, round: {{interviewRound}}"'
91-
*
92-
* Note, that the template should be defined in SendGrid.
93-
* The subject & body above (identical to actual SendGrid template) is for reference purposes.
94-
* We won't pass subject & body but only substitutions (replacements in template subject/body).
110+
/**
111+
* List all kind of emails which could be send as Email Notifications by scheduler, API endpoints or anything else.
95112
*/
96-
'interview-invitation': {
97-
subject: '',
98-
body: '',
99-
from: config.INTERVIEW_INVITATION_SENDER_EMAIL,
100-
cc: config.INTERVIEW_INVITATION_CC_LIST,
101-
recipients: config.INTERVIEW_INVITATION_RECIPIENTS_LIST,
102-
sendgridTemplateId: config.INTERVIEW_INVITATION_SENDGRID_TEMPLATE_ID
113+
notificationEmailTemplates: {
114+
'taas.notification.candidates-available-for-review': {
115+
subject: 'Topcoder - {{teamName}} has job candidates available for review',
116+
body: '',
117+
recipients: [],
118+
from: config.NOTIFICATION_SENDER_EMAIL,
119+
sendgridTemplateId: config.NOTIFICATION_SENDGRID_TEMPLATE_ID
120+
},
121+
'taas.notification.interview-coming-up-host': {
122+
subject: 'Topcoder - Interview Coming Up: {{jobTitle}} with {{guestFullName}}',
123+
body: '',
124+
recipients: [],
125+
from: config.NOTIFICATION_SENDER_EMAIL,
126+
sendgridTemplateId: config.NOTIFICATION_SENDGRID_TEMPLATE_ID
127+
},
128+
'taas.notification.interview-coming-up-guest': {
129+
subject: 'Topcoder - Interview Coming Up: {{jobTitle}} with {{hostFullName}}',
130+
body: '',
131+
recipients: [],
132+
from: config.NOTIFICATION_SENDER_EMAIL,
133+
sendgridTemplateId: config.NOTIFICATION_SENDGRID_TEMPLATE_ID
134+
},
135+
'taas.notification.interview-awaits-resolution': {
136+
subject: 'Topcoder - Interview Awaits Resolution: {{jobTitle}} for {{guestFullName}}',
137+
body: '',
138+
recipients: [],
139+
from: config.NOTIFICATION_SENDER_EMAIL,
140+
sendgridTemplateId: config.NOTIFICATION_SENDGRID_TEMPLATE_ID
141+
},
142+
'taas.notification.post-interview-action-required': {
143+
subject: 'Topcoder - Candidate Action Required in {{teamName}} for {{numCandidates}} candidates',
144+
body: '',
145+
recipients: [],
146+
from: config.NOTIFICATION_SENDER_EMAIL,
147+
sendgridTemplateId: config.NOTIFICATION_SENDGRID_TEMPLATE_ID
148+
},
149+
'taas.notification.resource-booking-expiration': {
150+
subject: 'Topcoder - Resource Booking Expiring in {{teamName}} for {{numResourceBookings}} resource bookings',
151+
body: '',
152+
recipients: [],
153+
from: config.NOTIFICATION_SENDER_EMAIL,
154+
sendgridTemplateId: config.NOTIFICATION_SENDGRID_TEMPLATE_ID
155+
}
103156
}
104157
}

0 commit comments

Comments
 (0)