Skip to content

Commit 66d3a76

Browse files
authored
Merge pull request #120 from imcaizheng/recruit-crm-job-import
Create a script to import Jobs data from Recruit CRM
2 parents d359adf + cc6c548 commit 66d3a76

File tree

12 files changed

+555
-2
lines changed

12 files changed

+555
-2
lines changed

docs/swagger.yaml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1002,6 +1002,32 @@ paths:
10021002
type: string
10031003
enum: ['hourly', 'daily', 'weekly', 'monthly']
10041004
description: The rate type.
1005+
- in: query
1006+
name: jobId
1007+
required: false
1008+
schema:
1009+
type: string
1010+
format: uuid
1011+
description: The job id.
1012+
- in: query
1013+
name: userId
1014+
required: false
1015+
schema:
1016+
type: string
1017+
format: uuid
1018+
description: The job id.
1019+
- in: query
1020+
name: projectId
1021+
required: false
1022+
schema:
1023+
type: integer
1024+
description: The project id.
1025+
- in: query
1026+
name: projectIds
1027+
required: false
1028+
schema:
1029+
type: string
1030+
description: comma separated project ids.
10051031

10061032
responses:
10071033
'200':

package-lock.json

Lines changed: 18 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,9 @@
5050
"winston": "^3.3.3"
5151
},
5252
"devDependencies": {
53+
"@joi/date": "^2.0.1",
5354
"chai": "^4.2.0",
55+
"csv-parser": "^3.0.0",
5456
"mocha": "^8.1.3",
5557
"nodemon": "^2.0.4",
5658
"nyc": "^15.1.0",
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
Recruit CRM Data Import
2+
===
3+
4+
# Configuration
5+
Configuration file is at `./scripts/recruit-crm-job-import/config.js`.
6+
7+
8+
# Usage
9+
``` bash
10+
node scripts/recruit-crm-job-import <pathname-to-a-csv-file>
11+
```
12+
13+
By default the script creates jobs and resource bookings via `TC_API`.
14+
# Example
15+
16+
Follow the README for Taas API to deploy Taas API locally and then point the script to the local API by running:
17+
18+
``` bash
19+
export RCRM_IMPORT_TAAS_API_URL=http://localhost:3000/api/v5
20+
node scripts/recruit-crm-job-import scripts/recruit-crm-job-import/example_data.csv | tee /tmp/report.txt
21+
```
22+
23+
The example output is:
24+
25+
``` bash
26+
DEBUG: processing line #1 - {"directProjectId":"24568","projectId":"(dynamic load)","externalId":"","title":"taas-demo-job5","startDate":"10/26/2020","endDate":"01/29/2021","numPositions":"2","userHandle":"nkumartest","jobid":"(dynamic load)","customerRate":"20","memberRate":"10","_lnum":1}
27+
WARN: #1 - externalId is missing
28+
DEBUG: processed line #1
29+
DEBUG: processing line #2 - {"directProjectId":"24568","projectId":"(dynamic load)","externalId":"0","title":"taas-demo-job5","startDate":"10/26/2020","endDate":"01/29/2021","numPositions":"2","userHandle":"not_found_handle","jobid":"(dynamic load)","customerRate":"20","memberRate":"10","_lnum":2}
30+
ERROR: #2 - handle: not_found_handle user not found
31+
DEBUG: processed line #2
32+
DEBUG: processing line #3 - {"directProjectId":"24568","projectId":"(dynamic load)","externalId":"0","title":"taas-demo-job5","startDate":"10/26/2020","endDate":"01/29/2021","numPositions":"2","userHandle":"nkumartest","jobid":"(dynamic load)","customerRate":"20","memberRate":"10","_lnum":3}
33+
DEBUG: userHandle: nkumartest userId: 57646ff9-1cd3-4d3c-88ba-eb09a395366c
34+
DEBUG: resourceBookingId: dc8b23d4-9987-4a7d-a587-2056283223de status: assigned
35+
INFO: #3 - id: 7c8ed989-35bf-4899-9c93-708630a7c63b job already exists; id: dc8b23d4-9987-4a7d-a587-2056283223de resource booking created; id: dc8b23d4-9987-4a7d-a587-2056283223de status: assigned resource booking updated
36+
DEBUG: processed line #3
37+
DEBUG: processing line #4 - {"directProjectId":"24567","projectId":"(dynamic load)","externalId":"1212","title":"Dummy Description","startDate":"10/20/2020","endDate":"01/29/2021","numPositions":"2","userHandle":"pshah_manager","jobid":"(dynamic load)","customerRate":"150","memberRate":"100","_lnum":4}
38+
DEBUG: userHandle: pshah_manager userId: a55fe1bc-1754-45fa-9adc-cf3d6d7c377a
39+
DEBUG: resourceBookingId: 708469fb-ead0-4fc3-bef7-1ef4dd041428 status: assigned
40+
INFO: #4 - id: f61da880-5295-40c2-b6db-21e6cdef93f9 job created; id: 708469fb-ead0-4fc3-bef7-1ef4dd041428 resource booking created; id: 708469fb-ead0-4fc3-bef7-1ef4dd041428 status: assigned resource booking updated
41+
DEBUG: processed line #4
42+
DEBUG: processing line #5 - {"directProjectId":"24566","projectId":"(dynamic load)","externalId":"23850272","title":"33fromzaps330","startDate":"02/21/2021","endDate":"03/15/2021","numPositions":"7","userHandle":"nkumar2","jobid":"(dynamic load)","customerRate":"50","memberRate":"30","_lnum":5}
43+
DEBUG: userHandle: nkumar2 userId: 4b00d029-c87b-47b2-bfe2-0ab80d8b5774
44+
DEBUG: resourceBookingId: 7870c30b-e511-48f2-8687-499ab116174f status: assigned
45+
INFO: #5 - id: 72dc0399-5e4b-4783-9a27-ea07a4ce99a7 job created; id: 7870c30b-e511-48f2-8687-499ab116174f resource booking created; id: 7870c30b-e511-48f2-8687-499ab116174f status: assigned resource booking updated
46+
DEBUG: processed line #5
47+
DEBUG: processing line #6 - {"directProjectId":"24565","projectId":"(dynamic load)","externalId":"23843365","title":"Designer","startDate":"02/24/2021","endDate":"03/30/2021","numPositions":"1","userHandle":"GunaK-TopCoder","jobid":"(dynamic load)","customerRate":"70","memberRate":"70","_lnum":6}
48+
DEBUG: userHandle: GunaK-TopCoder userId: 2bba34d5-20e4-46d6-bfc1-05736b17afbb
49+
DEBUG: resourceBookingId: b2e705d3-6864-4697-96bb-dc2a288755bc status: assigned
50+
INFO: #6 - id: 7ff0737e-958c-494e-8a5a-592ac1c5d4ff job created; id: b2e705d3-6864-4697-96bb-dc2a288755bc resource booking created; id: b2e705d3-6864-4697-96bb-dc2a288755bc status: assigned resource booking updated
51+
DEBUG: processed line #6
52+
DEBUG: processing line #7 - {"directProjectId":"24564","projectId":"(dynamic load)","externalId":"23836459","title":"demo-dev-19janV4","startDate":"01/20/2021","endDate":"01/30/2021","numPositions":"1","userHandle":"nkumar1","jobid":"(dynamic load)","customerRate":"400","memberRate":"200","_lnum":7}
53+
DEBUG: userHandle: nkumar1 userId: ab19a53b-0607-4a99-8bdd-f3b0cb552293
54+
DEBUG: resourceBookingId: 04299b4c-3f6e-4b3e-ae57-bf8232408cf9 status: assigned
55+
INFO: #7 - id: 73301ade-40ff-4103-bd50-37b8d2a98183 job created; id: 04299b4c-3f6e-4b3e-ae57-bf8232408cf9 resource booking created; id: 04299b4c-3f6e-4b3e-ae57-bf8232408cf9 status: assigned resource booking updated
56+
DEBUG: processed line #7
57+
INFO: === summary ===
58+
INFO: total: 7
59+
INFO: success: 5
60+
INFO: failure: 1
61+
INFO: skips: 1
62+
INFO: === summary ===
63+
INFO: done!
64+
```
65+
66+
To list all skipped lines:
67+
68+
``` bash
69+
cat /tmp/report.txt | grep 'WARN'
70+
```
71+
72+
To find out whether there are some users not found by user handles, run the following command:
73+
74+
``` bash
75+
cat /tmp/report.txt | grep 'ERROR' | grep 'user not found'
76+
```
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/*
2+
* Configuration for the RCRM import script.
3+
* Namespace is created to allow to configure the env variables for this script independently.
4+
*/
5+
6+
const config = require('config')
7+
8+
const namespace = process.env.RCRM_IMPORT_CONFIG_NAMESAPCE || 'RCRM_IMPORT_'
9+
10+
module.exports = {
11+
SLEEP_TIME: process.env[`${namespace}SLEEP_TIME`] || 500,
12+
TAAS_API_URL: process.env[`${namespace}TAAS_API_URL`] || config.TC_API,
13+
14+
TC_API: process.env[`${namespace}TC_API`] || config.TC_API,
15+
AUTH0_URL: process.env[`${namespace}AUTH0_URL`] || config.AUTH0_URL,
16+
AUTH0_AUDIENCE: process.env[`${namespace}AUTH0_AUDIENCE`] || config.AUTH0_AUDIENCE,
17+
TOKEN_CACHE_TIME: process.env[`${namespace}TOKEN_CACHE_TIME`] || config.TOKEN_CACHE_TIME,
18+
AUTH0_CLIENT_ID: process.env[`${namespace}AUTH0_CLIENT_ID`] || config.AUTH0_CLIENT_ID,
19+
AUTH0_CLIENT_SECRET: process.env[`${namespace}AUTH0_CLIENT_SECRET`] || config.AUTH0_CLIENT_SECRET,
20+
AUTH0_PROXY_SERVER_URL: process.env[`${namespace}AUTH0_PROXY_SERVER_URL`] || config.AUTH0_PROXY_SERVER_URL
21+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/*
2+
* Constants for the RCRM import script.
3+
*/
4+
5+
module.exports = {
6+
ProcessingStatus: {
7+
Successful: 'successful',
8+
Failed: 'failed',
9+
Skipped: 'skipped'
10+
},
11+
fieldNameMap: {
12+
DirectprojectId: 'directProjectId',
13+
externalId: 'externalId',
14+
title: 'title',
15+
startDate: 'startDate',
16+
endDate: 'endDate',
17+
numPositions: 'numPositions',
18+
userHandle: 'userHandle',
19+
customerRate: 'customerRate',
20+
memberRate: 'memberRate'
21+
}
22+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
DirectprojectId,projectId,externalId,title,startDate,endDate,numPositions,userHandle,jobid,customerRate,memberRate
2+
24568,(dynamic load),,taas-demo-job5,10/26/2020,01/29/2021,2,nkumartest,(dynamic load),20,10
3+
24568,(dynamic load),0,taas-demo-job5,10/26/2020,01/29/2021,2,not_found_handle,(dynamic load),20,10
4+
24568,(dynamic load),0,taas-demo-job5,10/26/2020,01/29/2021,2,nkumartest,(dynamic load),20,10
5+
24567,(dynamic load),1212,Dummy Description,10/20/2020,01/29/2021,2,pshah_manager,(dynamic load),150,100
6+
24566,(dynamic load),23850272,33fromzaps330,02/21/2021,03/15/2021,7,nkumar2,(dynamic load),50,30
7+
24565,(dynamic load),23843365,Designer,02/24/2021,03/30/2021,1,GunaK-TopCoder,(dynamic load),70,70
8+
24564,(dynamic load),23836459,demo-dev-19janV4,01/20/2021,01/30/2021,1,nkumar1,(dynamic load),400,200
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
/*
2+
* Provide some commonly used functions for the RCRM import script.
3+
*/
4+
const config = require('./config')
5+
const request = require('superagent')
6+
const { getM2MToken } = require('../../src/common/helper')
7+
8+
/**
9+
* Sleep for a given number of milliseconds.
10+
*
11+
* @param {Number} milliseconds the sleep time
12+
* @returns {undefined}
13+
*/
14+
async function sleep (milliseconds) {
15+
return new Promise((resolve) => setTimeout(resolve, milliseconds))
16+
}
17+
18+
/**
19+
* Create a new job via taas api.
20+
*
21+
* @param {Object} data the job data
22+
* @returns {Object} the result
23+
*/
24+
async function createJob (data) {
25+
const token = await getM2MToken()
26+
const { body: job } = await request.post(`${config.TAAS_API_URL}/jobs`)
27+
.set('Authorization', `Bearer ${token}`)
28+
.set('Content-Type', 'application/json')
29+
.send(data)
30+
return job
31+
}
32+
33+
/**
34+
* Find taas job by external id.
35+
*
36+
* @param {String} externalId the external id
37+
* @returns {Object} the result
38+
*/
39+
async function getJobByExternalId (externalId) {
40+
const token = await getM2MToken()
41+
const { body: jobs } = await request.get(`${config.TAAS_API_URL}/jobs`)
42+
.query({ externalId })
43+
.set('Authorization', `Bearer ${token}`)
44+
if (!jobs.length) {
45+
throw new Error(`externalId: ${externalId} job not found`)
46+
}
47+
return jobs[0]
48+
}
49+
50+
/**
51+
* Update the status of a resource booking.
52+
*
53+
* @param {String} resourceBookingId the resource booking id
54+
* @param {String} status the status for the resource booking
55+
* @returns {Object} the result
56+
*/
57+
async function updateResourceBookingStatus (resourceBookingId, status) {
58+
const token = await getM2MToken()
59+
const { body: resourceBooking } = await request.patch(`${config.TAAS_API_URL}/resourceBookings/${resourceBookingId}`)
60+
.set('Authorization', `Bearer ${token}`)
61+
.set('Content-Type', 'application/json')
62+
.send({ status })
63+
return resourceBooking
64+
}
65+
66+
/**
67+
* Find taas resource booking by job id and user id.
68+
*
69+
* @param {String} jobId the job id
70+
* @param {String} userId the user id
71+
* @returns {Object} the result
72+
*/
73+
async function getResourceBookingByJobIdAndUserId (jobId, userId) {
74+
const token = await getM2MToken()
75+
const { body: resourceBookings } = await request.get(`${config.TAAS_API_URL}/resourceBookings`)
76+
.query({ jobId, userId })
77+
.set('Authorization', `Bearer ${token}`)
78+
if (!resourceBookings.length) {
79+
throw new Error(`jobId: ${jobId} userId: ${userId} resource booking not found`)
80+
}
81+
return resourceBookings[0]
82+
}
83+
84+
/**
85+
* Create a new resource booking via taas api.
86+
*
87+
* @param {Object} data the resource booking data
88+
* @returns {Object} the result
89+
*/
90+
async function createResourceBooking (data) {
91+
const token = await getM2MToken()
92+
const { body: resourceBooking } = await request.post(`${config.TAAS_API_URL}/resourceBookings`)
93+
.set('Authorization', `Bearer ${token}`)
94+
.set('Content-Type', 'application/json')
95+
.send(data)
96+
return resourceBooking
97+
}
98+
99+
/**
100+
* Find user via /v5/users by user handle.
101+
*
102+
* @param {String} handle the user handle
103+
* @returns {Object} the result
104+
*/
105+
async function getUserByHandle (handle) {
106+
const token = await getM2MToken()
107+
const { body: users } = await request.get(`${config.TC_API}/users`)
108+
.query({ handle })
109+
.set('Authorization', `Bearer ${token}`)
110+
if (!users.length) {
111+
throw new Error(`handle: ${handle} user not found`)
112+
}
113+
return users[0]
114+
}
115+
116+
/**
117+
* Find project via /v5/projects by Direct project id.
118+
*
119+
* @param {Number} directProjectId the Direct project id
120+
* @returns {Object} the result
121+
*/
122+
async function getProjectByDirectProjectId (directProjectId) {
123+
const token = await getM2MToken()
124+
const { body: projects } = await request.get(`${config.TC_API}/projects`)
125+
.query({ directProjectId })
126+
.set('Authorization', `Bearer ${token}`)
127+
if (!projects.length) {
128+
throw new Error(`directProjectId: ${directProjectId} project not found`)
129+
}
130+
return projects[0]
131+
}
132+
133+
module.exports = {
134+
sleep,
135+
createJob,
136+
getJobByExternalId,
137+
updateResourceBookingStatus,
138+
getResourceBookingByJobIdAndUserId,
139+
createResourceBooking,
140+
getUserByHandle,
141+
getProjectByDirectProjectId
142+
}

0 commit comments

Comments
 (0)