Skip to content

Issues-94: Updating group category #37

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
Nov 5, 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
2 changes: 1 addition & 1 deletion src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ module.exports = {
KICK: 'kick'
},
TOPCODER: {
ROLE_COPILOT : 'copilot'
ROLE_COPILOT: 'copilot'
},
VANILLA: {
CHALLENGES_FORUM: 'Challenges Forums',
Expand Down
1 change: 0 additions & 1 deletion src/modules/update_challenge/helpers.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
const config = require('config')
const _ = require('lodash')

/**
* Processes a payload from the topic, to be consumed by the handler
Expand Down
89 changes: 51 additions & 38 deletions src/services/vanilla.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,24 +35,24 @@ async function manageVanillaUser (data) {
}
const topcoderProfile = JSON.parse(topcoderProfileResponseStr).result.content[0]

const {text: topcoderRolesResponseStr} = await topcoderApi.getRoles(topcoderProfile.id)
const { text: topcoderRolesResponseStr } = await topcoderApi.getRoles(topcoderProfile.id)
const topcoderRolesResponse = JSON.parse(topcoderRolesResponseStr).result.content
const topcoderRoleNames = _.map(topcoderRolesResponse, 'roleName')

const{ body: allVanillaRoles } = await vanillaClient.getAllRoles()
const { body: allVanillaRoles } = await vanillaClient.getAllRoles()

// Add all missing Topcoder roles
await addTopcoderRoles(allVanillaRoles, topcoderRoleNames)

const { body: allNewVanillaRoles } = await vanillaClient.getAllRoles()

let allTopcoderRoles = _.filter(allNewVanillaRoles, { type: 'topcoder' })
const allTopcoderRoles = _.filter(allNewVanillaRoles, { type: 'topcoder' })

let nonTopcoderRoles = _.filter(allNewVanillaRoles, role => role['type'] != 'topcoder')
let nonTopcoderRoleIDs = _.map(nonTopcoderRoles, 'roleID')
const nonTopcoderRoles = _.filter(allNewVanillaRoles, role => role.type !== 'topcoder')
const nonTopcoderRoleIDs = _.map(nonTopcoderRoles, 'roleID')

let userTopcoderRoles = _.filter(allTopcoderRoles, role => topcoderRoleNames.includes(role['name']))
let userTopcoderRoleIDs = _.map(userTopcoderRoles, 'roleID')
const userTopcoderRoles = _.filter(allTopcoderRoles, role => topcoderRoleNames.includes(role.name))
const userTopcoderRoleIDs = _.map(userTopcoderRoles, 'roleID')

if (!vanillaUser) {
logger.info(`The '${username}' user wasn't found in Vanilla`)
Expand All @@ -79,7 +79,7 @@ async function manageVanillaUser (data) {
vanillaUser = user

// Sync Topcoder roles
const allCurrentUserRoleIDs = _.map(vanillaUser.roles, 'roleID');
const allCurrentUserRoleIDs = _.map(vanillaUser.roles, 'roleID')
const currentVanillaRoleIDs = _.intersection(allCurrentUserRoleIDs, nonTopcoderRoleIDs)
const userData = {
roleID: [...currentVanillaRoleIDs, ...userTopcoderRoleIDs]
Expand Down Expand Up @@ -119,15 +119,15 @@ async function manageVanillaUser (data) {
}
}

async function addTopcoderRoles(allVanillaRoles, topcoderRoleNames) {
async function addTopcoderRoles (allVanillaRoles, topcoderRoleNames) {
const allTopcoderRoles = _.filter(allVanillaRoles, { type: 'topcoder' })
const userTopcoderRoles = _.filter(allTopcoderRoles, role => topcoderRoleNames.includes(role['name']))
const userTopcoderRoles = _.filter(allTopcoderRoles, role => topcoderRoleNames.includes(role.name))
const userTopcoderRoleIDs = _.map(userTopcoderRoles, 'roleID')

if(topcoderRoleNames.length != userTopcoderRoleIDs.length) {
const missingRoles = _.difference(topcoderRoleNames, _.map(userTopcoderRoles, 'name'))
if (topcoderRoleNames.length !== userTopcoderRoleIDs.length) {
const missingRoles = _.difference(topcoderRoleNames, _.map(userTopcoderRoles, 'name'))
logger.info('Missing roles:' + JSON.stringify(missingRoles))
for(const missingRole of missingRoles) {
for (const missingRole of missingRoles) {
await vanillaClient.createRole({
canSession: 1,
description: 'Added by Challenge Forum Processor',
Expand Down Expand Up @@ -168,7 +168,7 @@ async function createVanillaGroup (challenge) {
throw new Error('Multiple discussions with type=\'challenge\' and provider=\'vanilla\' are not supported.')
}

const {body: project} = await topcoderApi.getProject(challenge.projectId)
const { body: project } = await topcoderApi.getProject(challenge.projectId)
const copilots = _.filter(project.members, { role: constants.TOPCODER.ROLE_COPILOT })
const challengesForums = _.filter(template.categories, ['name', constants.VANILLA.CHALLENGES_FORUM])
if (!challengesForums) {
Expand Down Expand Up @@ -228,15 +228,15 @@ async function createVanillaGroup (challenge) {

// Create the root challenge category
const { body: challengeCategory } = await vanillaClient.createCategory({
name: challengeDetailsDiscussion.name,
name: challenge.name,
urlcode: `${challenge.id}`,
parentCategoryID: parentCategory[0].categoryID,
displayAs: groupTemplate.categories ? constants.VANILLA.CATEGORY_DISPLAY_STYLE.CATEGORIES : constants.VANILLA.CATEGORY_DISPLAY_STYLE.DISCUSSIONS
})

logger.info(`The '${challengeCategory.name}' category was created`)

if(groupTemplate.categories) {
if (groupTemplate.categories) {
for (const item of groupTemplate.categories) {
const urlCodeTemplate = _.template(item.urlcode)
const { body: childCategory } = await vanillaClient.createCategory({
Expand All @@ -245,16 +245,16 @@ async function createVanillaGroup (challenge) {
parentCategoryID: challengeCategory.categoryID
})
logger.info(`The '${item.name}' category was created`)
await createDiscussions(group, challenge, item.discussions, childCategory);
await createDiscussions(group, challenge, item.discussions, childCategory)
}
}

if(groupTemplate.discussions){
await createDiscussions(group, challenge, groupTemplate.discussions, challengeCategory);
if (groupTemplate.discussions) {
await createDiscussions(group, challenge, groupTemplate.discussions, challengeCategory)
}

for (const copilot of copilots) {
await manageVanillaUser({challengeId: challenge.id, action: constants.USER_ACTIONS.INVITE, handle: copilot.handle})
await manageVanillaUser({ challengeId: challenge.id, action: constants.USER_ACTIONS.INVITE, handle: copilot.handle })
}

challengeDetailsDiscussion.url = `${challengeCategory.url}`
Expand All @@ -274,34 +274,47 @@ async function updateVanillaGroup (challenge) {
logger.info(`The challenge with challengeID=${challenge.id}:`)

const { body: groups } = await vanillaClient.searchGroups(challenge.id)
if (groups.length == 0) {
throw new Error('The group wasn\'t found for this challenge')
if (groups.length === 0) {
throw new Error('The group wasn\'t found for this challenge')
}

if (groups.length > 1) {
throw new Error('Multiple groups were found for this challenge')
}

const {body: updatedGroup} = await vanillaClient.updateGroup(groups[0].groupID, {name: challenge.name})

const { body: updatedGroup } = await vanillaClient.updateGroup(groups[0].groupID, { name: challenge.name })
logger.info(`The group was updated: ${JSON.stringify(updatedGroup)}`)

const { body: groupCategory } = await vanillaClient.getCategoryByUrlcode(`${challenge.id}`)
if (!groupCategory) {
throw new Error('Group category wasn\'t found for this challenge')
}

const { body: groupCategoryForEdit } = await vanillaClient.getCategoryForEdit(groupCategory.categoryID)
if (!groupCategoryForEdit) {
throw new Error('Group category wasn\'t found for this challenge')
}
groupCategoryForEdit.name = challenge.name

const { body: updatedGroupCategory } = await vanillaClient.updateCategory(groupCategoryForEdit.categoryID, groupCategoryForEdit)
logger.info(`The group category was updated: ${JSON.stringify(updatedGroupCategory)}`)
}

async function createDiscussions (group, challenge, templateDiscussions, vanillaCategory) {
for (const discussion of templateDiscussions) {
// create a discussion
const bodyTemplate = _.template(discussion.body)
await vanillaClient.createDiscussion({
body: bodyTemplate({ challenge: challenge }),
name: discussion.title,
groupID: group.groupID,
categoryID: vanillaCategory.categoryID,
format: constants.VANILLA.DISCUSSION_FORMAT.WYSIWYG,
closed: discussion.closed,
pinned: discussion.announce
})
logger.info(`The '${discussion.title}' discussion/announcement was created`)
}
for (const discussion of templateDiscussions) {
// create a discussion
const bodyTemplate = _.template(discussion.body)
await vanillaClient.createDiscussion({
body: bodyTemplate({ challenge: challenge }),
name: discussion.title,
groupID: group.groupID,
categoryID: vanillaCategory.categoryID,
format: constants.VANILLA.DISCUSSION_FORMAT.WYSIWYG,
closed: discussion.closed,
pinned: discussion.announce
})
logger.info(`The '${discussion.title}' discussion/announcement was created`)
}
}

module.exports = {
Expand Down
87 changes: 43 additions & 44 deletions src/utils/topcoder-api.util.js
Original file line number Diff line number Diff line change
@@ -1,33 +1,33 @@
const config = require("config");
const _ = require("lodash");
const m2mAuth = require("tc-core-library-js").auth.m2m;
const request = require("superagent");
const logger = require("./logger.util");
const config = require('config')
const _ = require('lodash')
const m2mAuth = require('tc-core-library-js').auth.m2m
const request = require('superagent')
const logger = require('./logger.util')

let m2m = null;
let m2m = null

/**
* Function to get M2M token
*/
async function getM2MToken() {
async function getM2MToken () {
if (_.isNull(m2m)) {
m2m = m2mAuth(
_.pick(config.TOPCODER, [
"AUTH0_URL",
"AUTH0_AUDIENCE",
"TOKEN_CACHE_TIME",
"AUTH0_PROXY_SERVER_URL",
'AUTH0_URL',
'AUTH0_AUDIENCE',
'TOKEN_CACHE_TIME',
'AUTH0_PROXY_SERVER_URL'
])
);
)
}
logger.info(
`Getting M2M token for client ID=${config.TOPCODER.AUTH0_CLIENT_ID}`
);
)

return m2m.getMachineToken(
config.TOPCODER.AUTH0_CLIENT_ID,
config.TOPCODER.AUTH0_CLIENT_SECRET
);
)
}

