Skip to content

Commit 9481b5c

Browse files
authored
Merge pull request #46 from topcoder-platform/issues-293
Issues-293
2 parents 700d922 + 549e271 commit 9481b5c

File tree

5 files changed

+156
-24
lines changed

5 files changed

+156
-24
lines changed

src/constants.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ module.exports = {
4444
COPILOT: 'copilot',
4545
MANAGER: 'manager',
4646
CUSTOMER: 'customer'
47+
},
48+
CHALLENGE_ROLES: {
49+
COPILOT: 'Copilot',
50+
MANAGER: 'Manager'
4751
}
4852
},
4953
VANILLA: {

src/modules/user_management/handler.js

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
const config = require('config')
2+
const _ = require('lodash')
23
const util = require('util')
34
const constants = require('../../constants')
45
const logger = require('../../utils/logger.util')
56
const { manageRocketUser } = require('../../services/rokect')
67
const { manageVanillaUser } = require('../../services/vanilla')
78
const {
89
getTopcoderUserHandle: getUserHandle,
10+
getAllChallengeRolesForUser,
11+
getProjectRoleForUser,
912
processPayload
1013
} = require('./helpers')
1114

@@ -34,6 +37,23 @@ function canProcessEvent (payload, topic) {
3437
return true
3538
}
3639

40+
async function processPayloadItem (item, topic) {
41+
const data = processPayload(item, topic)
42+
try {
43+
data.handle = data.handle || (await getUserHandle(data.userId))
44+
data.projectRole = (await getProjectRoleForUser(data.challengeId, data.userId))
45+
data.challengeRoles = (await getAllChallengeRolesForUser(data.challengeId, data.userId))
46+
} catch (err) {
47+
logger.error(util.inspect(err))
48+
}
49+
for (const service of services) {
50+
await service(data)
51+
.catch(err => {
52+
logger.error(util.inspect(err))
53+
})
54+
}
55+
}
56+
3757
/**
3858
* Handle a set of messages from the Kafka topic
3959
* @param {Array} messageSet
@@ -48,19 +68,12 @@ async function handler (messageSet, topic) {
4868
if (!canProcessEvent(item, topic)) {
4969
continue
5070
}
51-
52-
const data = processPayload(item, topic)
53-
try {
54-
data.handle = data.handle || (await getUserHandle(data.userId))
55-
} catch (err) {
56-
logger.error(util.inspect(err))
57-
continue
58-
}
59-
for (const service of services) {
60-
await service(data)
61-
.catch(err => {
62-
logger.error(util.inspect(err))
63-
})
71+
if (_.isArray(item)) {
72+
for (const i of item) {
73+
await processPayloadItem(i, topic)
74+
}
75+
} else {
76+
await processPayloadItem(item, topic)
6477
}
6578
}
6679
}

src/modules/user_management/helpers.js

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,62 @@ async function getTopcoderUserHandle (userId) {
6161
return userHandle
6262
}
6363

64+
/**
65+
* Get a list of role names for User
66+
* @param challengeId
67+
* @param userId
68+
* @returns {Promise<*[]>}
69+
*/
70+
async function getAllChallengeRolesForUser (challengeId, userId) {
71+
const { body: challengeRoles, status: challengeRolesStatus } = await topcoderApi.getAllChallengeRoles(challengeId)
72+
if (challengeRolesStatus !== 200) {
73+
throw new Error('Couldn\'t load Topcoder Challenge Roles', challengeRoles)
74+
}
75+
const roles = _.filter(challengeRoles, { memberId: userId })
76+
77+
const { body: resourceRoles, status: allRolesStatus } = await topcoderApi.getAllRoles()
78+
if (allRolesStatus !== 200) {
79+
throw new Error('Couldn\'t load Topcoder Resource Roles', resourceRoles)
80+
}
81+
82+
if (_.isEmpty(resourceRoles)) {
83+
throw new Error(`No Resource Roles for ${challengeId}`)
84+
}
85+
86+
_.map(roles, function (obj) {
87+
// add the properties from second array matching the roleId
88+
return _.assign(obj, _.find(resourceRoles, { id: obj.roleId }))
89+
})
90+
91+
// Get all role names
92+
return _.map(roles, 'name')
93+
}
94+
95+
async function getProjectRoleForUser (challengeId, userId) {
96+
const { body: challenge, status: challengeStatus } = await topcoderApi.getChallenge(challengeId)
97+
if (challengeStatus !== 200) {
98+
throw new Error(`Couldn't load Topcoder Challenge by challengeId ${challengeId}`)
99+
}
100+
const { body: project, status: projectStatus } = await topcoderApi.getProject(challenge.projectId)
101+
102+
if (projectStatus !== 200) {
103+
throw new Error(`Couldn't load Topcoder Project by projectId ${challenge.projectId}`)
104+
}
105+
106+
// userId - string , x.userId - int
107+
/* eslint-disable eqeqeq */
108+
const member = _.filter(project.members, x => x.userId == userId)
109+
// User doesn't have project roles
110+
if (_.isEmpty(member)) {
111+
return null
112+
} else {
113+
return member[0].role
114+
}
115+
}
116+
64117
module.exports = {
65118
getTopcoderUserHandle,
119+
getAllChallengeRolesForUser,
120+
getProjectRoleForUser,
66121
processPayload
67122
}

src/services/vanilla.js

Lines changed: 53 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,18 @@ const utils = require('../utils/common.util')
1111
const template = require(config.TEMPLATES.TEMPLATE_FILE_PATH)
1212

1313
/**
14-
* Add/Remove a user to/from a category.
15-
*
14+
* Add/Remove a user to/from a group.
1615
* @param {Object} data the data from message payload
1716
* @returns {undefined}
1817
*/
1918
async function manageVanillaUser (data) {
20-
const { challengeId, action, handle: username, role: projectRole } = data
21-
logger.info(`Managing users for challengeID=${challengeId} ...`)
19+
const { challengeId, action, handle: username, projectRole, challengeRoles } = data
20+
logger.info(`Managing user for challengeID=${challengeId} [action=${action}, handle=${username}, projectRole=${JSON.stringify(projectRole)}, challengeRoles=${JSON.stringify(challengeRoles)}]...`)
2221
const { body: groups } = await vanillaClient.searchGroups(challengeId)
2322
const group = groups.length > 0 ? groups[0] : null
2423

25-
// Only members and copilots will receive notifications
26-
const watch = (!projectRole || projectRole === constants.TOPCODER.PROJECT_ROLES.COPILOT) ? 1 : 0
27-
const follow = 1
24+
const watch = shouldWatchCategories(projectRole, challengeRoles)
25+
const follow = shouldFollowCategories(projectRole, challengeRoles)
2826

2927
if (!group) {
3028
throw new Error('The group wasn\'t not found by challengeID')
@@ -77,7 +75,7 @@ async function manageVanillaUser (data) {
7775

7876
const { body: user } = await vanillaClient.addUser(userData)
7977
vanillaUser = user
80-
logger.info(`New user with UserID=${vanillaUser.userID} was added.`)
78+
logger.info(`New user [UserID=${vanillaUser.userID}, Name=${vanillaUser.name}] was added.`)
8179
} else {
8280
// Get a full user profile with roles
8381
const { body: user } = await vanillaClient.getUser(vanillaUser.userID)
@@ -90,6 +88,7 @@ async function manageVanillaUser (data) {
9088
roleID: [...currentVanillaRoleIDs, ...userTopcoderRoleIDs]
9189
}
9290
await vanillaClient.updateUser(vanillaUser.userID, userData)
91+
// logger.info(`Roles were synchronized for User [UserID=${vanillaUser.userID}, Name=${vanillaUser.name}].`)
9392
}
9493

9594
// Choose action to perform
@@ -100,12 +99,12 @@ async function manageVanillaUser (data) {
10099
watch: watch,
101100
follow: follow
102101
})
103-
logger.info(`The user '${vanillaUser.name}' was added to the group '${group.name}'`)
102+
logger.info(`User [UserID=${vanillaUser.userID}, Name=${vanillaUser.name} was added to Group [GroupID=${group.groupID}, Name=${group.name}, Watch=${watch}, Follow=${follow}]`)
104103
break
105104
}
106105
case constants.USER_ACTIONS.KICK: {
107106
await vanillaClient.removeUserFromGroup(group.groupID, vanillaUser.userID)
108-
logger.info(`The user '${vanillaUser.name}' was removed from the group '${group.name}'`)
107+
logger.info(`User [UserID=${vanillaUser.userID}, Name =${vanillaUser.name} was removed from Group [GroupID=${group.groupID}, Name=${group.name}]`)
109108
break
110109
}
111110
default:
@@ -256,7 +255,12 @@ async function createVanillaGroup (challenge) {
256255
}
257256

258257
for (const member of members) {
259-
await manageVanillaUser({ challengeId: challenge.id, action: constants.USER_ACTIONS.INVITE, handle: member.handle, role: member.role })
258+
await manageVanillaUser({
259+
challengeId: challenge.id,
260+
action: constants.USER_ACTIONS.INVITE,
261+
handle: member.handle,
262+
projectRole: member.role
263+
})
260264
}
261265

262266
challengeDetailsDiscussion.url = `${challengeCategory.url}`
@@ -319,6 +323,44 @@ async function createDiscussions (group, challenge, templateDiscussions, vanilla
319323
}
320324
}
321325

