Skip to content
This repository was archived by the owner on Mar 13, 2025. It is now read-only.

Commit 4a3fe33

Browse files
authored
Merge pull request #139 from topcoder-platform/integration-b
My Gigs > Profile Update : integration
2 parents a0780cd + 3d8a0d7 commit 4a3fe33

File tree

26 files changed

+441
-197
lines changed

26 files changed

+441
-197
lines changed

.circleci/config.yml

+1-2
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,7 @@ workflows:
7676
filters:
7777
branches:
7878
only:
79-
- dev
80-
- fix-m2m-env-var
79+
- integration-b
8180

8281
# Production builds are exectuted only on tagged commits to the
8382
# master branch.

config/default.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ module.exports = {
1212
COMMUNITY_APP: "https://community-app.topcoder-dev.com",
1313
PLATFORM_WEBSITE_URL: "https://platform.topcoder-dev.com",
1414
},
15-
RECRUIT_API: process.env.RECRUIT_API,
15+
RECRUIT_API: "https://www.topcoder-dev.com",
1616
// the server api base path
1717
API_BASE_PATH: process.env.API_BASE_PATH || "/earn-app/api/my-gigs",
1818
// the log level, default is 'debug'

config/development.js

+1
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,5 @@ module.exports = {
1111
COMMUNITY_APP: "https://community-app.topcoder-dev.com",
1212
PLATFORM_WEBSITE_URL: "https://platform.topcoder-dev.com",
1313
},
14+
RECRUIT_API: "https://www.topcoder-dev.com",
1415
};

config/production.js

+1
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,5 @@ module.exports = {
1111
COMMUNITY_APP: "https://community-app.topcoder.com",
1212
PLATFORM_WEBSITE_URL: "https://platform.topcoder.com",
1313
},
14+
RECRUIT_API: "https://www.topcoder.com",
1415
};

src/actions/lookup.js

+25-3
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,35 @@ async function checkIsLoggedIn() {
1313
return service.checkIsLoggedIn();
1414
}
1515

