Skip to content

Issues-293 #46

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Dec 13, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ module.exports = {
COPILOT: 'copilot',
MANAGER: 'manager',
CUSTOMER: 'customer'
},
CHALLENGE_ROLES: {
COPILOT: 'Copilot',
MANAGER: 'Manager'
}
},
VANILLA: {
Expand Down
39 changes: 26 additions & 13 deletions src/modules/user_management/handler.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
const config = require('config')
const _ = require('lodash')
const util = require('util')
const constants = require('../../constants')
const logger = require('../../utils/logger.util')
const { manageRocketUser } = require('../../services/rokect')
const { manageVanillaUser } = require('../../services/vanilla')
const {
getTopcoderUserHandle: getUserHandle,
getAllChallengeRolesForUser,
getProjectRoleForUser,
processPayload
} = require('./helpers')

Expand Down Expand Up @@ -34,6 +37,23 @@ function canProcessEvent (payload, topic) {
return true
}

async function processPayloadItem (item, topic) {
const data = processPayload(item, topic)
try {
data.handle = data.handle || (await getUserHandle(data.userId))
data.projectRole = (await getProjectRoleForUser(data.challengeId, data.userId))
data.challengeRoles = (await getAllChallengeRolesForUser(data.challengeId, data.userId))
} catch (err) {
logger.error(util.inspect(err))
}
for (const service of services) {
await service(data)
.catch(err => {
logger.error(util.inspect(err))
})
}
}

/**
* Handle a set of messages from the Kafka topic
* @param {Array} messageSet
Expand All @@ -48,19 +68,12 @@ async function handler (messageSet, topic) {
if (!canProcessEvent(item, topic)) {
continue
}

const data = processPayload(item, topic)
try {
data.handle = data.handle || (await getUserHandle(data.userId))
} catch (err) {
logger.error(util.inspect(err))
continue
}
for (const service of services) {
await service(data)
.catch(err => {
logger.error(util.inspect(err))
})
if (_.isArray(item)) {
for (const i of item) {
await processPayloadItem(i, topic)
}
} else {
await processPayloadItem(item, topic)
}
}
}
Expand Down
55 changes: 55 additions & 0 deletions src/modules/user_management/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,62 @@ async function getTopcoderUserHandle (userId) {
return userHandle
}

/**
* Get a list of role names for User
* @param challengeId
* @param userId
* @returns {Promise<*[]>}
*/
async function getAllChallengeRolesForUser (challengeId, userId) {
const { body: challengeRoles, status: challengeRolesStatus } = await topcoderApi.getAllChallengeRoles(challengeId)
if (challengeRolesStatus !== 200) {
throw new Error('Couldn\'t load Topcoder Challenge Roles', challengeRoles)
}
const roles = _.filter(challengeRoles, { memberId: userId })

const { body: resourceRoles, status: allRolesStatus } = await topcoderApi.getAllRoles()
if (allRolesStatus !== 200) {
throw new Error('Couldn\'t load Topcoder Resource Roles', resourceRoles)
}

if (_.isEmpty(resourceRoles)) {
throw new Error(`No Resource Roles for ${challengeId}`)
}

_.map(roles, function (obj) {
// add the properties from second array matching the roleId
return _.assign(obj, _.find(resourceRoles, { id: obj.roleId }))
})

// Get all role names
return _.map(roles, 'name')
}

async function getProjectRoleForUser (challengeId, userId) {
const { body: challenge, status: challengeStatus } = await topcoderApi.getChallenge(challengeId)
if (challengeStatus !== 200) {
throw new Error(`Couldn't load Topcoder Challenge by challengeId ${challengeId}`)
}
const { body: project, status: projectStatus } = await topcoderApi.getProject(challenge.projectId)

if (projectStatus !== 200) {
throw new Error(`Couldn't load Topcoder Project by projectId ${challenge.projectId}`)
}

// userId - string , x.userId - int
/* eslint-disable eqeqeq */
const member = _.filter(project.members, x => x.userId == userId)
// User doesn't have project roles
if (_.isEmpty(member)) {
return null
} else {
return member[0].role
}
}

module.exports = {
getTopcoderUserHandle,
getAllChallengeRolesForUser,
getProjectRoleForUser,
processPayload
}
64 changes: 53 additions & 11 deletions src/services/vanilla.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,18 @@ const utils = require('../utils/common.util')
const template = require(config.TEMPLATES.TEMPLATE_FILE_PATH)

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

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

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

const { body: user } = await vanillaClient.addUser(userData)
vanillaUser = user
logger.info(`New user with UserID=${vanillaUser.userID} was added.`)
logger.info(`New user [UserID=${vanillaUser.userID}, Name=${vanillaUser.name}] was added.`)
} else {
// Get a full user profile with roles
const { body: user } = await vanillaClient.getUser(vanillaUser.userID)
Expand All @@ -90,6 +88,7 @@ async function manageVanillaUser (data) {
roleID: [...currentVanillaRoleIDs, ...userTopcoderRoleIDs]
}
await vanillaClient.updateUser(vanillaUser.userID, userData)
// logger.info(`Roles were synchronized for User [UserID=${vanillaUser.userID}, Name=${vanillaUser.name}].`)
}