326+
/**
327+
* Auto-watch categories
328+
* @param projectRole string
329+
* @param challengeRoles array
330+
* @returns {boolean}
331+
*/
332+
function shouldWatchCategories (projectRole, challengeRoles) {
333+
// New user
334+
if (!projectRole && _.isEmpty(challengeRoles)) {
335+
return true
336+
}
337+
338+
// Project Copilots / Challenge Copilots
339+
return (projectRole === constants.TOPCODER.PROJECT_ROLES.COPILOT ||
340+
(_.isArray(challengeRoles) && _.includes(challengeRoles, constants.TOPCODER.CHALLENGE_ROLES.COPILOT))
341+
)
342+
}
343+
344+
/**
345+
* Auto-follow categories
346+
* @param projectRole string
347+
* @param challengeRoles array
348+
* @returns {boolean}
349+
*/
350+
function shouldFollowCategories (projectRole, challengeRoles) {
351+
// New user
352+
if (!projectRole && _.isEmpty(challengeRoles)) {
353+
return true
354+
}
355+
356+
// Project Copilots or Managers / Challenge Copilots and Managers
357+
return projectRole === constants.TOPCODER.PROJECT_ROLES.COPILOT ||
358+
projectRole === constants.TOPCODER.PROJECT_ROLES.MANAGER ||
359+
(_.isArray(challengeRoles) && (_.includes(challengeRoles, constants.TOPCODER.CHALLENGE_ROLES.COPILOT) ||
360+
_.includes(challengeRoles, constants.TOPCODER.CHALLENGE_ROLES.MANAGER))
361+
)
362+
}
363+
322364
module.exports = {
323365
manageVanillaUser,
324366
createVanillaGroup,

src/utils/topcoder-api.util.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,11 +110,29 @@ async function getProject (projectId) {
110110
return reqToAPI('GET', path)
111111
}
112112

113+
/**
114+
* Gets the list of Resource Roles by challengeId
115+
*/
116+
async function getAllChallengeRoles (challengeId) {
117+
const path = `${config.TOPCODER.API_URL}/v5/resources?challengeId=${challengeId}`
118+
return reqToAPI('GET', path)
119+
}
120+
121+
/**
122+
* Gets the list of All Resource Roles
123+
*/
124+
async function getAllRoles () {
125+
const path = `${config.TOPCODER.API_URL}/v5/resource-roles`
126+
return reqToAPI('GET', path)
127+
}
128+
113129
module.exports = {
114130
getUserDetailsById,
115131
getUserDetailsByHandle,
116132
getChallenge,
117133
updateChallenge,
118134
getRoles,
135+
getAllRoles,
136+
getAllChallengeRoles,
119137
getProject
120138
}

0 commit comments

Comments
 (0)