/**
Expand All @@ -36,90 +36,89 @@ async function getM2MToken() {
* @param {String} path Complete path of the API URL
* @param {Object} reqBody Body of the request
*/
async function reqToAPI(reqType, path, reqBody) {
const token = await getM2MToken();
const authHeader = token ? { Authorization: `Bearer ${token}` } : {};
async function reqToAPI (reqType, path, reqBody) {
const token = await getM2MToken()
const authHeader = token ? { Authorization: `Bearer ${token}` } : {}

const validReqTypes = ["GET", "HEAD", "POST", "PUT", "PATCH", "DELETE"];
const hasBody = ["POST", "PUT", "PATCH"];
const validReqTypes = ['GET', 'HEAD', 'POST', 'PUT', 'PATCH', 'DELETE']
const hasBody = ['POST', 'PUT', 'PATCH']

if (_.indexOf(validReqTypes, _.upperCase(reqType)) === -1) {
throw new Error("Invalid request type");
throw new Error('Invalid request type')
}
const reqMethod = request[_.lowerCase(reqType)];
const reqMethod = request[_.lowerCase(reqType)]

if (_.indexOf(hasBody, _.upperCase(reqType)) === -1) {
return reqMethod(path)
.set(authHeader)
.set("Content-Type", "application/json");
.set('Content-Type', 'application/json')
} else {
return reqMethod(path)
.set(authHeader)
.set("Content-Type", "application/json")
.send(reqBody);
.set('Content-Type', 'application/json')
.send(reqBody)
}
}

/**
* Gets the user's handle given the user's ID
* @param {String} userId User's ID (6-digit numeric)
*/
async function getUserDetailsById(userId) {
const path = `${config.TOPCODER.API_URL}/v3/users?filter=id%3D${userId}`;
return reqToAPI("GET", path);
async function getUserDetailsById (userId) {
const path = `${config.TOPCODER.API_URL}/v3/users?filter=id%3D${userId}`
return reqToAPI('GET', path)
}

/**
* Gets the user by Topcoder's handle
* @param {String} handle
*/
async function getUserDetailsByHandle(handle) {
const path = `${config.TOPCODER.API_URL}/v3/users?filter=handle%3D${handle}`;
return reqToAPI("GET", path);
async function getUserDetailsByHandle (handle) {
const path = `${config.TOPCODER.API_URL}/v3/users?filter=handle%3D${handle}`
return reqToAPI('GET', path)
}

/**
* Gets the challenge
* @param {String} challengeId Challenge's ID (uuid)
*/
async function getChallenge(challengeId) {
const path = `${config.TOPCODER.API_URL}/v5/challenges/${challengeId}`;
return reqToAPI("GET", path);
async function getChallenge (challengeId) {
const path = `${config.TOPCODER.API_URL}/v5/challenges/${challengeId}`
return reqToAPI('GET', path)
}

/**
* Update the challenge
* @param {String} challengeId Challenge's ID (uuid)
*/
async function updateChallenge(challengeId, data) {
const path = `${config.TOPCODER.API_URL}/v5/challenges/${challengeId}`;
return reqToAPI("PATCH", path, data);
async function updateChallenge (challengeId, data) {
const path = `${config.TOPCODER.API_URL}/v5/challenges/${challengeId}`
return reqToAPI('PATCH', path, data)
}

/**
* Gets the roles for an user
* @param {int} userId User's ID
*/
async function getRoles(userId) {
const path = `${config.TOPCODER.API_URL}/v3/roles?filter=subjectID%3D${userId}`;
return reqToAPI("GET", path);
async function getRoles (userId) {
const path = `${config.TOPCODER.API_URL}/v3/roles?filter=subjectID%3D${userId}`
return reqToAPI('GET', path)
}

/**
* Gets the project
* @param {int} projectId Project's Id (int)
*/
async function getProject(projectId) {
const path = `${config.TOPCODER.API_URL}/v5/projects/${projectId}`;
return reqToAPI("GET", path);
async function getProject (projectId) {
const path = `${config.TOPCODER.API_URL}/v5/projects/${projectId}`
return reqToAPI('GET', path)
}


module.exports = {
getUserDetailsById,
getUserDetailsByHandle,
getChallenge,
updateChallenge,
getRoles,
getProject
};
}
14 changes: 12 additions & 2 deletions src/utils/vanilla-client.util.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ function getVanillaClient () {
throw err
})
},
updateCategory: (categoryId, data) => {
return request.patch(`${config.VANILLA.API_URL}/categories/${categoryId}`)
.query({ access_token: config.VANILLA.ADMIN_ACCESS_TOKEN })
.send(data)
},
getCategories: (parentCategoryID) => {
const queryParams = { access_token: config.VANILLA.ADMIN_ACCESS_TOKEN }
if (_.isNumber(parentCategoryID)) {
Expand All @@ -39,9 +44,14 @@ function getVanillaClient () {
return request.get(`${config.VANILLA.API_URL}/categories`)
.query(queryParams)
},
getGroupCategory: () => {
getCategoryByUrlcode: (urlcode) => {
const queryParams = { access_token: config.VANILLA.ADMIN_ACCESS_TOKEN }
return request.get(`${config.VANILLA.API_URL}/categories/urlcode/${urlcode}`)
.query(queryParams)
},
getCategoryForEdit: (categoryId) => {
const queryParams = { access_token: config.VANILLA.ADMIN_ACCESS_TOKEN }
return request.get(`${config.VANILLA.API_URL}/groups/category`)
return request.get(`${config.VANILLA.API_URL}/categories/${categoryId}/edit`)
.query(queryParams)
},
watchCategory: (categoryId, userId, data) => {
Expand Down