Skip to content

Commit 1431927

Browse files
authored
Merge branch 'dev' into change-validatations-in-job-j
2 parents 628db9c + 22a63bc commit 1431927

13 files changed

+121
-206
lines changed

.nvmrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
12

README.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,13 @@ The following parameters can be set in config files or in env variables:
9090

9191
## Local deployment
9292

93-
1. Make sure that Kafka and Elasticsearch is running as per instructions above.
93+
0. Make sure that Kafka and Elasticsearch is running as per instructions above.
94+
95+
1. Make sure to use Node v12+ by command `node -v`. We recommend using [NVM](https://github.com/nvm-sh/nvm) to quickly switch to the right version:
96+
97+
```bash
98+
nvm use
99+
```
94100

95101
2. From the project root directory, run the following command to install the dependencies
96102

config/test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,5 @@ module.exports = {
88
ZAPIER_JOB_CANDIDATE_SWITCH: process.env.ZAPIER_JOB_CANDIDATE_SWITCH || 'ON'
99
},
1010
// don't retry actions during tests because tests for now don't expect it and should be updated first
11-
MAX_RETRY: 0,
11+
MAX_RETRY: 0
1212
}

src/bootstrap.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,16 @@ const allowedInterviewStatuses = _.values(Interview.Status)
99

1010
global.Promise = require('bluebird')
1111

12-
Joi.rateType = () => Joi.string().valid('hourly', 'daily', 'weekly', 'monthly','annual')
12+
Joi.rateType = () => Joi.string().valid('hourly', 'daily', 'weekly', 'monthly', 'annual')
1313
Joi.jobStatus = () => Joi.string().valid('sourcing', 'in-review', 'assigned', 'closed', 'cancelled')
1414
Joi.resourceBookingStatus = () => Joi.string().valid('placed', 'closed', 'cancelled')
1515
Joi.jobCandidateStatus = () => Joi.string().valid('open', 'placed', 'selected', 'client rejected - screening', 'client rejected - interview', 'rejected - other', 'cancelled', 'interview', 'topcoder-rejected', 'applied', 'rejected-pre-screen', 'skills-test', 'skills-test', 'phone-screen', 'job-closed', 'offered')
1616
Joi.workload = () => Joi.string().valid('full-time', 'fractional')
1717
Joi.title = () => Joi.string().max(128)
18-
Joi.paymentStatus = () => Joi.string().valid('pending', 'partially-completed', 'completed', 'cancelled')
18+
Joi.paymentStatus = () => Joi.string().valid('pending', 'in-progress', 'partially-completed', 'completed', 'failed', 'no-days')
1919
Joi.xaiTemplate = () => Joi.string().valid(...allowedXAITemplates)
2020
Joi.interviewStatus = () => Joi.string().valid(...allowedInterviewStatuses)
21-
Joi.workPeriodPaymentStatus = () => Joi.string().valid('completed', 'scheduled', 'cancelled')
21+
Joi.workPeriodPaymentStatus = () => Joi.string().valid('completed', 'scheduled', 'in-progress', 'failed', 'cancelled')
2222
// Empty string is not allowed by Joi by default and must be enabled with allow('').
2323
// See https://joi.dev/api/?v=17.3.0#string fro details why it's like this.
2424
// In many cases we would like to allow empty string to make it easier to create UI for editing data.

