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

issues/459 #460

Merged
merged 1 commit into from
Jun 14, 2022
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
22 changes: 22 additions & 0 deletions src/common/db-helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,27 @@ async function queryOneUserMappingByTCUsername(model, tcusername) {
});
}

/**
* Get single data by query parameters
* @param {Object} model The dynamoose model to query
* @param {String} provider The git provider
* @param {String} gitUsername The git username
* @returns {Promise<void>}
*/
async function queryTCUsernameByGitUsername(model, provider, gitUsername) {
return await new Promise((resolve, reject) => {
model.queryOne(`${provider}Username`).eq(gitUsername)
.all()
.exec((err, result) => {
if (err) {
logger.debug(`queryTCUsernameByGitUsername. Error. ${err}`);
return reject(err);
}
return resolve(result.topcoderUsername);
});
});
}

/**
* Get single data by query parameters
* @param {Object} model The dynamoose model to query
Expand Down Expand Up @@ -673,6 +694,7 @@ module.exports = {
queryOneUserGroupMapping,
queryOneUserTeamMapping,
queryOneUserMappingByTCUsername,
queryTCUsernameByGitUsername,
queryRepositoriesByProjectId,
queryRepositoryByProjectIdFilterUrl
};
61 changes: 55 additions & 6 deletions src/common/helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ const bcrypt = require('bcryptjs');
const moment = require('moment');
const parseDomain = require('parse-domain');
const config = require('../config');
const kafka = require('../utils/kafka');
const models = require('../models');
const logger = require('./logger');
const errors = require('./errors');
const constants = require('./constants');
Expand Down Expand Up @@ -120,6 +122,52 @@ function buildController(controller) {
});
}

/**
* Convert github api error.
* @param {String} copilotHandle the copilot handle
* @param {String} provider the git provider
*/
async function sendTokenExpiredEvent(copilotHandle, provider) {
const notificationTokenExpiredEvent = {
event: 'notification.tokenExpired',
data: {
copilotHandle,
provider,
},
};
await kafka.send(JSON.stringify(notificationTokenExpiredEvent));
}

/**
* Convert github api error.
* @param {Error} err the github api error
* @param {String} message the error message
* @param {String} gitUsername the git username
* @returns {Error} converted error
*/
async function convertGitHubErrorAsync(err, message, gitUsername) {
if (err.statusCode === 401 && gitUsername) { // eslint-disable-line no-magic-numbers
const copilotHandle = await dbHelper.queryTCUsernameByGitUsername(models.GithubUserMapping, 'github', gitUsername);
await sendTokenExpiredEvent(copilotHandle, 'Github');
}
return convertGitHubError(err, message);
}

/**
* Convert gitlab api error.
* @param {Error} err the gitlab api error
* @param {String} message the error message
* @param {String} gitUsername the git username
* @returns {Error} converted error
*/
async function convertGitLabErrorAsync(err, message, gitUsername) {
if (err.statusCode === 401 && gitUsername) { // eslint-disable-line no-magic-numbers
const copilotHandle = await dbHelper.queryTCUsernameByGitUsername(models.GitlabUserMapping, 'gitlab', gitUsername);
await sendTokenExpiredEvent(copilotHandle, 'Gitlab');
}
return convertGitLabError(err, message);
}