16-
async function getGigStatuses() {
17-
return service.getGigStatuses();
16+
/**
17+
* Gets all the countries.
18+
* @returns {Array} Array containing all countries
19+
*/
20+
async function getAllCountries() {
21+
// fetch the first page to see how many more fetches are necessary to get all
22+
const countries = await service.getPaginatedCountries();
23+
const {
24+
meta: { totalPages },
25+
} = countries;
26+
27+
const pagesMissing = totalPages - 1;
28+
29+
// fetch the other pages.
30+
const allPageResults = await Promise.all(
31+
[...Array(pagesMissing > 0 ? pagesMissing : 0)].map((_, index) => {
32+
const newPage = index + 2;
33+
34+
return service.getPaginatedCountries(newPage);
35+
})
36+
);
37+
38+
const newCountries = allPageResults.map((data) => data).flat();
39+
return [...countries, ...newCountries];
1840
}
1941

2042
export default createActions({
2143
GET_TAGS: getTags,
2244
GET_COMMUNITY_LIST: getCommunityList,
2345
CHECK_IS_LOGGED_IN: checkIsLoggedIn,
24-
GET_GIG_STATUSES: getGigStatuses,
46+
GET_ALL_COUNTRIES: getAllCountries,
2547
});

src/api/app-routes.js

+13-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,19 @@ const authenticator = require("tc-core-library-js").middleware.jwtAuthenticator;
1818
*/
1919
module.exports = (app) => {
2020
app.use(express.json());
21-
app.use(cors());
21+
app.use(
22+
cors({
23+
// Allow browsers access pagination data in headers
24+
exposedHeaders: [
25+
"X-Page",
26+
"X-Per-Page",
27+
"X-Total",
28+
"X-Total-Pages",
29+
"X-Prev-Page",
30+
"X-Next-Page",
31+
],
32+
})
33+
);
2234
app.use(
2335
fileUpload({
2436
limits: {

src/api/common/helper.js

+69-10
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,26 @@ async function getMember(handle, query) {
335335
return res.body;
336336
}
337337

338+
/**
339+
* Get member traits info
340+
* @param {string} handle the handle of the user
341+
* @param {string} query the query criteria
342+
* @returns the object of member traitsdetails
343+
*/
344+
async function getMemberTraits(handle, query) {
345+
const token = await getM2MToken();
346+
const url = `${config.API.V5}/members/${handle}/traits`;
347+
const res = await request
348+
.get(url)
349+
.query(query)
350+
.set("Authorization", `Bearer ${token}`)
351+
.set("Accept", "application/json");
352+
localLogger.debug({
353+
context: "getMemberTraits",
354+
message: `response body: ${JSON.stringify(res.body)}`,
355+
});
356+
return res.body;
357+
}
338358
/**
339359
* Update member details
340360
* @param {string} handle the handle of the user
@@ -357,6 +377,28 @@ async function updateMember(currentUser, data) {
357377
return res.body;
358378
}
359379

380+
/**
381+
* Update member traits
382+
* @param {string} handle the handle of the user
383+
* @param {object} data the data to be updated
384+
* @return {object} the object of updated member details
385+
*/
386+
async function updateMemberTraits(currentUser, data) {
387+
const token = currentUser.jwtToken;
388+
const url = `${config.API.V5}/members/${currentUser.handle}/traits`;
389+
const res = await request
390+
.put(url)
391+
.set("Authorization", token)
392+
.set("Content-Type", "application/json")
393+
.set("Accept", "application/json")
394+
.send(data);
395+
localLogger.debug({
396+
context: "updateMemberTraits",
397+
message: `response body: ${JSON.stringify(res.body)}`,
398+
});
399+
return res.body;
400+
}
401+
360402
/**
361403
* Get Recruit CRM profile details
362404
* @param {object} currentUser the user who performs the operation
@@ -379,21 +421,36 @@ async function getRCRMProfile(currentUser) {
379421
/**
380422
* Update Recruit CRM profile details
381423
* @param {object} currentUser the user who performs the operation
382-
* @param {object} file the resume file
383424
* @param {object} data the data to be updated
425+
* @param {object} file the resume file
384426
* @return {object} the returned object
385427
*/
386-
async function updateRCRMProfile(currentUser, file, data) {
428+
async function updateRCRMProfile(currentUser, data, file) {
387429
const token = currentUser.jwtToken;
388430
const url = `${config.RECRUIT_API}/api/recruit/profile`;
389-
const res = await request
390-
.post(url)
391-
.set("Authorization", token)
392-
.set("Content-Type", "multipart/form-data")
393-
.set("Accept", "application/json")
394-
.field("phone", data.phone)
395-
.field("availability", data.availability)
396-
.attach("resume", file.data, file.name);
431+
let res = null;
432+
if (file) {
433+
res = await request
434+
.post(url)
435+
.set("Authorization", token)
436+
.set("Content-Type", "multipart/form-data")
437+
.set("Accept", "application/json")
438+
.field("phone", data.phone)
439+
.field("availability", data.availability)
440+
.field("city", data.city)
441+
.field("countryName", data.countryName)
442+
.attach("resume", file.data, file.name);
443+
} else {
444+
res = await request
445+
.post(url)
446+
.set("Authorization", token)
447+
.set("Content-Type", "multipart/form-data")
448+
.set("Accept", "application/json")
449+
.field("phone", data.phone)
450+
.field("availability", data.availability)
451+
.field("city", data.city)
452+
.field("countryName", data.countryName);
453+
}
397454
localLogger.debug({
398455
context: "updateRCRMProfile",
399456
message: `response body: ${JSON.stringify(res.body)}`,
@@ -412,7 +469,9 @@ module.exports = {
412469
getJobCandidates,
413470
getJobs,
414471
getMember,
472+
getMemberTraits,
415473
updateMember,
474+
updateMemberTraits,
416475
getRCRMProfile,
417476
updateRCRMProfile,
418477
};

src/api/controllers/ProfileController.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ async function getMyProfile(req, res) {
1919
* @param res the response
2020
*/
2121
async function updateMyProfile(req, res) {
22-
await service.updateMyProfile(req.authUser, req.files, req.body);
22+
await service.updateMyProfile(req.authUser, req.body, req.files);
2323
res.status(204).end();
2424
}
2525

src/api/mock-api/mock-api.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,10 @@ app.use((req, res, next) => {
2121

2222
app.get("/api/recruit/profile", (req, res) => {
2323
const result = {
24+
hasProfile: false,
2425
phone: "555-555-55-55",
2526
resume: "https://resume.topcoder.com/1234567",
26-
availibility: true,
27+
availability: true,
2728
};
2829
res.status(200).json(result);
2930
});

src/api/services/ProfileService.js

+67-26
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,10 @@ async function getMyProfile(currentUser) {
3131
email: _.get(member, "email", null),
3232
city: _.get(member, "addresses[0].city", null),
3333
country: _.get(member, "competitionCountryCode", null),
34+
hasProfile: _.get(recruitProfile, "hasProfile", false),
3435
phone: _.get(recruitProfile, "phone", null),
3536
resume: _.get(recruitProfile, "resume", null),
36-
availability: _.get(recruitProfile, "availibility", true),
37+
availability: _.get(recruitProfile, "availability", true),
3738
};
3839
}
3940

@@ -48,13 +49,13 @@ getMyProfile.schema = Joi.object()
4849
* @param {object} currentUser the user who perform this operation.
4950
* @param {object} data the data to be updated
5051
*/
51-
async function updateMyProfile(currentUser, files, data) {
52+
async function updateMyProfile(currentUser, data, files) {
5253
// we expect logged-in users
5354
if (currentUser.isMachine) {
5455
return;
5556
}
5657
// check if file was truncated
57-
if (files.resume.truncated) {
58+
if (files && files.resume.truncated) {
5859
throw new errors.BadRequestError(
5960
`Maximum allowed file size is ${config.MAX_ALLOWED_FILE_SIZE_MB} MB`
6061
);
@@ -64,7 +65,7 @@ async function updateMyProfile(currentUser, files, data) {
6465
`^.*\.(${_.join(config.ALLOWED_FILE_TYPES, "|")})$`,
6566
"i"
6667
);
67-
if (!regex.test(files.resume.name)) {
68+
if (files && !regex.test(files.resume.name)) {
6869
throw new errors.BadRequestError(
6970
`Allowed file types are: ${_.join(config.ALLOWED_FILE_TYPES, ",")}`
7071
);
@@ -75,56 +76,96 @@ async function updateMyProfile(currentUser, files, data) {
7576
"fields=addresses,competitionCountryCode,homeCountryCode"
7677
);
7778
const update = {};
79+
let shouldUpdateTrait = false;
7880
// update member data if city is different from existing one
7981
if (_.get(member, "addresses[0].city") !== data.city) {
8082
update.addresses = _.cloneDeep(member.addresses);
8183
if (!_.isEmpty(update.addresses)) {
8284
update.addresses[0].city = data.city;
8385
delete update.addresses[0].createdAt;
8486
delete update.addresses[0].updatedAt;
87+
delete update.addresses[0].createdBy;
88+
delete update.addresses[0].updatedBy;
89+
update.addresses[0].streetAddr1 = update.addresses[0].streetAddr1
90+
? update.addresses[0].streetAddr1
91+
: " ";
92+
update.addresses[0].streetAddr2 = update.addresses[0].streetAddr2
93+
? update.addresses[0].streetAddr2
94+
: " ";
95+
update.addresses[0].type = update.addresses[0].type
96+
? update.addresses[0].type
97+
: "HOME";
98+
update.addresses[0].stateCode = update.addresses[0].stateCode
99+
? update.addresses[0].stateCode
100+
: " ";
101+
update.addresses[0].zip = update.addresses[0].zip
102+
? update.addresses[0].zip
103+
: " ";
85104
} else {
86105
update.addresses = [
87106
{
88107
city: data.city,
108+
type: "HOME",
109+
stateCode: " ",
110+
zip: " ",
111+
streetAddr1: " ",
112+
streetAddr2: " ",
89113
},
90114
];
91115
}
92116
}
93117
// update member data if competitionCountryCode is different from existing one
94118
if (_.get(member, "competitionCountryCode") !== data.country) {
95119
update.competitionCountryCode = data.country;
120+
shouldUpdateTrait = true;
96121
}
97122
if (_.get(member, "homeCountryCode") !== data.country) {
98123
update.homeCountryCode = data.country;
124+
shouldUpdateTrait = true;
99125
}
100126
// avoid unnecessary api calls
101127
if (!_.isEmpty(update)) {
102128
await helper.updateMember(currentUser, update);
103129
}
104-
await helper.updateRCRMProfile(currentUser, files.resume, {
105-
phone: data.phone,
106-
availability: data.availability,
107-
});
130+
if (shouldUpdateTrait) {
131+
const memberTraits = await helper.getMemberTraits(
132+
currentUser.handle,
133+
`traitIds=basic_info`
134+
);
135+
if (memberTraits && memberTraits.length) {
136+
memberTraits[0]["traits"].data[0].country = data.countryName;
137+
delete memberTraits[0].createdAt;
138+
delete memberTraits[0].createdBy;
139+
delete memberTraits[0].updatedAt;
140+
delete memberTraits[0].updatedBy;
141+
delete memberTraits[0].userId;
142+
await helper.updateMemberTraits(currentUser, memberTraits);
143+
}
144+
}
145+
await helper.updateRCRMProfile(
146+
currentUser,
147+
{
148+
phone: data.phone,
149+
availability: data.availability,
150+
city: data.city,
151+
countryName: data.countryName,
152+
},
153+
files && files.resume
154+
);
108155
}
109156

110-
updateMyProfile.schema = Joi.object()
111-
.keys({
112-
currentUser: Joi.object().required(),
113-
files: Joi.object()
114-
.keys({
115-
resume: Joi.object().required(),
116-
})
117-
.required(),
118-
data: Joi.object()
119-
.keys({
120-
city: Joi.string().required(),
121-
country: Joi.string().required(),
122-
phone: Joi.string().required(),
123-
availability: Joi.boolean().required(),
124-
})
125-
.required(),
126-
})
127-
.required();
157+
updateMyProfile.schema = Joi.object({
158+
currentUser: Joi.object().required(),
159+
data: Joi.object()
160+
.keys({
161+
city: Joi.string().required(),
162+
country: Joi.string().required(),
163+
countryName: Joi.string().required(),
164+
phone: Joi.string().required(),
165+
availability: Joi.boolean().required(),
166+
})
167+
.required(),
168+
}).unknown();
128169

129170
module.exports = {
130171
getMyProfile,

0 commit comments

Comments
 (0)