src/scripts/createIndex.js

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,17 +119,30 @@ async function createIndex () {
119119
startDate: { type: 'date', format: 'yyyy-MM-dd' },
120120
endDate: { type: 'date', format: 'yyyy-MM-dd' },
121121
daysWorked: { type: 'integer' },
122-
memberRate: { type: 'float' },
123-
customerRate: { type: 'float' },
122+
daysPaid: { type: 'integer' },
123+
paymentTotal: { type: 'float' },
124124
paymentStatus: { type: 'keyword' },
125125
payments: {
126126
type: 'nested',
127127
properties: {
128128
id: { type: 'keyword' },
129129
workPeriodId: { type: 'keyword' },
130130
challengeId: { type: 'keyword' },
131+
memberRate: { type: 'float' },
132+
customerRate: { type: 'float' },
133+
days: { type: 'integer' },
131134
amount: { type: 'float' },
132135
status: { type: 'keyword' },
136+
statusDetails: {
137+
type: 'nested',
138+
properties: {
139+
errorMessage: { type: 'text' },
140+
errorCode: { type: 'integer' },
141+
retry: { type: 'integer' },
142+
step: { type: 'keyword' },
143+
challengeId: { type: 'keyword' }
144+
}
145+
},
133146
billingAccountId: { type: 'integer' },
134147
createdAt: { type: 'date' },
135148
createdBy: { type: 'keyword' },

src/services/InterviewProcessorService.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ processRequestInterview.schema = {
5656
originator: Joi.string().required(),
5757
timestamp: Joi.date().required(),
5858
'mime-type': Joi.string().required(),
59+
key: Joi.string().allow(null),
5960
payload: Joi.object().keys({
6061
id: Joi.string().uuid().required(),
6162
xaiId: Joi.string().allow(null),
@@ -176,6 +177,7 @@ processBulkUpdateInterviews.schema = {
176177
originator: Joi.string().required(),
177178
timestamp: Joi.date().required(),
178179
'mime-type': Joi.string().required(),
180+
key: Joi.string().allow(null),
179181
payload: Joi.object().pattern(
180182
Joi.string().uuid(), // key - jobCandidateId
181183
Joi.object().pattern(

src/services/JobCandidateProcessorService.js

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,8 @@ processCreate.schema = {
9191
topic: Joi.string().required(),
9292
originator: Joi.string().required(),
9393
timestamp: Joi.date().required(),
94-
"mime-type": Joi.string().required(),
94+
'mime-type': Joi.string().required(),
95+
key: Joi.string().allow(null),
9596
payload: Joi.object()
9697
.keys({
9798
id: Joi.string().uuid().required(),
@@ -106,11 +107,11 @@ processCreate.schema = {
106107
resume: Joi.string().uri().allow(null).allow(""),
107108
remark: Joi.stringAllowEmpty().allow(null),
108109
})
109-
.required(),
110+
.required()
110111
})
111112
.required(),
112-
transactionId: Joi.string().required(),
113-
};
113+
transactionId: Joi.string().required()
114+
}
114115

115116
/**
116117
* Process update entity message
@@ -164,6 +165,7 @@ processDelete.schema = {
164165
originator: Joi.string().required(),
165166
timestamp: Joi.date().required(),
166167
'mime-type': Joi.string().required(),
168+
key: Joi.string().allow(null),
167169
payload: Joi.object().keys({
168170
id: Joi.string().uuid().required()
169171
}).required()

src/services/JobProcessorService.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ processDelete.schema = {
148148
originator: Joi.string().required(),
149149
timestamp: Joi.date().required(),
150150
'mime-type': Joi.string().required(),
151+
key: Joi.string().allow(null),
151152
payload: Joi.object().keys({
152153
id: Joi.string().uuid().required()
153154
}).required()

src/services/ResourceBookingProcessorService.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ processCreate.schema = {
3232
originator: Joi.string().required(),
3333
timestamp: Joi.date().required(),
3434
'mime-type': Joi.string().required(),
35+
key: Joi.string().allow(null),
3536
payload: Joi.object().keys({
3637
id: Joi.string().uuid().required(),
3738
projectId: Joi.number().integer().required(),
@@ -94,6 +95,7 @@ processDelete.schema = {
9495
originator: Joi.string().required(),
9596
timestamp: Joi.date().required(),
9697
'mime-type': Joi.string().required(),
98+
key: Joi.string().allow(null),
9799
payload: Joi.object().keys({
98100
id: Joi.string().uuid().required()
99101
}).required()

src/services/RoleProcessorService.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ processCreate.schema = {
3232
originator: Joi.string().required(),
3333
timestamp: Joi.date().required(),
3434
'mime-type': Joi.string().required(),
35+
key: Joi.string().allow(null),
3536
payload: Joi.object().keys({
3637
id: Joi.string().uuid().required(),
3738
name: Joi.string().max(50).required(),
@@ -103,6 +104,7 @@ processDelete.schema = {
103104
originator: Joi.string().required(),
104105
timestamp: Joi.date().required(),
105106
'mime-type': Joi.string().required(),
107+
key: Joi.string().allow(null),
106108
payload: Joi.object().keys({
107109
id: Joi.string().uuid().required()
108110
}).required()

src/services/WorkPeriodPaymentProcessorService.js

Lines changed: 40 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -4,53 +4,46 @@
44

55
const Joi = require('@hapi/joi')
66
const config = require('config')
7-
const _ = require('lodash')
87
const logger = require('../common/logger')
98
const helper = require('../common/helper')
109
const constants = require('../common/constants')
1110

1211
const esClient = helper.getESClient()
1312

1413
/**
15-
* Process create entity message
16-
* @param {Object} message the kafka message
17-
* @param {String} transactionId
18-
*/
14+
* Process create entity message
15+
* @param {Object} message the kafka message
16+
* @param {String} transactionId
17+
*/
1918
async function processCreate (message, transactionId) {
20-
const data = message.payload
19+
const workPeriodPayment = message.payload
2120
// find related resourceBooking
22-
const result = await esClient.search({
21+
const resourceBooking = await esClient.search({
2322
index: config.get('esConfig.ES_INDEX_RESOURCE_BOOKING'),
2423
body: {
2524
query: {
2625
nested: {
2726
path: 'workPeriods',
2827
query: {
29-
match: { 'workPeriods.id': data.workPeriodId }
28+
match: { 'workPeriods.id': workPeriodPayment.workPeriodId }
3029
}
3130
}
3231
}
3332
}
3433
})
35-
if (!result.body.hits.total.value) {
36-
throw new Error(`id: ${data.workPeriodId} "WorkPeriod" not found`)
34+
if (!resourceBooking.body.hits.total.value) {
35+
throw new Error(`id: ${workPeriodPayment.workPeriodId} "WorkPeriod" not found`)
3736
}
38-
const resourceBooking = result.body.hits.hits[0]._source
39-
// find related workPeriod record
40-
const workPeriod = _.find(resourceBooking.workPeriods, ['id', data.workPeriodId])
41-
// Get workPeriod's existing payments
42-
const payments = _.isArray(workPeriod.payments) ? workPeriod.payments : []
43-
// Append new payment
44-
payments.push(data)
45-
// Assign new payments array to workPeriod
46-
workPeriod.payments = payments
47-
// Update ResourceBooking's workPeriods property
48-
await esClient.updateExtra({
37+
await esClient.update({
4938
index: config.get('esConfig.ES_INDEX_RESOURCE_BOOKING'),
50-
id: resourceBooking.id,
39+
id: resourceBooking.body.hits.hits[0]._id,
5140
transactionId,
5241
body: {
53-
doc: { workPeriods: resourceBooking.workPeriods }
42+
script: {
43+
lang: 'painless',
44+
source: 'def wp = ctx._source.workPeriods.find(workPeriod -> workPeriod.id == params.workPeriodPayment.workPeriodId); if(!wp.containsKey("payments") || wp.payments == null){wp["payments"]=[]}wp.payments.add(params.workPeriodPayment)',
45+
params: { workPeriodPayment }
46+
}
5447
},
5548
refresh: constants.esRefreshOption
5649
})
@@ -62,13 +55,24 @@ processCreate.schema = {
6255
originator: Joi.string().required(),
6356
timestamp: Joi.date().required(),
6457
'mime-type': Joi.string().required(),
58+
key: Joi.string().allow(null),
6559
payload: Joi.object().keys({
6660
id: Joi.string().uuid().required(),
6761
workPeriodId: Joi.string().uuid().required(),
6862
challengeId: Joi.string().uuid().allow(null),
63+
memberRate: Joi.number().required(),
64+
customerRate: Joi.number().allow(null),
65+
days: Joi.number().integer().min(1).max(5).required(),
6966
amount: Joi.number().greater(0).allow(null),
7067
status: Joi.workPeriodPaymentStatus().required(),
7168
billingAccountId: Joi.number().allow(null),
69+
statusDetails: Joi.object().keys({
70+
errorMessage: Joi.string().required(),
71+
errorCode: Joi.number().integer().allow(null),
72+
retry: Joi.number().integer().allow(null),
73+
step: Joi.string().allow(null),
74+
challengeId: Joi.string().uuid().allow(null)
75+
}).unknown(true).allow(null),
7276
createdAt: Joi.date().required(),
7377
createdBy: Joi.string().uuid().required(),
7478
updatedAt: Joi.date().allow(null),
@@ -79,14 +83,14 @@ processCreate.schema = {
7983
}
8084

8185
/**
82-
* Process update entity message
83-
* @param {Object} message the kafka message
84-
* @param {String} transactionId
85-
*/
86+
* Process update entity message
87+
* @param {Object} message the kafka message
88+
* @param {String} transactionId
89+
*/
8690
async function processUpdate (message, transactionId) {
8791
const data = message.payload
8892
// find workPeriodPayment in it's parent ResourceBooking
89-
let result = await esClient.search({
93+
const resourceBooking = await esClient.search({
9094
index: config.get('esConfig.ES_INDEX_RESOURCE_BOOKING'),
9195
body: {
9296
query: {
@@ -99,89 +103,19 @@ async function processUpdate (message, transactionId) {
99103
}
100104
}
101105
})
102-
if (!result.body.hits.total.value) {
106+
if (!resourceBooking.body.hits.total.value) {
103107
throw new Error(`id: ${data.id} "WorkPeriodPayment" not found`)
104108
}
105-
const resourceBooking = _.cloneDeep(result.body.hits.hits[0]._source)
106-
let workPeriod = null
107-
let payment = null
108-
let paymentIndex = null
109-
// find workPeriod and workPeriodPayment records
110-
_.forEach(resourceBooking.workPeriods, wp => {
111-
_.forEach(wp.payments, (p, pi) => {
112-
if (p.id === data.id) {
113-
payment = p
114-
paymentIndex = pi
115-
return false
116-
}
117-
})
118-
if (payment) {
119-
workPeriod = wp
120-
return false
121-
}
122-
})
123-
let payments
124-
// if WorkPeriodPayment's workPeriodId changed then it must be deleted from the old WorkPeriod
125-
// and added to the new WorkPeriod
126-
if (payment.workPeriodId !== data.workPeriodId) {
127-
// remove payment from payments
128-
payments = _.filter(workPeriod.payments, p => p.id !== data.id)
129-
// assign payments to workPeriod record
130-
workPeriod.payments = payments
131-
// Update old ResourceBooking's workPeriods property
132-
await esClient.updateExtra({
133-
index: config.get('esConfig.ES_INDEX_RESOURCE_BOOKING'),
134-
id: resourceBooking.id,
135-
transactionId,
136-
body: {
137-
doc: { workPeriods: resourceBooking.workPeriods }
138-
},
139-
refresh: constants.esRefreshOption
140-
})
141-
// find workPeriodPayment's new parent WorkPeriod
142-
result = await esClient.search({
143-
index: config.get('esConfig.ES_INDEX_RESOURCE_BOOKING'),
144-
body: {
145-
query: {
146-
nested: {
147-
path: 'workPeriods',
148-
query: {
149-
match: { 'workPeriods.id': data.workPeriodId }
150-
}
151-
}
152-
}
153-
}
154-
})
155-
const newResourceBooking = result.body.hits.hits[0]._source
156-
// find WorkPeriod record in ResourceBooking
157-
const newWorkPeriod = _.find(newResourceBooking.workPeriods, ['id', data.workPeriodId])
158-
// Get WorkPeriod's existing payments
159-
const newPayments = _.isArray(newWorkPeriod.payments) ? newWorkPeriod.payments : []
160-
// Append new payment
161-
newPayments.push(data)
162-
// Assign new payments array to workPeriod
163-
newWorkPeriod.payments = newPayments
164-
// Update new ResourceBooking's workPeriods property
165-
await esClient.updateExtra({
166-
index: config.get('esConfig.ES_INDEX_RESOURCE_BOOKING'),
167-
id: newResourceBooking.id,
168-
transactionId,
169-
body: {
170-
doc: { workPeriods: newResourceBooking.workPeriods }
171-
},
172-
refresh: constants.esRefreshOption
173-
})
174-
return
175-
}
176-
// update payment record
177-
workPeriod.payments[paymentIndex] = data
178-
// Update ResourceBooking's workPeriods property
179-
await esClient.updateExtra({
109+
await esClient.update({
180110
index: config.get('esConfig.ES_INDEX_RESOURCE_BOOKING'),
181-
id: resourceBooking.id,
111+
id: resourceBooking.body.hits.hits[0]._id,
182112
transactionId,
183113
body: {
184-
doc: { workPeriods: resourceBooking.workPeriods }
114+
script: {
115+
lang: 'painless',
116+
source: 'def wp = ctx._source.workPeriods.find(workPeriod -> workPeriod.id == params.data.workPeriodId); wp.payments.removeIf(payment -> payment.id == params.data.id); wp.payments.add(params.data)',
117+
params: { data }
118+
}
185119
},
186120
refresh: constants.esRefreshOption
187121
})

0 commit comments

Comments
 (0)