/**
* Convert github api error.
* @param {Error} err the github api error
Expand Down Expand Up @@ -209,24 +257,23 @@ async function getProviderType(repoUrl) {

/**
* gets the git username of copilot/owner for a project
* @param {Object} models the db models
* @param {Object} project the db project detail
* @param {String} provider the git provider
* @param {Boolean} isCopilot if true, then get copilot, otherwise get owner
* @returns {Object} the owner/copilot for the project
*/
async function getProjectCopilotOrOwner(models, project, provider, isCopilot) {
async function getProjectCopilotOrOwner(project, provider, isCopilot) {
const userMapping = await dbHelper.queryOneUserMappingByTCUsername(
provider === 'github' ? models.GithubUserMapping : models.GitlabUserMapping,
provider === 'github' ? models.GithubUserMapping : models.GitlabUserMapping,
isCopilot ? project.copilot : project.owner);

if (!userMapping ||
(provider === 'github' && !userMapping.githubUserId)
if (!userMapping ||
(provider === 'github' && !userMapping.githubUserId)
|| (provider === 'gitlab' && !userMapping.gitlabUserId)) {
throw new Error(`Couldn't find ${isCopilot ? 'copilot' : 'owner'} username for '${provider}' for this repository.`);
}

let user = await dbHelper.queryOneUserByType(models.User,
let user = await dbHelper.queryOneUserByType(models.User,
provider === 'github' ? userMapping.githubUsername : // eslint-disable-line no-nested-ternary
userMapping.gitlabUsername, provider);

Expand Down Expand Up @@ -270,6 +317,8 @@ module.exports = {
buildController,
convertGitHubError,
convertGitLabError,
convertGitHubErrorAsync,
convertGitLabErrorAsync,
ensureExists,
ensureExistsWithKey,
generateIdentifier,
Expand Down
8 changes: 5 additions & 3 deletions src/controllers/GithubController.js
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ async function addUserToTeamCallback(req, res) {
const token = result.body.access_token;

// get team details
const teamDetails = await GithubService.getTeamDetails(team.ownerToken, team.teamId);
const teamDetails = await GithubService.getTeamDetails(team.ownerUsername, team.ownerToken, team.teamId);
const organisation = teamDetails.organization.login;

// Add member to organisation
Expand All @@ -173,7 +173,8 @@ async function addUserToTeamCallback(req, res) {

// add user to team
console.log(`adding ${token} to ${team.teamId} with ${team.ownerToken}`); /* eslint-disable-line no-console */
const githubUser = await GithubService.addTeamMember(team.teamId, team.ownerToken, token, team.accessLevel);
const githubUser = await GithubService.addTeamMember(
team.ownerUsername, team.teamId, team.ownerToken, token, team.accessLevel);
// associate github username with TC username
const mapping = await dbHelper.queryOneUserMappingByTCUsername(GithubUserMapping, req.session.tcUsername);

Expand Down Expand Up @@ -247,7 +248,8 @@ async function deleteUsersFromTeam(req, res) {
});
// eslint-disable-next-line no-restricted-syntax
for (const userTeamMapItem of userTeamMappings) {
await GithubService.deleteUserFromGithubTeam(token, teamId, githubOrgId, userTeamMapItem.githubUserName);
await GithubService.deleteUserFromGithubTeam(
teamInDB.ownerUsername, token, teamId, githubOrgId, userTeamMapItem.githubUserName);
await dbHelper.removeById(UserTeamMapping, userTeamMapItem.id);
}
} catch (err) {
Expand Down
14 changes: 9 additions & 5 deletions src/controllers/GitlabController.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,11 +99,14 @@ async function ownerUserLoginCallback(req, res) {
*/
async function listOwnerUserGroups(req) {
const user = await UserService.getAccessTokenByHandle(req.currentUser.handle, constants.USER_TYPES.GITLAB);
// NOTE: Only user with topcoder-x account can pass this condition.
// Only them will be inserted into `User` table,
// normal user will not be in the `User` table.
if (!user || !user.accessToken) {
throw new errors.UnauthorizedError('You have not setup for Gitlab.');
}
const refreshedUser = await GitlabService.refreshGitlabUserAccessToken(user);
return await GitlabService.listOwnerUserGroups(refreshedUser.accessToken, req.query.page,
return await GitlabService.listOwnerUserGroups(refreshedUser.username, refreshedUser.accessToken, req.query.page,
req.query.perPage, req.query.getAll);
}

Expand Down Expand Up @@ -197,14 +200,15 @@ async function addUserToGroupCallback(req, res) {
const token = result.body.access_token;

// get group name
const groupsResult = await GitlabService.listOwnerUserGroups(refreshedOwnerUser.accessToken, 1,
constants.MAX_PER_PAGE, true);
const groupsResult = await GitlabService.listOwnerUserGroups(refreshedOwnerUser.username,
refreshedOwnerUser.accessToken, 1, constants.MAX_PER_PAGE, true);
const currentGroup = _.find(groupsResult.groups, (item) => { // eslint-disable-line arrow-body-style
return item.id.toString() === group.groupId.toString();
});

// add user to group
const gitlabUser = await GitlabService.addGroupMember(
refreshedOwnerUser.username,
group.groupId,
refreshedOwnerUser.accessToken,
token,
Expand Down Expand Up @@ -272,8 +276,8 @@ async function deleteUsersFromTeam(req, res) {
const userGroupMappings = await dbHelper.scan(UserGroupMapping, {groupId});
// eslint-disable-next-line no-restricted-syntax
for (const userGroupMapItem of userGroupMappings) {
await GitlabService.deleteUserFromGitlabGroup(refreshedOwnerUser.accessToken, groupId,
userGroupMapItem.gitlabUserId);
await GitlabService.deleteUserFromGitlabGroup(refreshedOwnerUser.username,
refreshedOwnerUser.accessToken, groupId, userGroupMapItem.gitlabUserId);
await dbHelper.removeById(UserGroupMapping, userGroupMapItem.id);
}
} catch (err) {
Expand Down
20 changes: 14 additions & 6 deletions src/services/GithubService.js
Original file line number Diff line number Diff line change
Expand Up @@ -187,13 +187,14 @@ getTeamRegistrationUrl.schema = Joi.object().keys({

/**
* Add team member.
* @param {String} gitUsername the git username
* @param {String} teamId the team id
* @param {String} ownerUserToken the owner user token
* @param {String} normalUserToken the normal user token
* @param {String} accessLevel the team's access level
* @returns {Promise} the promise result
*/
async function addTeamMember(teamId, ownerUserToken, normalUserToken, accessLevel) {
async function addTeamMember(gitUsername, teamId, ownerUserToken, normalUserToken, accessLevel) {
let username;
let id;
let state;
Expand All @@ -220,14 +221,15 @@ async function addTeamMember(teamId, ownerUserToken, normalUserToken, accessLeve
}).get('true')
.isUndefined()
.value()) {
throw helper.convertGitHubError(err, 'Failed to add team member');
throw await helper.convertGitHubErrorAsync(err, 'Failed to add team member', gitUsername);
}
}
// return github username and its state
return {username, id, state};
}

addTeamMember.schema = Joi.object().keys({
gitUsername: Joi.string().required(),
teamId: Joi.string().required(),
ownerUserToken: Joi.string().required(),
normalUserToken: Joi.string().required(),
Expand Down Expand Up @@ -342,12 +344,13 @@ getUserIdByUsername.schema = Joi.object().keys({
/**
* Get team detailed data
*
* @param {String} gitUsername git username
* @param {String} token user owner token
* @param {String|Number} teamId team id
*
* @returns {Object} team object, see https://developer.github.com/v3/teams/#get-team
*/
async function getTeamDetails(token, teamId) {
async function getTeamDetails(gitUsername, token, teamId) {
const teamIdAsNumber = !_.isNumber(teamId) ? parseInt(teamId, 10) : teamId;
let team;

Expand All @@ -357,13 +360,14 @@ async function getTeamDetails(token, teamId) {

team = teamResponse.data;
} catch (err) {
throw helper.convertGitHubError(err, `Failed to get team with id '${teamId}'.`);
throw await helper.convertGitHubErrorAsync(err, `Failed to get team with id '${teamId}'.`, gitUsername);
}

return team;
}

getTeamDetails.schema = Joi.object().keys({
gitUsername: Joi.string().required(),
token: Joi.string().required(),
teamId: Joi.alternatives().try(Joi.string(), Joi.number()).required(),
});
Expand All @@ -372,14 +376,15 @@ getTeamDetails.schema = Joi.object().keys({
/**
* Get team detailed data
*
* @param {String} gitUsername git username
* @param {String} token user owner token
* @param {String|Number} teamId team id
* @param {String|Number} orgId team id
* @param {String|Number} githubUserName team id
*
* @returns {Object} status object, see https://developer.github.com/v3/teams/members/#remove-team-membership
*/
async function deleteUserFromGithubTeam(token, teamId, orgId, githubUserName) {
async function deleteUserFromGithubTeam(gitUsername, token, teamId, orgId, githubUserName) {
const teamIdAsNumber = !_.isNumber(teamId) ? parseInt(teamId, 10) : teamId;
let deleteResult;
try {
Expand All @@ -388,12 +393,15 @@ async function deleteUserFromGithubTeam(token, teamId, orgId, githubUserName) {
const deleteGithubUserEndpoint = `/organizations/${orgId}/team/${teamIdAsNumber}/memberships/${githubUserName}`;
deleteResult = await team._request('DELETE', deleteGithubUserEndpoint);
} catch (err) {
throw helper.convertGitHubError(err, `Failed to delete user '${githubUserName}' from org with orgId '${orgId}' and team id '${teamId}'.`);
throw await helper.convertGitHubErrorAsync(
err, `Failed to delete user '${githubUserName}' from org with orgId '${orgId}' and team id '${teamId}'.`,
githubUserName);
}
return deleteResult;
}

deleteUserFromGithubTeam.schema = Joi.object().keys({
gitUsername: Joi.string().required(),
token: Joi.string().required(),
teamId: Joi.alternatives().try(Joi.string(), Joi.number()).required(),
orgId: Joi.string().required(),
Expand Down
Loading