// Choose action to perform
Expand All @@ -100,12 +99,12 @@ async function manageVanillaUser (data) {
watch: watch,
follow: follow
})
logger.info(`The user '${vanillaUser.name}' was added to the group '${group.name}'`)
logger.info(`User [UserID=${vanillaUser.userID}, Name=${vanillaUser.name} was added to Group [GroupID=${group.groupID}, Name=${group.name}, Watch=${watch}, Follow=${follow}]`)
break
}
case constants.USER_ACTIONS.KICK: {
await vanillaClient.removeUserFromGroup(group.groupID, vanillaUser.userID)
logger.info(`The user '${vanillaUser.name}' was removed from the group '${group.name}'`)
logger.info(`User [UserID=${vanillaUser.userID}, Name =${vanillaUser.name} was removed from Group [GroupID=${group.groupID}, Name=${group.name}]`)
break
}
default:
Expand Down Expand Up @@ -256,7 +255,12 @@ async function createVanillaGroup (challenge) {
}

for (const member of members) {
await manageVanillaUser({ challengeId: challenge.id, action: constants.USER_ACTIONS.INVITE, handle: member.handle, role: member.role })
await manageVanillaUser({
challengeId: challenge.id,
action: constants.USER_ACTIONS.INVITE,
handle: member.handle,
projectRole: member.role
})
}

challengeDetailsDiscussion.url = `${challengeCategory.url}`
Expand Down Expand Up @@ -319,6 +323,44 @@ async function createDiscussions (group, challenge, templateDiscussions, vanilla
}
}

/**
* Auto-watch categories
* @param projectRole string
* @param challengeRoles array
* @returns {boolean}
*/
function shouldWatchCategories (projectRole, challengeRoles) {
// New user
if (!projectRole && _.isEmpty(challengeRoles)) {
return true
}

// Project Copilots / Challenge Copilots
return (projectRole === constants.TOPCODER.PROJECT_ROLES.COPILOT ||
(_.isArray(challengeRoles) && _.includes(challengeRoles, constants.TOPCODER.CHALLENGE_ROLES.COPILOT))
)
}

/**
* Auto-follow categories
* @param projectRole string
* @param challengeRoles array
* @returns {boolean}
*/
function shouldFollowCategories (projectRole, challengeRoles) {
// New user
if (!projectRole && _.isEmpty(challengeRoles)) {
return true
}

// Project Copilots or Managers / Challenge Copilots and Managers
return projectRole === constants.TOPCODER.PROJECT_ROLES.COPILOT ||
projectRole === constants.TOPCODER.PROJECT_ROLES.MANAGER ||
(_.isArray(challengeRoles) && (_.includes(challengeRoles, constants.TOPCODER.CHALLENGE_ROLES.COPILOT) ||
_.includes(challengeRoles, constants.TOPCODER.CHALLENGE_ROLES.MANAGER))
)
}

module.exports = {
manageVanillaUser,
createVanillaGroup,
Expand Down
18 changes: 18 additions & 0 deletions src/utils/topcoder-api.util.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,11 +110,29 @@ async function getProject (projectId) {
return reqToAPI('GET', path)
}

/**
* Gets the list of Resource Roles by challengeId
*/
async function getAllChallengeRoles (challengeId) {
const path = `${config.TOPCODER.API_URL}/v5/resources?challengeId=${challengeId}`
return reqToAPI('GET', path)
}

/**
* Gets the list of All Resource Roles
*/
async function getAllRoles () {
const path = `${config.TOPCODER.API_URL}/v5/resource-roles`
return reqToAPI('GET', path)
}

module.exports = {
getUserDetailsById,
getUserDetailsByHandle,
getChallenge,
updateChallenge,
getRoles,
getAllRoles,
getAllChallengeRoles,
getProject
}